feat: Dokumentengenerator — Vollständige Vorlage-Bibliothek + Frontend-Redesign
All checks were successful
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Successful in 36s
CI / test-python-backend-compliance (push) Successful in 31s
CI / test-python-document-crawler (push) Successful in 23s
CI / test-python-dsms-gateway (push) Successful in 18s

- Migration 020: Typ-Renames (data_processing_agreement→dpa, withdrawal_policy→widerruf) + 11 neue MIT-Templates (NDA DE/EN, SLA, AUP, Community Guidelines, Copyright Policy, Cloud Service Agreement, Data Usage Clause, Cookie Banner, AGB, Liability Clause)
- Backend: VALID_DOCUMENT_TYPES auf 16 Typen erweitert; /legal-templates/status nutzt jetzt dynamisches GROUP BY statt Hard-coded Felder
- searchTemplates.ts: loadAllTemplates() für Library-First UX
- page.tsx: Vollständig-Rewrite — Template-Bibliothek (immer sichtbar) mit Kategorie-Pills, Sprache-Toggle, optionaler Suche, Inline-Preview-Expand und Kachel-Grid; Generator-Section erscheint per Scroll wenn Vorlage gewählt
- Tests: 52/52 bestanden, TestLegalTemplateNewTypes (19 neue Tests) + aktualisierte Typ-Checks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-04 10:47:38 +01:00
parent 87dc22500d
commit 7e5047290c
6 changed files with 1098 additions and 691 deletions

View File

@@ -32,12 +32,25 @@ router = APIRouter(prefix="/legal-templates", tags=["legal-templates"])
DEFAULT_TENANT_ID = "9282a473-5c95-4b3a-bf78-0ecc0ec71d3e"
VALID_DOCUMENT_TYPES = {
# Original types
"privacy_policy",
"terms_of_service",
"impressum",
"data_processing_agreement",
"withdrawal_policy",
"cookie_policy",
# Renamed from data_processing_agreement / withdrawal_policy (Migration 020)
"dpa",
"widerruf",
# New types (Migration 020)
"nda",
"sla",
"acceptable_use",
"community_guidelines",
"copyright_policy",
"cloud_service_agreement",
"data_usage_clause",
"cookie_banner",
"agb",
"clause",
}
VALID_STATUSES = {"published", "draft", "archived"}
@@ -184,42 +197,38 @@ async def get_templates_status(
"""Return template counts by document_type."""
tenant_id = _get_tenant_id(x_tenant_id)
row = db.execute(text("""
SELECT
COUNT(*) AS total,
COUNT(*) FILTER (WHERE status = 'published') AS published,
COUNT(*) FILTER (WHERE status = 'draft') AS draft,
COUNT(*) FILTER (WHERE status = 'archived') AS archived,
COUNT(*) FILTER (WHERE document_type = 'privacy_policy') AS privacy_policy,
COUNT(*) FILTER (WHERE document_type = 'terms_of_service') AS terms_of_service,
COUNT(*) FILTER (WHERE document_type = 'impressum') AS impressum,
COUNT(*) FILTER (WHERE document_type = 'data_processing_agreement') AS data_processing_agreement,
COUNT(*) FILTER (WHERE document_type = 'withdrawal_policy') AS withdrawal_policy,
COUNT(*) FILTER (WHERE document_type = 'cookie_policy') AS cookie_policy
total_row = db.execute(
text("SELECT COUNT(*) FROM compliance_legal_templates WHERE tenant_id = :tenant_id"),
{"tenant_id": tenant_id},
).fetchone()
total = int(total_row[0] or 0) if total_row else 0
status_rows = db.execute(text("""
SELECT status, COUNT(*) AS cnt
FROM compliance_legal_templates
WHERE tenant_id = :tenant_id
"""), {"tenant_id": tenant_id}).fetchone()
GROUP BY status
"""), {"tenant_id": tenant_id}).fetchall()
by_status: Dict[str, int] = {r[0]: int(r[1] or 0) for r in status_rows}
if row:
d = dict(row._mapping)
counts = {k: int(v or 0) for k, v in d.items()}
return {
"total": counts["total"],
"by_status": {
"published": counts["published"],
"draft": counts["draft"],
"archived": counts["archived"],
},
"by_type": {
"privacy_policy": counts["privacy_policy"],
"terms_of_service": counts["terms_of_service"],
"impressum": counts["impressum"],
"data_processing_agreement": counts["data_processing_agreement"],
"withdrawal_policy": counts["withdrawal_policy"],
"cookie_policy": counts["cookie_policy"],
},
}
return {"total": 0, "by_status": {}, "by_type": {}}
type_rows = db.execute(text("""
SELECT document_type, COUNT(*) AS cnt
FROM compliance_legal_templates
WHERE tenant_id = :tenant_id
GROUP BY document_type
ORDER BY document_type
"""), {"tenant_id": tenant_id}).fetchall()
by_type: Dict[str, int] = {r[0]: int(r[1] or 0) for r in type_rows}
return {
"total": total,
"by_status": {
"published": by_status.get("published", 0),
"draft": by_status.get("draft", 0),
"archived": by_status.get("archived", 0),
},
"by_type": by_type,
}
@router.get("/sources")