b0ecdc6cfb
Disables the form and displays a spinner overlay after clicking Entwurf generieren. Co-authored-by: Cursor <cursoragent@cursor.com>
203 lines
6.4 KiB
HTML
203 lines
6.4 KiB
HTML
{% extends "admin/base_site.html" %}
|
||
{% load i18n %}
|
||
|
||
{% block extrastyle %}
|
||
{{ block.super }}
|
||
<style>
|
||
.assistant-wrap { max-width: 820px; }
|
||
.chat-box {
|
||
background: #f8f9fa;
|
||
border: 1px solid #dee2e6;
|
||
border-radius: 8px;
|
||
padding: 1rem;
|
||
min-height: 120px;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
.chat-hint { color: #6c757d; font-size: 0.9rem; }
|
||
.assistant-card {
|
||
border: 1px solid #dee2e6;
|
||
border-radius: 8px;
|
||
padding: 1.25rem;
|
||
background: #fff;
|
||
position: relative;
|
||
}
|
||
.assistant-card.is-loading {
|
||
pointer-events: none;
|
||
}
|
||
.fk-assistant-loading {
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 2;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 8px;
|
||
background: rgba(255, 255, 255, 0.92);
|
||
}
|
||
.fk-assistant-loading[hidden] {
|
||
display: none !important;
|
||
}
|
||
.fk-assistant-loading-inner {
|
||
text-align: center;
|
||
color: #374151;
|
||
}
|
||
.fk-assistant-loading-inner p {
|
||
margin: 0.75rem 0 0;
|
||
font-weight: 500;
|
||
}
|
||
.fk-assistant-loading-hint {
|
||
margin-top: 0.35rem !important;
|
||
font-size: 0.85rem;
|
||
font-weight: 400 !important;
|
||
color: #6b7280;
|
||
}
|
||
.fk-spinner {
|
||
display: inline-block;
|
||
width: 2.25rem;
|
||
height: 2.25rem;
|
||
border: 3px solid #d1d5db;
|
||
border-top-color: #0d9488;
|
||
border-radius: 50%;
|
||
animation: fk-spin 0.75s linear infinite;
|
||
}
|
||
@keyframes fk-spin {
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
.fk-generate-btn.is-loading .fk-btn-label {
|
||
display: none;
|
||
}
|
||
.fk-generate-btn.is-loading .fk-btn-loading {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
.fk-btn-loading {
|
||
display: none;
|
||
}
|
||
.fk-btn-loading .fk-spinner {
|
||
width: 1rem;
|
||
height: 1rem;
|
||
border-width: 2px;
|
||
}
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block breadcrumbs %}
|
||
<div class="breadcrumbs">
|
||
<a href="{% url 'admin:index' %}">Start</a>
|
||
› <a href="{% url 'admin:claims_claimdraft_changelist' %}">Fakten-Entwürfe</a>
|
||
› Assistent
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="assistant-wrap">
|
||
<h1>Fakten-Assistent</h1>
|
||
<p class="chat-hint">
|
||
Link, Bild und/oder Beschreibung eingeben. Die KI erstellt einen <strong>Entwurf zur Prüfung</strong> –
|
||
nichts wird automatisch veröffentlicht.
|
||
</p>
|
||
|
||
<div class="chat-box">
|
||
<p class="mb-1"><strong>So funktioniert es:</strong></p>
|
||
<ol class="mb-0 ps-3">
|
||
<li>Eingaben senden (Artikel-URL, Beschreibung, optional Bild/PDF)</li>
|
||
<li>KI analysiert den Kontext und schlägt Kategorie & Fakteneintrag vor</li>
|
||
<li>Sie prüfen, bearbeiten und veröffentlichen erst nach Freigabe</li>
|
||
</ol>
|
||
</div>
|
||
|
||
<div class="assistant-card" id="fk-assistant-card">
|
||
<div class="fk-assistant-loading" id="fk-assistant-loading" hidden>
|
||
<div class="fk-assistant-loading-inner">
|
||
<span class="fk-spinner" aria-hidden="true"></span>
|
||
<p>Entwurf wird generiert…</p>
|
||
<p class="fk-assistant-loading-hint">Die KI analysiert Ihre Eingaben. Das kann einige Sekunden dauern.</p>
|
||
</div>
|
||
</div>
|
||
<form method="post" enctype="multipart/form-data" novalidate id="fk-assistant-form">
|
||
{% csrf_token %}
|
||
{% if form.non_field_errors %}
|
||
<div class="alert alert-danger">{{ form.non_field_errors }}</div>
|
||
{% endif %}
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label" for="{{ form.article_url.id_for_label }}">{{ form.article_url.label }}</label>
|
||
{{ form.article_url }}
|
||
{% if form.article_url.errors %}<div class="text-danger small">{{ form.article_url.errors }}</div>{% endif %}
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label" for="{{ form.user_notes.id_for_label }}">{{ form.user_notes.label }}</label>
|
||
{{ form.user_notes }}
|
||
{% if form.user_notes.errors %}<div class="text-danger small">{{ form.user_notes.errors }}</div>{% endif %}
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label" for="{{ form.uploaded_file.id_for_label }}">{{ form.uploaded_file.label }}</label>
|
||
{{ form.uploaded_file }}
|
||
<div class="form-text">JPG, PNG, GIF, WebP, SVG oder PDF. SVG wird für die KI intern in PNG umgewandelt.</div>
|
||
</div>
|
||
|
||
<button type="submit" class="button default fk-generate-btn" id="fk-generate-draft-btn">
|
||
<span class="fk-btn-label">Entwurf generieren</span>
|
||
<span class="fk-btn-loading">
|
||
<span class="fk-spinner" aria-hidden="true"></span>
|
||
Wird generiert…
|
||
</span>
|
||
</button>
|
||
</form>
|
||
</div>
|
||
|
||
{% if recent_drafts %}
|
||
<div class="mt-4">
|
||
<h2 class="h6 text-muted">Letzte Entwürfe</h2>
|
||
<ul>
|
||
{% for draft in recent_drafts %}
|
||
<li>
|
||
<a href="{% url 'admin:claims_claimdraft_review' draft.pk %}">
|
||
{{ draft.title|default:"(ohne Titel)" }}
|
||
</a>
|
||
– {{ draft.get_status_display }}
|
||
<span class="text-muted">({{ draft.created_at|date:"d.m.Y H:i" }})</span>
|
||
</li>
|
||
{% endfor %}
|
||
</ul>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block extrahead %}
|
||
{{ block.super }}
|
||
<script>
|
||
(function () {
|
||
var form = document.getElementById('fk-assistant-form');
|
||
if (!form) {
|
||
return;
|
||
}
|
||
|
||
form.addEventListener('submit', function () {
|
||
var card = document.getElementById('fk-assistant-card');
|
||
var overlay = document.getElementById('fk-assistant-loading');
|
||
var button = document.getElementById('fk-generate-draft-btn');
|
||
|
||
if (card) {
|
||
card.classList.add('is-loading');
|
||
}
|
||
if (overlay) {
|
||
overlay.hidden = false;
|
||
}
|
||
if (button) {
|
||
button.classList.add('is-loading');
|
||
button.disabled = true;
|
||
}
|
||
|
||
form.querySelectorAll('input:not([type="hidden"]), textarea, select, button').forEach(function (field) {
|
||
field.disabled = true;
|
||
});
|
||
});
|
||
})();
|
||
</script>
|
||
{% endblock %}
|