first commit
This commit is contained in:
@@ -4,8 +4,15 @@
|
||||
|
||||
{% block body %}
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3 mb-0"><i class="bi bi-card-list"></i> Dokumente</h1>
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 flex-wrap gap-2">
|
||||
<div>
|
||||
<h1 class="h3 mb-1">
|
||||
<i class="bi bi-card-list"></i> Dokumente
|
||||
</h1>
|
||||
<div class="small text-muted">
|
||||
Übersicht über Dokumente, aktive Versionen, Ingest-Zustände und Tag-Zuordnungen.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="{{ path('admin_document_new') }}"
|
||||
class="btn btn-sm btn-outline-info">
|
||||
@@ -13,154 +20,222 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% for message in app.flashes('success') %}
|
||||
<div class="alert alert-success shadow-sm">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% for message in app.flashes('danger') %}
|
||||
<div class="alert alert-danger shadow-sm">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% for message in app.flashes('info') %}
|
||||
<div class="alert alert-info shadow-sm">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<div class="card bg-dark border-secondary text-light mb-4 shadow-sm">
|
||||
<div class="card-body row g-4">
|
||||
<div class="col-lg-7">
|
||||
<h5 class="text-info mb-3">Worauf achten?</h5>
|
||||
<ul class="small mb-0">
|
||||
<li><strong>INDEXED</strong> bedeutet: aktive Version ist sauber im Wissensindex.</li>
|
||||
<li><strong>PENDING</strong> oder <strong>FAILED</strong> bedeuten: Dokument prüfen und ggf. Ingest erneut anstoßen.</li>
|
||||
<li><strong>Tags</strong> sollten fachlich präzise sein und nicht nur generische Oberbegriffe abbilden.</li>
|
||||
<li>Die aktive Version ist die fachlich führende Version des Dokuments.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-5">
|
||||
<h5 class="text-info mb-3">Schnellzugriff</h5>
|
||||
<div class="small text-light">
|
||||
Über <strong>Tags</strong> gelangst du direkt in die Tag-Pflege des Dokuments.
|
||||
Über <strong>Details</strong> steuerst du Versionen, Aktivierung, Re-Ingest und Löschung.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if documents is empty %}
|
||||
|
||||
<div class="alert alert-secondary">
|
||||
<div class="alert alert-secondary shadow-sm">
|
||||
Keine Dokumente vorhanden.
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
|
||||
<div class="card bg-black border-secondary">
|
||||
<div class="card bg-black border-secondary shadow-sm">
|
||||
<div class="card-body p-0">
|
||||
|
||||
<table class="table table-dark table-striped table-hover align-middle mb-0">
|
||||
<thead class="table-secondary text-dark">
|
||||
<tr>
|
||||
<th>Titel</th>
|
||||
<th>ID</th>
|
||||
<th>Typ</th>
|
||||
<th>Status</th>
|
||||
<th>Indexierung</th>
|
||||
<th>Versionen</th>
|
||||
<th>Aktive Version</th>
|
||||
<th>Erstellt</th>
|
||||
<th class="text-end">Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<div class="d-flex justify-content-between align-items-center px-3 py-3 border-bottom border-secondary flex-wrap gap-2">
|
||||
<div>
|
||||
<strong class="text-info">Vorhandene Dokumente</strong>
|
||||
<span class="small text-muted ms-2">{{ documents|length }} Einträge</span>
|
||||
</div>
|
||||
|
||||
{% for document in documents %}
|
||||
<div class="small text-muted">
|
||||
Neueste Dokumente stehen oben.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-striped table-hover align-middle mb-0">
|
||||
<thead class="table-secondary text-dark">
|
||||
<tr>
|
||||
<th style="width: 20%">Titel</th>
|
||||
<th style="width: 14%">ID</th>
|
||||
<th style="width: 8%">Typ</th>
|
||||
<th style="width: 8%">Status</th>
|
||||
<th style="width: 10%">Indexierung</th>
|
||||
<th style="width: 7%">Versionen</th>
|
||||
<th style="width: 8%">Aktive Version</th>
|
||||
<th style="width: 7%">Tags</th>
|
||||
<th style="width: 8%">Erstellt</th>
|
||||
<th class="text-end" style="width: 10%">Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
{# Titel #}
|
||||
<td>
|
||||
<a href="{{ path('admin_document_show', {id: document.id}) }}"
|
||||
class="text-light text-decoration-none">
|
||||
{{ document.title }}
|
||||
</a>
|
||||
</td>
|
||||
{% for document in documents %}
|
||||
<tr>
|
||||
<td>
|
||||
<div class="fw-semibold">
|
||||
<a href="{{ path('admin_document_show', {id: document.id}) }}"
|
||||
class="text-light text-decoration-none">
|
||||
{{ document.title }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{# ID #}
|
||||
<td class="small text-info">
|
||||
{{ document.id }}
|
||||
</td>
|
||||
{% if document.currentVersion and document.currentVersion.filePath %}
|
||||
<div class="small text-muted mt-1">
|
||||
Aktive Datei vorhanden
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
{# Typ #}
|
||||
<td>
|
||||
{% if document.currentVersion %}
|
||||
<span class="badge bg-secondary">
|
||||
{{ document.currentVersion.fileTypeLabel }}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="badge bg-dark border border-secondary">
|
||||
-
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="small text-info">
|
||||
<code>{{ document.id }}</code>
|
||||
</td>
|
||||
|
||||
{# Dokument Status #}
|
||||
<td>
|
||||
{% if document.status == 'ACTIVE' %}
|
||||
<span class="badge bg-success">Aktiv</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">Archiviert</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
{# Ingest Status #}
|
||||
<td>
|
||||
{% if document.currentVersion %}
|
||||
{% if document.currentVersion.ingestStatus == 'INDEXED' %}
|
||||
<span class="badge bg-success">INDEXED</span>
|
||||
{% elseif document.currentVersion.ingestStatus == 'PENDING' %}
|
||||
<span class="badge bg-warning text-dark">PENDING</span>
|
||||
{% elseif document.currentVersion.ingestStatus == 'FAILED' %}
|
||||
<span class="badge bg-danger">FAILED</span>
|
||||
<td>
|
||||
{% if document.currentVersion %}
|
||||
<span class="badge bg-secondary">
|
||||
{{ document.currentVersion.fileTypeLabel }}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="badge bg-dark border border-secondary">
|
||||
{{ document.currentVersion.ingestStatus }}
|
||||
</span>
|
||||
-
|
||||
</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="badge bg-dark border border-secondary">-</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</td>
|
||||
|
||||
{# Version Count #}
|
||||
<td>
|
||||
{{ document.versions|length }}
|
||||
</td>
|
||||
<td>
|
||||
{% if document.status == 'ACTIVE' %}
|
||||
<span class="badge bg-success">Aktiv</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">Archiviert</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
{# Aktive Version #}
|
||||
<td>
|
||||
{% if document.currentVersion %}
|
||||
v{{ document.currentVersion.versionNumber }}
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if document.currentVersion %}
|
||||
{% if document.currentVersion.ingestStatus == 'INDEXED' %}
|
||||
<span class="badge bg-success">INDEXED</span>
|
||||
{% elseif document.currentVersion.ingestStatus == 'PENDING' %}
|
||||
<span class="badge bg-warning text-dark">PENDING</span>
|
||||
{% elseif document.currentVersion.ingestStatus == 'RUNNING' %}
|
||||
<span class="badge bg-warning text-dark">RUNNING</span>
|
||||
{% elseif document.currentVersion.ingestStatus == 'FAILED' %}
|
||||
<span class="badge bg-danger">FAILED</span>
|
||||
{% else %}
|
||||
<span class="badge bg-dark border border-secondary">
|
||||
{{ document.currentVersion.ingestStatus ?: '-' }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="badge bg-dark border border-secondary">-</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
{# Created At #}
|
||||
<td class="small">
|
||||
{{ document.createdAt|date('d.m.Y H:i') }}
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge text-bg-dark border border-secondary">
|
||||
{{ document.versions|length }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
{# Aktionen #}
|
||||
<td class="text-end">
|
||||
<td>
|
||||
{% if document.currentVersion %}
|
||||
<span class="badge bg-info text-dark">
|
||||
v{{ document.currentVersion.versionNumber }}
|
||||
</span>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<a class="btn btn-sm btn-outline-info me-2"
|
||||
href="{{ path('admin_document_tags_edit', {id: document.id}) }}">
|
||||
Tags
|
||||
</a>
|
||||
<td>
|
||||
<span class="badge text-bg-dark border border-secondary">
|
||||
{{ document.tags|length }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<a class="btn btn-sm btn-outline-light me-2"
|
||||
href="{{ path('admin_document_show', {id: document.id}) }}">
|
||||
Details
|
||||
</a>
|
||||
<td class="small">
|
||||
{{ document.createdAt|date('d.m.Y H:i') }}
|
||||
</td>
|
||||
|
||||
{% if is_granted('ROLE_SUPER_ADMIN') %}
|
||||
<form method="post"
|
||||
action="{{ path('admin_document_delete', {id: document.id}) }}"
|
||||
class="d-inline"
|
||||
onsubmit="return confirm('Dokument wirklich endgültig löschen? Diese Aktion entfernt Dokument, Versionen und Index-Daten.');">
|
||||
<td class="text-end">
|
||||
<div class="d-flex justify-content-end flex-wrap gap-2">
|
||||
<a class="btn btn-sm btn-outline-info"
|
||||
href="{{ path('admin_document_tags_edit', {id: document.id}) }}">
|
||||
Tags
|
||||
</a>
|
||||
|
||||
<input type="hidden"
|
||||
name="_token"
|
||||
value="{{ csrf_token('delete_document_' ~ document.id) }}">
|
||||
<a class="btn btn-sm btn-outline-light"
|
||||
href="{{ path('admin_document_show', {id: document.id}) }}">
|
||||
Details
|
||||
</a>
|
||||
|
||||
<button class="btn btn-sm btn-outline-danger">
|
||||
Löschen
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if is_granted('ROLE_SUPER_ADMIN') %}
|
||||
<form method="post"
|
||||
action="{{ path('admin_document_delete', {id: document.id}) }}"
|
||||
class="d-inline"
|
||||
onsubmit="return confirm('Dokument wirklich löschen? Der Inhalt wird per Delete-Job aus dem Index entfernt.');">
|
||||
<input type="hidden"
|
||||
name="_token"
|
||||
value="{{ csrf_token('delete_document_' ~ document.id) }}">
|
||||
|
||||
</td>
|
||||
<button class="btn btn-sm btn-outline-danger">
|
||||
Löschen
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-4 small text-secondary">
|
||||
Hinweis: Das Löschen eines Dokuments entfernt alle Versionen und
|
||||
erfordert eine Aktualisierung des NDJSON-Indexes.
|
||||
<div class="card bg-dark border-secondary text-light mt-4 shadow-sm">
|
||||
<div class="card-body">
|
||||
<h5 class="text-info mb-3">Hinweis zum Dokument-Lifecycle</h5>
|
||||
<div class="small text-light">
|
||||
Änderungen an aktiven Versionen und Löschvorgänge wirken sich direkt auf den Wissensindex aus.
|
||||
Zugewiesene Tags beeinflussen zusätzlich die semantische Routing-Ebene des Systems.
|
||||
Dokumente mit schwachen oder fehlenden Tags sind oft ein guter Kandidat für fachliche Nachpflege.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -4,8 +4,13 @@
|
||||
|
||||
{% block body %}
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3">Neues Dokument</h1>
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 flex-wrap gap-2">
|
||||
<div>
|
||||
<h1 class="h3 mb-1">Neues Dokument</h1>
|
||||
<div class="small text-muted">
|
||||
Neuer Upload mit initialer Version und anschließendem asynchronen Ingest.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="{{ path('admin_documents') }}"
|
||||
class="btn btn-sm btn-outline-secondary">
|
||||
@@ -13,7 +18,49 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card bg-black border-secondary text-light">
|
||||
{% for message in app.flashes('success') %}
|
||||
<div class="alert alert-success shadow-sm">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% for message in app.flashes('danger') %}
|
||||
<div class="alert alert-danger shadow-sm">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% for message in app.flashes('info') %}
|
||||
<div class="alert alert-info shadow-sm">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<div class="card bg-dark border-secondary text-light mb-4 shadow-sm">
|
||||
<div class="card-body row g-4">
|
||||
<div class="col-lg-7">
|
||||
<h5 class="text-info mb-3">Warum ist der Titel wichtig?</h5>
|
||||
|
||||
<ul class="small mb-0">
|
||||
<li>Der Titel wird später Teil des fachlichen Kontexts des Dokuments.</li>
|
||||
<li>Ein präziser Titel verbessert Retrieval, Chunk-Einordnung und spätere Tag-Pflege.</li>
|
||||
<li>Generische Titel wie <code>Dokument 1</code> oder nur Dateinamen sind deutlich schwächer.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-5">
|
||||
<h5 class="text-info mb-3">Gute Beispiele</h5>
|
||||
|
||||
<ul class="small mb-0">
|
||||
<li><code>Testomat 808 – Technisches Datenblatt</code></li>
|
||||
<li><code>Resthärte-Messung – Produktübersicht</code></li>
|
||||
<li><code>Indikator 300 – Anwendung und Dosierung</code></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card bg-black border-secondary text-light shadow-sm">
|
||||
<div class="card-body">
|
||||
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
@@ -22,31 +69,24 @@
|
||||
name="_token"
|
||||
value="{{ csrf_token('create_document') }}">
|
||||
|
||||
{# ============================= #}
|
||||
{# Titel #}
|
||||
{# ============================= #}
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label">Titel</label>
|
||||
|
||||
<div class="alert alert-secondary small">
|
||||
<strong>Hinweis zur Qualität:</strong><br>
|
||||
Der Titel ist entscheidend für die semantische Einordnung
|
||||
der erzeugten Chunks. Jeder Chunk erhält den Titel als Kontext,
|
||||
wodurch Retrieval und Antwortqualität signifikant verbessert werden.<br><br>
|
||||
|
||||
Wird kein Titel angegeben, wird automatisch der Dateiname
|
||||
verwendet (nicht empfohlen).
|
||||
Verwende einen fachlich präzisen Titel, der Produkt, Thema oder Dokumenttyp klar beschreibt.
|
||||
Wenn kein Titel angegeben wird, wird automatisch der Dateiname verwendet.
|
||||
</div>
|
||||
|
||||
<input class="form-control bg-dark text-light border-secondary"
|
||||
name="title"
|
||||
placeholder="z. B. Sicherheitsdatenblatt – Produkt XY">
|
||||
</div>
|
||||
value="{{ app.request.get('title') }}"
|
||||
placeholder="z. B. Testomat 808 – Technisches Datenblatt">
|
||||
|
||||
{# ============================= #}
|
||||
{# Datei Upload #}
|
||||
{# ============================= #}
|
||||
<div class="form-text text-secondary">
|
||||
Der Titel muss nicht lang sein, aber fachlich eindeutig.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label">Datei</label>
|
||||
@@ -58,14 +98,22 @@
|
||||
|
||||
<div class="form-text text-secondary">
|
||||
Unterstützte Formate: PDF, DOCX, TXT, MD.
|
||||
Das Dokument wird versioniert gespeichert und anschließend
|
||||
indexiert.
|
||||
Nach dem Upload wird automatisch Version 1 erstellt und ein Ingest-Job gestartet.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# ============================= #}
|
||||
{# Submit #}
|
||||
{# ============================= #}
|
||||
<div class="card bg-dark border-secondary mb-4">
|
||||
<div class="card-body">
|
||||
<h6 class="text-info mb-3">Was passiert nach dem Speichern?</h6>
|
||||
|
||||
<ul class="small mb-0">
|
||||
<li>Das Dokument wird versioniert gespeichert.</li>
|
||||
<li>Die erste Version wird als aktuelle Version gesetzt.</li>
|
||||
<li>Ein asynchroner Ingest-Job verarbeitet das Dokument für den Wissensindex.</li>
|
||||
<li>Später können dem Dokument gezielt Tags zugewiesen werden.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end">
|
||||
<button class="btn btn-outline-info">
|
||||
@@ -79,8 +127,7 @@
|
||||
</div>
|
||||
|
||||
<div class="mt-4 small text-secondary">
|
||||
Hinweis: Nach dem Upload wird automatisch eine neue Dokumentversion erstellt.
|
||||
Die Indexierung erfolgt asynchron über einen Ingest-Job.
|
||||
Hinweis: Ein sauber benanntes Dokument ist die beste Grundlage für gutes Retrieval und späteres präzises Tagging.
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
@@ -4,10 +4,13 @@
|
||||
|
||||
{% block body %}
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3 mb-0">
|
||||
Neue Version
|
||||
</h1>
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 flex-wrap gap-2">
|
||||
<div>
|
||||
<h1 class="h3 mb-1">Neue Version</h1>
|
||||
<div class="small text-muted">
|
||||
Neue unveränderliche Version für ein bestehendes Dokument hochladen.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="{{ path('admin_document_show', {id: document.id}) }}"
|
||||
class="btn btn-sm btn-outline-secondary">
|
||||
@@ -15,36 +18,99 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card bg-dark border-secondary mb-4 text-light">
|
||||
<div class="card-body">
|
||||
{% for message in app.flashes('success') %}
|
||||
<div class="alert alert-success shadow-sm">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<div class="mb-3">
|
||||
<strong>Dokument:</strong>
|
||||
<span class="text-light">{{ document.title }}</span>
|
||||
{% for message in app.flashes('danger') %}
|
||||
<div class="alert alert-danger shadow-sm">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% for message in app.flashes('info') %}
|
||||
<div class="alert alert-info shadow-sm">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<div class="card bg-dark border-secondary text-light mb-4 shadow-sm">
|
||||
<div class="card-body row g-4">
|
||||
<div class="col-lg-7">
|
||||
<h5 class="text-info mb-3">Dokumentkontext</h5>
|
||||
|
||||
<div class="mb-2">
|
||||
<strong>Dokument:</strong>
|
||||
<span class="text-light">{{ document.title }}</span>
|
||||
</div>
|
||||
|
||||
<div class="small text-secondary">
|
||||
Eine neue Version erzeugt eine zusätzliche, unveränderliche Dokumentversion.
|
||||
Die bestehende aktive Version bleibt zunächst unverändert.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="small text-secondary">
|
||||
Das Hochladen einer neuen Version erzeugt eine zusätzliche
|
||||
unveränderliche Dokumentversion. Die Aktivierung erfolgt separat
|
||||
und löst einen deterministischen Re-Ingest aus.
|
||||
</div>
|
||||
<div class="col-lg-5">
|
||||
<h5 class="text-info mb-3">Aktueller Stand</h5>
|
||||
|
||||
<div class="small mb-2">
|
||||
<strong>Aktive Version:</strong>
|
||||
{% if document.currentVersion %}
|
||||
<span class="badge bg-info text-dark">
|
||||
v{{ document.currentVersion.versionNumber }}
|
||||
</span>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="small mb-2">
|
||||
<strong>Vorhandene Versionen:</strong>
|
||||
{{ document.versions|length }}
|
||||
</div>
|
||||
|
||||
<div class="small">
|
||||
<strong>Zugewiesene Tags:</strong>
|
||||
{{ document.tags|length }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card bg-black border-secondary text-light">
|
||||
<div class="card bg-dark border-secondary text-light mb-4 shadow-sm">
|
||||
<div class="card-body row g-4">
|
||||
<div class="col-lg-7">
|
||||
<h5 class="text-info mb-3">Wichtig für den Lifecycle</h5>
|
||||
|
||||
<ul class="small mb-0">
|
||||
<li>Der Upload erzeugt nur eine <strong>neue Version</strong>, aber aktiviert sie nicht automatisch.</li>
|
||||
<li>Erst die spätere <strong>Aktivierung</strong> löst den deterministischen Re-Ingest aus.</li>
|
||||
<li>Tags bleiben auf <strong>Dokumentebene</strong> bestehen und gelten weiterhin für das Dokument als Ganzes.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-5">
|
||||
<h5 class="text-info mb-3">Gute Praxis</h5>
|
||||
|
||||
<ul class="small mb-0">
|
||||
<li>Nur fachlich wirklich passende Nachfolgeversionen hochladen.</li>
|
||||
<li>Kein anderes Thema oder anderes Produkt in dieselbe Dokumentlinie mischen.</li>
|
||||
<li>Bei stark verändertem Fachinhalt später Tagging mitprüfen.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card bg-black border-secondary text-light shadow-sm">
|
||||
<div class="card-body">
|
||||
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
|
||||
<input type="hidden"
|
||||
name="_token"
|
||||
value="{{ csrf_token('create_document_version_' ~ document.id) }}">
|
||||
|
||||
{# ============================= #}
|
||||
{# Datei Upload #}
|
||||
{# ============================= #}
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label">Datei auswählen</label>
|
||||
|
||||
@@ -54,15 +120,23 @@
|
||||
required>
|
||||
|
||||
<div class="form-text text-secondary">
|
||||
Unterstützte Formate: PDF, DOCX, TXT, MD.<br>
|
||||
Die Datei wird versioniert gespeichert und mit einer
|
||||
eindeutigen Checksum versehen.
|
||||
Unterstützte Formate: PDF, DOCX, TXT, MD.
|
||||
Die Datei wird versioniert gespeichert und mit einer eindeutigen Checksum versehen.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# ============================= #}
|
||||
{# Submit #}
|
||||
{# ============================= #}
|
||||
<div class="card bg-dark border-secondary mb-4">
|
||||
<div class="card-body">
|
||||
<h6 class="text-info mb-3">Was passiert nach dem Upload?</h6>
|
||||
|
||||
<ul class="small mb-0">
|
||||
<li>Es wird eine neue, unveränderliche Dokumentversion angelegt.</li>
|
||||
<li>Die aktive Version bleibt zunächst unverändert.</li>
|
||||
<li>Ein Re-Ingest erfolgt erst nach späterer Aktivierung dieser Version.</li>
|
||||
<li>Danach wird der Wissensindex deterministisch neu aufgebaut.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if is_granted('ROLE_SUPER_ADMIN') %}
|
||||
<div class="d-flex justify-content-end">
|
||||
@@ -71,16 +145,14 @@
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 small text-secondary">
|
||||
Hinweis: Eine neue Version ersetzt nicht automatisch die aktive Version.
|
||||
Erst nach Aktivierung wird ein Re-Ingest durchgeführt und der Index
|
||||
neu aufgebaut.
|
||||
Hinweis: Eine neue Version verbessert den Dokument-Lifecycle nur dann sauber, wenn sie fachlich wirklich zu diesem Dokument gehört.
|
||||
Bei stark verändertem Inhalt sollten nach der späteren Aktivierung auch die Tags geprüft werden.
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
@@ -4,116 +4,225 @@
|
||||
|
||||
{% block body %}
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1 class="h3 mb-0">{{ document.title ?? 'Ein Fehler trat auf' }}</h1>
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 flex-wrap gap-2">
|
||||
<div>
|
||||
<h1 class="h3 mb-1">{{ document.title }}</h1>
|
||||
<div class="small text-muted">
|
||||
Detailansicht für Dokument, Versionen und Tag-Zuordnung.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="{{ path('admin_documents') }}"
|
||||
class="btn btn-sm btn-outline-secondary">
|
||||
Zurück zur Übersicht
|
||||
</a>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<a href="{{ path('admin_document_tags_edit', {id: document.id}) }}"
|
||||
class="btn btn-sm btn-outline-info">
|
||||
Tags bearbeiten
|
||||
</a>
|
||||
|
||||
<a href="{{ path('admin_documents') }}"
|
||||
class="btn btn-sm btn-outline-secondary">
|
||||
Zurück zur Übersicht
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if document %}
|
||||
|
||||
{# ============================= #}
|
||||
{# Dokument-Meta #}
|
||||
{# ============================= #}
|
||||
|
||||
<div class="card bg-dark border-secondary mb-5 text-light">
|
||||
<div class="card-body">
|
||||
|
||||
<div class="mb-2">
|
||||
<strong>Status:</strong>
|
||||
{% if document.status == 'ACTIVE' %}
|
||||
<span class="badge bg-success">Aktiv</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">Archiviert</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<strong>Erstellt von:</strong>
|
||||
{{ document.createdBy ? document.createdBy.email : '-' }}
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<strong>Erstellt am:</strong>
|
||||
{{ document.createdAt|date('d.m.Y H:i') }}
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<strong>Aktive Version:</strong>
|
||||
{% if document.currentVersion %}
|
||||
<span class="badge bg-info text-dark">
|
||||
v{{ document.currentVersion.versionNumber }}
|
||||
</span>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% for message in app.flashes('success') %}
|
||||
<div class="alert alert-success shadow-sm">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{# ============================= #}
|
||||
{# Versionen #}
|
||||
{# ============================= #}
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h2 class="h5 mb-0">Versionen</h2>
|
||||
|
||||
{% if is_granted('ROLE_SUPER_ADMIN') %}
|
||||
<a href="{{ path('admin_document_version_new', {id: document.id}) }}"
|
||||
class="btn btn-sm btn-outline-info">
|
||||
Neue Version
|
||||
</a>
|
||||
{% endif %}
|
||||
{% for message in app.flashes('danger') %}
|
||||
<div class="alert alert-danger shadow-sm">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% if document.versions is empty %}
|
||||
{% for message in app.flashes('info') %}
|
||||
<div class="alert alert-info shadow-sm">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<div class="alert alert-secondary">
|
||||
Keine Versionen vorhanden.
|
||||
</div>
|
||||
<div class="row g-4 mb-4">
|
||||
|
||||
{% else %}
|
||||
|
||||
<div class="card bg-black border-secondary">
|
||||
<div class="col-lg-7">
|
||||
<div class="card bg-dark border-secondary text-light h-100 shadow-sm">
|
||||
<div class="card-body">
|
||||
<h5 class="text-info mb-3">Dokument-Metadaten</h5>
|
||||
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<div class="small text-muted mb-1">Status</div>
|
||||
<div>
|
||||
{% if document.status == 'ACTIVE' %}
|
||||
<span class="badge bg-success">Aktiv</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">Archiviert</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="small text-muted mb-1">Aktive Version</div>
|
||||
<div>
|
||||
{% if document.currentVersion %}
|
||||
<span class="badge bg-info text-dark">
|
||||
v{{ document.currentVersion.versionNumber }}
|
||||
</span>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="small text-muted mb-1">Erstellt von</div>
|
||||
<div>{{ document.createdBy ? document.createdBy.email : '-' }}</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="small text-muted mb-1">Erstellt am</div>
|
||||
<div>{{ document.createdAt|date('d.m.Y H:i:s') }}</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="small text-muted mb-1">Anzahl Versionen</div>
|
||||
<div>{{ document.versions|length }}</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="small text-muted mb-1">Zugewiesene Tags</div>
|
||||
<div>{{ document.tags|length }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if is_granted('ROLE_SUPER_ADMIN') %}
|
||||
<hr class="border-secondary">
|
||||
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<a href="{{ path('admin_document_version_new', {id: document.id}) }}"
|
||||
class="btn btn-sm btn-outline-info">
|
||||
Neue Version
|
||||
</a>
|
||||
|
||||
<form method="post"
|
||||
action="{{ path('admin_document_delete', {id: document.id}) }}"
|
||||
class="d-inline"
|
||||
onsubmit="return confirm('Dokument wirklich löschen? Der Inhalt wird per Delete-Job aus dem Index entfernt.');">
|
||||
<input type="hidden"
|
||||
name="_token"
|
||||
value="{{ csrf_token('delete_document_' ~ document.id) }}">
|
||||
<button class="btn btn-sm btn-outline-danger">
|
||||
Dokument löschen
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-5">
|
||||
<div class="card bg-dark border-secondary text-light h-100 shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h5 class="text-info mb-0">Tags</h5>
|
||||
|
||||
<a href="{{ path('admin_document_tags_edit', {id: document.id}) }}"
|
||||
class="btn btn-sm btn-outline-light">
|
||||
Bearbeiten
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% if document.tags is empty %}
|
||||
<div class="alert alert-secondary mb-0">
|
||||
Diesem Dokument sind noch keine Tags zugewiesen.
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
{% for tag in document.tags %}
|
||||
<span class="badge px-3 py-2
|
||||
{% if tag.type == 'catalog_entity' %}
|
||||
text-bg-info
|
||||
{% elseif tag.type == 'sales_signal' %}
|
||||
text-bg-warning
|
||||
{% else %}
|
||||
text-bg-secondary
|
||||
{% endif %}">
|
||||
{{ tag.label }}
|
||||
</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="small text-muted mt-3">
|
||||
Tags steuern die semantische Routing-Ebene. Weise nur fachlich wirklich passende Tags zu.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3 flex-wrap gap-2">
|
||||
<div>
|
||||
<h2 class="h5 mb-1">Versionen</h2>
|
||||
<div class="small text-muted">
|
||||
Beim Aktivieren einer Version wird automatisch ein Re-Ingest ausgelöst.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if is_granted('ROLE_SUPER_ADMIN') %}
|
||||
<a href="{{ path('admin_document_version_new', {id: document.id}) }}"
|
||||
class="btn btn-sm btn-outline-info">
|
||||
Neue Version
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if document.versions is empty %}
|
||||
|
||||
<div class="alert alert-secondary shadow-sm">
|
||||
Keine Versionen vorhanden.
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
|
||||
<div class="card bg-black border-secondary shadow-sm">
|
||||
<div class="card-body p-0">
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-dark table-striped table-hover align-middle mb-0">
|
||||
<thead class="table-secondary text-dark">
|
||||
<tr>
|
||||
<th>Version</th>
|
||||
<th>Status</th>
|
||||
<th>Ingest</th>
|
||||
<th>Checksum</th>
|
||||
<th>Erstellt von</th>
|
||||
<th>Datum</th>
|
||||
<th class="text-end">Aktionen</th>
|
||||
<th style="width: 10%">Version</th>
|
||||
<th style="width: 10%">Aktiv</th>
|
||||
<th style="width: 14%">Ingest</th>
|
||||
<th style="width: 18%">Checksum</th>
|
||||
<th style="width: 16%">Erstellt von</th>
|
||||
<th style="width: 14%">Datum</th>
|
||||
<th class="text-end" style="width: 18%">Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
{% for version in document.versions %}
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<strong>v{{ version.versionNumber }}</strong>
|
||||
{% if document.currentVersion and version.id == document.currentVersion.id %}
|
||||
<div class="small text-info mt-1">Current</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
{# Aktivstatus #}
|
||||
<td>
|
||||
{% if version.isActive %}
|
||||
<span class="badge bg-success">Aktiv</span>
|
||||
{% else %}
|
||||
<span class="badge bg-dark border border-secondary">
|
||||
Inaktiv
|
||||
</span>
|
||||
<span class="badge bg-dark border border-secondary">Inaktiv</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
{# Ingest Status #}
|
||||
<td>
|
||||
{% if version.ingestStatus == 'INDEXED' %}
|
||||
<span class="badge bg-success">INDEXED</span>
|
||||
@@ -125,99 +234,85 @@
|
||||
<span class="badge bg-secondary">PENDING</span>
|
||||
{% else %}
|
||||
<span class="badge bg-dark border border-secondary">
|
||||
{{ version.ingestStatus }}
|
||||
</span>
|
||||
{{ version.ingestStatus ?: '-' }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
{# Checksum #}
|
||||
<td class="small text-secondary">
|
||||
{{ version.checksum ? version.checksum[:10] ~ '…' : '-' }}
|
||||
{% if version.checksum %}
|
||||
<code>{{ version.checksum[:12] ~ '…' }}</code>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
{# Created by #}
|
||||
<td>
|
||||
{{ version.createdBy ? version.createdBy.email : '-' }}
|
||||
</td>
|
||||
|
||||
{# Date #}
|
||||
<td class="small">
|
||||
{{ version.createdAt|date('d.m.Y H:i') }}
|
||||
{{ version.createdAt|date('d.m.Y H:i:s') }}
|
||||
</td>
|
||||
|
||||
{# Aktionen #}
|
||||
<td class="text-end">
|
||||
|
||||
{% if version.isActive %}
|
||||
|
||||
{% if version.ingestStatus in ['PENDING', 'FAILED'] and is_granted('ROLE_SUPER_ADMIN') %}
|
||||
|
||||
<form method="post"
|
||||
action="{{ path('admin_document_version_ingest', {versionId: version.id}) }}"
|
||||
class="d-inline"
|
||||
onsubmit="return confirm('Ingest erneut starten?');">
|
||||
|
||||
<input type="hidden"
|
||||
name="_token"
|
||||
value="{{ csrf_token('ingest_version_' ~ version.id) }}">
|
||||
|
||||
<button class="btn btn-sm btn-outline-info">
|
||||
Ingest starten
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="d-flex justify-content-end flex-wrap gap-2">
|
||||
{% if version.isActive %}
|
||||
{% if version.ingestStatus in ['PENDING', 'FAILED'] and is_granted('ROLE_SUPER_ADMIN') %}
|
||||
<form method="post"
|
||||
action="{{ path('admin_document_version_ingest', {versionId: version.id}) }}"
|
||||
class="d-inline"
|
||||
onsubmit="return confirm('Ingest erneut starten?');">
|
||||
<input type="hidden"
|
||||
name="_token"
|
||||
value="{{ csrf_token('ingest_version_' ~ version.id) }}">
|
||||
<button class="btn btn-sm btn-outline-info">
|
||||
Ingest starten
|
||||
</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<span class="small text-success align-self-center">
|
||||
Keine Aktion nötig
|
||||
</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="text-success small">
|
||||
Bereits indexiert
|
||||
</span>
|
||||
{% if is_granted('ROLE_SUPER_ADMIN') %}
|
||||
<form method="post"
|
||||
action="{{ path('admin_document_version_activate', {versionId: version.id}) }}"
|
||||
class="d-inline"
|
||||
onsubmit="return confirm('Diese Version aktivieren? Es wird ein Re-Ingest ausgelöst.');">
|
||||
<input type="hidden"
|
||||
name="_token"
|
||||
value="{{ csrf_token('activate_version_' ~ version.id) }}">
|
||||
<button class="btn btn-sm btn-outline-light">
|
||||
Aktivieren
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
|
||||
{% if is_granted('ROLE_SUPER_ADMIN') %}
|
||||
<form method="post"
|
||||
action="{{ path('admin_document_version_activate', {versionId: version.id}) }}"
|
||||
class="d-inline"
|
||||
onsubmit="return confirm('Diese Version aktivieren? Es wird ein Re-Ingest ausgelöst.');">
|
||||
|
||||
<input type="hidden"
|
||||
name="_token"
|
||||
value="{{ csrf_token('activate_version_' ~ version.id) }}">
|
||||
|
||||
<button class="btn btn-sm btn-outline-light">
|
||||
Aktivieren
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-4 small text-secondary">
|
||||
Hinweis: Beim Aktivieren einer Version wird automatisch ein Re-Ingest
|
||||
durchgeführt. Der NDJSON-Index und der FAISS-Index werden deterministisch
|
||||
neu aufgebaut.
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
|
||||
<div class="alert alert-danger">
|
||||
Dokument nicht gefunden.
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
<div class="card bg-dark border-secondary text-light mt-4 shadow-sm">
|
||||
<div class="card-body">
|
||||
<h5 class="text-info mb-3">Hinweis zum Lifecycle</h5>
|
||||
<div class="small text-light">
|
||||
Beim Aktivieren einer Version wird automatisch ein Re-Ingest durchgeführt.
|
||||
Der NDJSON-Bestand und der Vektorindex werden deterministisch neu aufgebaut.
|
||||
Wenn Tags zugewiesen sind, beeinflusst dieses Dokument zusätzlich die semantische Routing-Ebene.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user