DGSVO Compliance update 1 #30
|
|
@ -384,6 +384,70 @@ def archive_legal_document(
|
||||||
return r2d(updated)
|
return r2d(updated)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/api/admin/legal-documents/{doc_id}/copy-as-draft", status_code=201)
|
||||||
|
def copy_legal_document_as_draft(
|
||||||
|
doc_id: int,
|
||||||
|
session: dict = Depends(require_auth),
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Kopiert ein beliebiges Dokument (egal welcher Status) als neuen Entwurf mit
|
||||||
|
nächster Versionsnummer. Inhalt und Titel werden übernommen.
|
||||||
|
"""
|
||||||
|
_require_superadmin(session)
|
||||||
|
|
||||||
|
profile_id = session["profile_id"]
|
||||||
|
|
||||||
|
with get_db() as conn:
|
||||||
|
cur = get_cursor(conn)
|
||||||
|
cur.execute(
|
||||||
|
"SELECT id, document_type, title, content_sections FROM legal_documents WHERE id = %s",
|
||||||
|
(doc_id,),
|
||||||
|
)
|
||||||
|
row = cur.fetchone()
|
||||||
|
|
||||||
|
if not row:
|
||||||
|
raise HTTPException(status_code=404, detail="Dokument nicht gefunden")
|
||||||
|
|
||||||
|
src = r2d(row)
|
||||||
|
|
||||||
|
import json as _json
|
||||||
|
sections_json = _json.dumps(src["content_sections"], ensure_ascii=False)
|
||||||
|
|
||||||
|
with get_db() as conn:
|
||||||
|
cur = get_cursor(conn)
|
||||||
|
cur.execute(
|
||||||
|
"SELECT COALESCE(MAX(version), 0) FROM legal_documents WHERE document_type = %s",
|
||||||
|
(src["document_type"],),
|
||||||
|
)
|
||||||
|
row2 = cur.fetchone()
|
||||||
|
next_version = list(row2.values())[0] + 1
|
||||||
|
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO legal_documents
|
||||||
|
(document_type, version, title, content_sections, status, change_note, created_by_profile_id)
|
||||||
|
VALUES (%s, %s, %s, %s::jsonb, 'draft', NULL, %s)
|
||||||
|
RETURNING id, document_type, version, title, content_sections,
|
||||||
|
status, change_note, created_at, updated_at
|
||||||
|
""",
|
||||||
|
(src["document_type"], next_version, src["title"], sections_json, profile_id),
|
||||||
|
)
|
||||||
|
new_row = cur.fetchone()
|
||||||
|
new_id = list(new_row.values())[0]
|
||||||
|
|
||||||
|
cur.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO legal_document_audit
|
||||||
|
(legal_document_id, action, changed_by_profile_id, change_note)
|
||||||
|
VALUES (%s, 'created', %s, %s)
|
||||||
|
""",
|
||||||
|
(new_id, profile_id, f"Kopie von Version {src.get('version', '?')} (ID {doc_id})"),
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
|
||||||
|
return r2d(new_row)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/admin/legal-documents/{doc_id}/audit")
|
@router.get("/api/admin/legal-documents/{doc_id}/audit")
|
||||||
def get_legal_document_audit(doc_id: int, session: dict = Depends(require_auth)):
|
def get_legal_document_audit(doc_id: int, session: dict = Depends(require_auth)):
|
||||||
"""Änderungslog für ein Dokument."""
|
"""Änderungslog für ein Dokument."""
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
# Shinkan Jinkendo Version Information
|
# Shinkan Jinkendo Version Information
|
||||||
|
|
||||||
APP_VERSION = "0.8.71"
|
APP_VERSION = "0.8.72"
|
||||||
BUILD_DATE = "2026-05-10"
|
BUILD_DATE = "2026-05-10"
|
||||||
DB_SCHEMA_VERSION = "20260510047"
|
DB_SCHEMA_VERSION = "20260510047"
|
||||||
|
|
||||||
MODULE_VERSIONS = {
|
MODULE_VERSIONS = {
|
||||||
"legal_documents": "1.0.0", # P-01c: Admin-konfigurierbare Rechtstexte (legal_documents + legal_document_audit)
|
"legal_documents": "1.1.0", # Als-Entwurf-kopieren: POST /api/admin/legal-documents/{id}/copy-as-draft
|
||||||
"auth": "1.2.3", # P-05b: reset-password min_length=8 via Pydantic PasswordResetConfirm
|
"auth": "1.2.3", # P-05b: reset-password min_length=8 via Pydantic PasswordResetConfirm
|
||||||
"profiles": "1.7.0", # exercise_list_prefs JSONB (Standard Übungsfilter); Patch via ProfileUpdate + Json()
|
"profiles": "1.7.0", # exercise_list_prefs JSONB (Standard Übungsfilter); Patch via ProfileUpdate + Json()
|
||||||
"tenant_context": "1.0.5", # Plattform-Admin: effective_club ohne Header aus Profil active_club_id wenn Verein existiert
|
"tenant_context": "1.0.5", # Plattform-Admin: effective_club ohne Header aus Profil active_club_id wenn Verein existiert
|
||||||
|
|
@ -30,6 +30,13 @@ MODULE_VERSIONS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
CHANGELOG = [
|
CHANGELOG = [
|
||||||
|
{
|
||||||
|
"version": "0.8.72",
|
||||||
|
"date": "2026-05-10",
|
||||||
|
"changes": [
|
||||||
|
"Rechtstexte: Als-Entwurf-kopieren — POST /api/admin/legal-documents/{id}/copy-as-draft; Inhalt und Titel werden uebernommen, Versionsnummer inkrementiert",
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "0.8.71",
|
"version": "0.8.71",
|
||||||
"date": "2026-05-10",
|
"date": "2026-05-10",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useState, useEffect, useCallback } from 'react'
|
import { useState, useEffect, useCallback } from 'react'
|
||||||
import { FileText, Plus, Eye, Edit2, Archive, CheckCircle, Clock, ChevronDown, ChevronUp } from 'lucide-react'
|
import { FileText, Plus, Edit2, Archive, CheckCircle, Clock, Copy } from 'lucide-react'
|
||||||
import api from '../utils/api'
|
import api from '../utils/api'
|
||||||
|
|
||||||
const DOC_TYPES = [
|
const DOC_TYPES = [
|
||||||
|
|
@ -95,7 +95,7 @@ function DocTypeTab({ docType, active, onClick }) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function DocumentRow({ doc, onPublish, onArchive, onEdit, onViewAudit }) {
|
function DocumentRow({ doc, onPublish, onArchive, onEdit, onCopy, onViewAudit }) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="card"
|
className="card"
|
||||||
|
|
@ -156,6 +156,14 @@ function DocumentRow({ doc, onPublish, onArchive, onEdit, onViewAudit }) {
|
||||||
<Archive size={13} />
|
<Archive size={13} />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
<button
|
||||||
|
className="btn btn-secondary"
|
||||||
|
style={{ padding: '4px 10px', fontSize: '0.78rem' }}
|
||||||
|
onClick={() => onCopy(doc)}
|
||||||
|
title="Als neuen Entwurf kopieren"
|
||||||
|
>
|
||||||
|
<Copy size={13} />
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
className="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
style={{ padding: '4px 10px', fontSize: '0.78rem' }}
|
style={{ padding: '4px 10px', fontSize: '0.78rem' }}
|
||||||
|
|
@ -372,6 +380,15 @@ export default function AdminLegalDocumentsPage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleCopy = async (doc) => {
|
||||||
|
try {
|
||||||
|
await api.copyLegalDocumentAsDraft(doc.id)
|
||||||
|
load()
|
||||||
|
} catch (e) {
|
||||||
|
alert('Fehler: ' + e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleEdit = (doc) => {
|
const handleEdit = (doc) => {
|
||||||
setEditDoc(doc)
|
setEditDoc(doc)
|
||||||
setShowForm(true)
|
setShowForm(true)
|
||||||
|
|
@ -492,6 +509,7 @@ export default function AdminLegalDocumentsPage() {
|
||||||
onPublish={handlePublish}
|
onPublish={handlePublish}
|
||||||
onArchive={handleArchive}
|
onArchive={handleArchive}
|
||||||
onEdit={handleEdit}
|
onEdit={handleEdit}
|
||||||
|
onCopy={handleCopy}
|
||||||
onViewAudit={handleViewAudit}
|
onViewAudit={handleViewAudit}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -1532,6 +1532,8 @@ export const api = {
|
||||||
}),
|
}),
|
||||||
archiveLegalDocument: (id) =>
|
archiveLegalDocument: (id) =>
|
||||||
request(`/api/admin/legal-documents/${id}/archive`, { method: 'POST' }),
|
request(`/api/admin/legal-documents/${id}/archive`, { method: 'POST' }),
|
||||||
|
copyLegalDocumentAsDraft: (id) =>
|
||||||
|
request(`/api/admin/legal-documents/${id}/copy-as-draft`, { method: 'POST' }),
|
||||||
getLegalDocumentAudit: (id) =>
|
getLegalDocumentAudit: (id) =>
|
||||||
request(`/api/admin/legal-documents/${id}/audit`),
|
request(`/api/admin/legal-documents/${id}/audit`),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
// Shinkan Jinkendo Frontend Version
|
// Shinkan Jinkendo Frontend Version
|
||||||
|
|
||||||
export const APP_VERSION = "0.8.71"
|
export const APP_VERSION = "0.8.72"
|
||||||
export const BUILD_DATE = "2026-05-10"
|
export const BUILD_DATE = "2026-05-10"
|
||||||
|
|
||||||
export const PAGE_VERSIONS = {
|
export const PAGE_VERSIONS = {
|
||||||
LoginPage: "1.0.2",
|
LoginPage: "1.0.2",
|
||||||
LegalPage: "1.1.0",
|
LegalPage: "1.1.0",
|
||||||
SettingsLegalPage: "1.0.0",
|
SettingsLegalPage: "1.0.0",
|
||||||
AdminLegalDocumentsPage: "1.0.0",
|
AdminLegalDocumentsPage: "1.1.0",
|
||||||
Dashboard: "1.0.0",
|
Dashboard: "1.0.0",
|
||||||
AccountSettingsPage: "1.0.1",
|
AccountSettingsPage: "1.0.1",
|
||||||
ExercisesPage: "1.5.0", // Fokus +/- Regeln, nur ohne Fokusbereich; Filterprefs
|
ExercisesPage: "1.5.0", // Fokus +/- Regeln, nur ohne Fokusbereich; Filterprefs
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user