Dokumentenmanagement
This commit is contained in:
parent
70ece97587
commit
c2c4ae3cbe
14 changed files with 759 additions and 13 deletions
46
vergabe_teilnahme/templates/dokumente/bibliothek_modal.html
Normal file
46
vergabe_teilnahme/templates/dokumente/bibliothek_modal.html
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Aus Bibliothek zuordnen{% endblock %}
|
||||
{% block content %}
|
||||
<div class="max-w-2xl mx-auto">
|
||||
<h1 class="page-title mb-2">Nachweis aus Bibliothek zuordnen</h1>
|
||||
<p class="text-sm text-slate-500 mb-6">Dokument: <span class="font-medium">{{ dokument.dateiname|default:"—" }}</span></p>
|
||||
|
||||
<form method="get" class="card mb-4 flex gap-3 items-end">
|
||||
<div class="flex-1">
|
||||
<label class="form-label">Suche</label>
|
||||
<input type="text" name="q" value="{{ q }}" placeholder="Nachweis-Titel…" class="form-input w-full">
|
||||
</div>
|
||||
<button type="submit" class="btn-primary">Suchen</button>
|
||||
</form>
|
||||
|
||||
{% if nachweise %}
|
||||
<div class="card divide-y divide-slate-100">
|
||||
{% for nw in nachweise %}
|
||||
<form method="post" class="flex items-start justify-between py-3 gap-4">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="nachweis_id" value="{{ nw.pk }}">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-slate-800">{{ nw.titel }}</p>
|
||||
<p class="text-xs text-slate-500">{{ nw.kategorie }}{% if nw.version %} · v{{ nw.version }}{% endif %}{% if nw.gueltig_bis %} · Gültig bis {{ nw.gueltig_bis|date:"d.m.Y" }}{% endif %}</p>
|
||||
{% if nw.ist_abgelaufen %}
|
||||
<p class="text-xs text-red-600 font-medium mt-0.5">Abgelaufen</p>
|
||||
{% elif nw.gueltig_bis %}
|
||||
{% now "Y-m-d" as today_str %}
|
||||
<p class="text-xs text-yellow-600 mt-0.5">Bald ablaufend</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<button type="submit" class="btn-secondary text-xs shrink-0">Zuordnen</button>
|
||||
</form>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% elif q %}
|
||||
<p class="text-sm text-slate-500 text-center py-6">Keine Nachweise für "{{ q }}" gefunden.</p>
|
||||
{% else %}
|
||||
<p class="text-sm text-slate-500 text-center py-6">Suchbegriff eingeben, um Nachweise zu finden.</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-4">
|
||||
<a href="{% url 'ausschreibungen:dokumente:detail' ausschreibung.pk dokument.pk %}" class="btn-ghost text-xs">← Zurück</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
122
vergabe_teilnahme/templates/dokumente/detail.html
Normal file
122
vergabe_teilnahme/templates/dokumente/detail.html
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
{% extends "base.html" %}
|
||||
{% load vergabe_tags %}
|
||||
{% block title %}{{ dokument.dateiname }}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h1 class="page-title">{{ dokument.dateiname|default:"Dokument" }}</h1>
|
||||
<a href="{% url 'ausschreibungen:dokumente:liste' ausschreibung.pk %}" class="btn-ghost text-xs">← Übersicht</a>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div class="col-span-2 space-y-4">
|
||||
|
||||
<div class="card space-y-3">
|
||||
<div class="flex items-center gap-3">
|
||||
{% status_badge dokument.status dokument.get_status_display %}
|
||||
{% if dokument.finale_abgabeversion %}
|
||||
<span class="text-xs bg-green-100 text-green-700 px-2 py-0.5 rounded font-medium">Finale Abgabeversion</span>
|
||||
{% endif %}
|
||||
<span class="text-xs text-slate-500">v{{ dokument.version }}</span>
|
||||
</div>
|
||||
{% if dokument.datei %}
|
||||
<a href="{{ dokument.datei.url }}" target="_blank" class="btn-primary text-xs inline-block">↓ Herunterladen</a>
|
||||
{% endif %}
|
||||
{% if dokument.beschreibung %}
|
||||
<p class="text-sm text-slate-700 whitespace-pre-wrap">{{ dokument.beschreibung }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if versionen.count > 1 %}
|
||||
<div class="card">
|
||||
<p class="text-xs font-medium text-slate-500 uppercase tracking-wide mb-3">Versionshistorie</p>
|
||||
<table class="w-full text-sm">
|
||||
<thead>
|
||||
<tr class="border-b border-slate-200 text-left text-xs text-slate-500">
|
||||
<th class="pb-2 pr-4">Version</th>
|
||||
<th class="pb-2 pr-4">Status</th>
|
||||
<th class="pb-2">Datum</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
{% for v in versionen %}
|
||||
<tr class="{% if v.pk == dokument.pk %}bg-blue-50{% else %}hover:bg-slate-50{% endif %}">
|
||||
<td class="py-2 pr-4">
|
||||
<a href="{% url 'ausschreibungen:dokumente:detail' ausschreibung.pk v.pk %}"
|
||||
class="{% if v.pk == dokument.pk %}font-semibold text-blue-700{% else %}text-slate-700 hover:text-blue-600{% endif %}">
|
||||
v{{ v.version }}
|
||||
</a>
|
||||
</td>
|
||||
<td class="py-2 pr-4">{% status_badge v.status v.get_status_display %}</td>
|
||||
<td class="py-2 text-xs text-slate-500">{{ v.upload_datum|date:"d.m.Y" }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if freigaben %}
|
||||
<div class="card">
|
||||
<p class="text-xs font-medium text-slate-500 uppercase tracking-wide mb-3">Freigaben</p>
|
||||
<ul class="space-y-2">
|
||||
{% for fg in freigaben %}
|
||||
<li class="text-sm text-slate-700">
|
||||
{% status_badge fg.status fg.get_status_display %}
|
||||
<span class="ml-2 text-slate-500">{{ fg.freigebender }}</span>
|
||||
{% if fg.kommentar %}<span class="ml-2 text-slate-400 text-xs">— {{ fg.kommentar }}</span>{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
|
||||
<div class="card space-y-2">
|
||||
<p class="text-xs font-medium text-slate-500 uppercase tracking-wide">Details</p>
|
||||
<p class="text-xs text-slate-600">Kategorie: <span class="font-medium">{{ dokument.get_kategorie_display }}</span></p>
|
||||
<p class="text-xs text-slate-600">Hochgeladen: <span class="font-medium">{{ dokument.upload_datum|date:"d.m.Y H:i" }}</span></p>
|
||||
{% if dokument.verantwortlicher %}
|
||||
<p class="text-xs text-slate-600">Verantwortlicher: {{ dokument.verantwortlicher }}</p>
|
||||
{% endif %}
|
||||
{% if dokument.pruefer %}
|
||||
<p class="text-xs text-slate-600">Prüfer: {{ dokument.pruefer }}</p>
|
||||
{% endif %}
|
||||
{% if dokument.los %}
|
||||
<p class="text-xs text-slate-600">Los: {{ dokument.los }}</p>
|
||||
{% endif %}
|
||||
{% if dokument.quelle %}
|
||||
<p class="text-xs text-slate-600">Quelle: {{ dokument.quelle }}</p>
|
||||
{% endif %}
|
||||
{% if dokument.bibliothek_nachweis %}
|
||||
<div class="border-t border-slate-100 pt-2 mt-2">
|
||||
<p class="text-xs text-slate-500 mb-1">Aus Bibliothek</p>
|
||||
<p class="text-xs font-medium text-slate-700">{{ dokument.bibliothek_nachweis.titel }}</p>
|
||||
{% if dokument.bibliothek_nachweis.ist_abgelaufen %}
|
||||
<p class="text-xs text-red-600 mt-1">Nachweis abgelaufen!</p>
|
||||
{% elif dokument.bibliothek_nachweis.gueltig_bis %}
|
||||
<p class="text-xs text-slate-500">Gültig bis: {{ dokument.bibliothek_nachweis.gueltig_bis|date:"d.m.Y" }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="card space-y-2">
|
||||
<p class="text-xs font-medium text-slate-500 uppercase tracking-wide">Aktionen</p>
|
||||
{% if not dokument.finale_abgabeversion %}
|
||||
<a href="{% url 'ausschreibungen:dokumente:neue_version' ausschreibung.pk dokument.pk %}" class="btn-secondary text-xs w-full block text-center">Neue Version hochladen</a>
|
||||
<form method="post" action="{% url 'ausschreibungen:dokumente:finale_version' ausschreibung.pk dokument.pk %}">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn-primary text-xs w-full mt-2">Als finale Abgabeversion kennzeichnen</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
<a href="{% url 'ausschreibungen:dokumente:bibliothek_zuordnen' ausschreibung.pk dokument.pk %}" class="btn-ghost text-xs w-full block text-center mt-1">Aus Bibliothek zuordnen</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
68
vergabe_teilnahme/templates/dokumente/form.html
Normal file
68
vergabe_teilnahme/templates/dokumente/form.html
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}{{ titel }}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="max-w-2xl mx-auto">
|
||||
<h1 class="page-title mb-6">{{ titel }}</h1>
|
||||
<form method="post" enctype="multipart/form-data" class="space-y-4"
|
||||
x-data="{ files: [] }">
|
||||
{% csrf_token %}
|
||||
<div class="card space-y-4">
|
||||
<div>
|
||||
<label class="form-label">Datei</label>
|
||||
<input type="file" name="datei"
|
||||
accept=".pdf,.docx,.xlsx,.zip,.png,.jpg,.jpeg"
|
||||
class="form-input"
|
||||
@change="files = Array.from($event.target.files).map(f => f.name)">
|
||||
<template x-if="files.length">
|
||||
<ul class="mt-1 space-y-0.5">
|
||||
<template x-for="name in files" :key="name">
|
||||
<li class="text-xs text-slate-600" x-text="name"></li>
|
||||
</template>
|
||||
</ul>
|
||||
</template>
|
||||
{% if form.datei.errors %}<p class="text-xs text-red-600 mt-1">{{ form.datei.errors.0 }}</p>{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Kategorie</label>
|
||||
{{ form.kategorie }}
|
||||
{% if form.kategorie.errors %}<p class="text-xs text-red-600 mt-1">{{ form.kategorie.errors.0 }}</p>{% endif %}
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="form-label">Version</label>
|
||||
{{ form.version }}
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Quelle (optional)</label>
|
||||
{{ form.quelle }}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Beschreibung (optional)</label>
|
||||
{{ form.beschreibung }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card space-y-4">
|
||||
<p class="text-xs font-medium text-slate-500 uppercase tracking-wide">Zuständigkeiten (optional)</p>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="form-label">Verantwortlicher</label>
|
||||
{{ form.verantwortlicher }}
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Prüfer</label>
|
||||
{{ form.pruefer }}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Los</label>
|
||||
{{ form.los }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<button type="submit" class="btn-primary">Hochladen</button>
|
||||
<a href="{% url 'ausschreibungen:dokumente:liste' ausschreibung.pk %}" class="btn-ghost">Abbrechen</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
85
vergabe_teilnahme/templates/dokumente/liste.html
Normal file
85
vergabe_teilnahme/templates/dokumente/liste.html
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
{% extends "base.html" %}
|
||||
{% load vergabe_tags %}
|
||||
{% block title %}Dokumente — {{ ausschreibung.titel }}{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h1 class="page-title">Dokumente</h1>
|
||||
<a href="{% url 'ausschreibungen:dokumente:upload' ausschreibung.pk %}" class="btn-primary text-xs">+ Hochladen</a>
|
||||
</div>
|
||||
|
||||
<form class="card mb-4 flex flex-wrap gap-3 items-end">
|
||||
<div>
|
||||
<label class="form-label">Status</label>
|
||||
<select name="status" class="form-input text-xs" onchange="this.form.submit()">
|
||||
<option value="">Alle</option>
|
||||
{% for val, label in status_choices %}
|
||||
<option value="{{ val }}"{% if current_status == val %} selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Kategorie</label>
|
||||
<select name="kategorie" class="form-input text-xs" onchange="this.form.submit()">
|
||||
<option value="">Alle</option>
|
||||
{% for val, label in kategorie_choices %}
|
||||
<option value="{{ val }}"{% if current_kategorie == val %} selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% if grouped_dokumente %}
|
||||
{% for kategorie_label, dokumente in grouped_dokumente.items %}
|
||||
<div class="mb-4" x-data="{ open: true }">
|
||||
<button @click="open = !open"
|
||||
class="w-full flex items-center justify-between card py-2 px-4 text-left text-sm font-semibold text-slate-700 hover:bg-slate-50">
|
||||
<span>{{ kategorie_label }} <span class="text-xs font-normal text-slate-400">({{ dokumente|length }})</span></span>
|
||||
<span x-text="open ? '▲' : '▼'" class="text-xs text-slate-400"></span>
|
||||
</button>
|
||||
<div x-show="open" class="card mt-0 rounded-t-none border-t-0">
|
||||
<table class="w-full text-sm">
|
||||
<thead>
|
||||
<tr class="border-b border-slate-200 text-left text-xs text-slate-500">
|
||||
<th class="pb-2 pr-4">Datei</th>
|
||||
<th class="pb-2 pr-4">Version</th>
|
||||
<th class="pb-2 pr-4">Status</th>
|
||||
<th class="pb-2 pr-4">Verantwortlicher</th>
|
||||
<th class="pb-2 pr-4">Prüfer</th>
|
||||
<th class="pb-2">Datum</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
{% for dok in dokumente %}
|
||||
<tr class="hover:bg-slate-50{% if dok.finale_abgabeversion %} bg-green-50{% endif %}">
|
||||
<td class="py-2 pr-4">
|
||||
<a href="{% url 'ausschreibungen:dokumente:detail' ausschreibung.pk dok.pk %}"
|
||||
class="font-medium text-slate-800 hover:text-blue-600">
|
||||
{{ dok.dateiname|default:dok.pk }}
|
||||
</a>
|
||||
{% if dok.finale_abgabeversion %}
|
||||
<span class="ml-1 inline-block text-xs bg-green-100 text-green-700 px-1.5 py-0.5 rounded">Final</span>
|
||||
{% endif %}
|
||||
{% if dok.quelle %}
|
||||
<p class="text-xs text-slate-400">{{ dok.quelle }}</p>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="py-2 pr-4 text-xs text-slate-600">{{ dok.version }}</td>
|
||||
<td class="py-2 pr-4" id="status-widget-{{ dok.pk }}">
|
||||
{% include "dokumente/partials/status_widget.html" with dokument=dok %}
|
||||
</td>
|
||||
<td class="py-2 pr-4 text-xs text-slate-600">{{ dok.verantwortlicher|default:"—" }}</td>
|
||||
<td class="py-2 pr-4 text-xs text-slate-600">{{ dok.pruefer|default:"—" }}</td>
|
||||
<td class="py-2 text-xs text-slate-500">{{ dok.upload_datum|date:"d.m.Y" }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="card text-center py-10 text-slate-500 text-sm">Noch keine Dokumente hochgeladen.</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
26
vergabe_teilnahme/templates/dokumente/neue_version.html
Normal file
26
vergabe_teilnahme/templates/dokumente/neue_version.html
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Neue Version — {{ dokument.dateiname }}{% endblock %}
|
||||
{% block content %}
|
||||
<div class="max-w-xl mx-auto">
|
||||
<h1 class="page-title mb-2">Neue Version hochladen</h1>
|
||||
<p class="text-sm text-slate-500 mb-6">Ersetzt: <span class="font-medium">{{ dokument.dateiname }}</span> v{{ dokument.version }}</p>
|
||||
<form method="post" enctype="multipart/form-data" class="space-y-4">
|
||||
{% csrf_token %}
|
||||
<div class="card space-y-4">
|
||||
<div>
|
||||
<label class="form-label">Neue Datei</label>
|
||||
{{ form.datei }}
|
||||
{% if form.datei.errors %}<p class="text-xs text-red-600 mt-1">{{ form.datei.errors.0 }}</p>{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">Versionsnummer</label>
|
||||
{{ form.version }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<button type="submit" class="btn-primary">Version hochladen</button>
|
||||
<a href="{% url 'ausschreibungen:dokumente:detail' ausschreibung.pk dokument.pk %}" class="btn-ghost">Abbrechen</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{% load vergabe_tags %}
|
||||
<span class="inline-flex items-center gap-1">
|
||||
{% status_badge dokument.status dokument.get_status_display %}
|
||||
<span class="text-xs bg-green-100 text-green-700 px-1.5 py-0.5 rounded font-medium">Final ✓</span>
|
||||
</span>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{% load vergabe_tags %}
|
||||
{% if dokument.status == 'final_abgegeben' or dokument.status == 'ersetzt' or dokument.status == 'archiviert' %}
|
||||
{% status_badge dokument.status dokument.get_status_display %}
|
||||
{% else %}
|
||||
<select name="status"
|
||||
hx-post="{% url 'ausschreibungen:dokumente:status' ausschreibung.pk dokument.pk %}"
|
||||
hx-target="#status-widget-{{ dokument.pk }}"
|
||||
hx-swap="innerHTML"
|
||||
class="form-input text-xs">
|
||||
{% for val, label in dokument.STATUS_CHOICES %}
|
||||
{% if val not in 'ersetzt,archiviert' %}
|
||||
<option value="{{ val }}"{% if val == dokument.status %} selected{% endif %}>{{ label }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% endif %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue