chore: bump version to 0.8.96 and enhance legal document features
All checks were successful
Deploy Development / deploy (push) Successful in 40s
Test Suite / pytest-backend (push) Successful in 35s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / playwright-tests (push) Successful in 59s
All checks were successful
Deploy Development / deploy (push) Successful in 40s
Test Suite / pytest-backend (push) Successful in 35s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / playwright-tests (push) Successful in 59s
- Updated app version to 0.8.96 with a new build date of 2026-05-12. - Improved legal documents functionality with a live preview feature alongside the editor. - Added modal for full document preview and updated CSS styles for better layout. - Enhanced the AdminLegalDocumentsPage to support rendering previews of legal documents. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
81b9e8f601
commit
98edb282ed
|
|
@ -1,11 +1,11 @@
|
||||||
# Shinkan Jinkendo Version Information
|
# Shinkan Jinkendo Version Information
|
||||||
|
|
||||||
APP_VERSION = "0.8.95"
|
APP_VERSION = "0.8.96"
|
||||||
BUILD_DATE = "2026-05-12"
|
BUILD_DATE = "2026-05-12"
|
||||||
DB_SCHEMA_VERSION = "20260511053"
|
DB_SCHEMA_VERSION = "20260511053"
|
||||||
|
|
||||||
MODULE_VERSIONS = {
|
MODULE_VERSIONS = {
|
||||||
"legal_documents": "1.3.0", # P-01: Ausgabe §-Nummerierung pro Abschnitt; Markdown im Fließtext + PDF; gem. legalPdfExport
|
"legal_documents": "1.4.0", # Admin: Live-Vorschau pro Abschnitt + modale Vollvorschau (Editor + Dokumentenliste)
|
||||||
"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
|
||||||
|
|
@ -34,6 +34,13 @@ MODULE_VERSIONS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
CHANGELOG = [
|
CHANGELOG = [
|
||||||
|
{
|
||||||
|
"version": "0.8.96",
|
||||||
|
"date": "2026-05-12",
|
||||||
|
"changes": [
|
||||||
|
"P-01 Admin Rechtstexte: Live-Vorschau je Abschnitt (Markdown) neben dem Editor; modale „Vollständige Vorschau“ aus dem Formular; Augen-Symbol in der Dokumentenliste für die gerenderte Ansicht (API-Laden).",
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "0.8.95",
|
"version": "0.8.95",
|
||||||
"date": "2026-05-12",
|
"date": "2026-05-12",
|
||||||
|
|
|
||||||
|
|
@ -368,6 +368,82 @@ ul > li.card + li.card,
|
||||||
margin: 0.85em 0;
|
margin: 0.85em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Admin: Rechtstext Editor — Live-Vorschau neben Textarea */
|
||||||
|
.legal-section-split {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
@media (min-width: 720px) {
|
||||||
|
.legal-section-split {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
.legal-section-split__editor {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.legal-section-split__preview {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.legal-section-preview-box {
|
||||||
|
padding: 12px;
|
||||||
|
background: var(--surface2);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
|
.legal-section-preview-box h4 {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0 0 0.45rem;
|
||||||
|
color: var(--text1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modales Vorschau-Overlay (Rechtstexte Admin) */
|
||||||
|
.legal-preview-modal-backdrop {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 2000;
|
||||||
|
background: rgba(0, 0, 0, 0.45);
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
padding: max(16px, env(safe-area-inset-top, 0px)) 16px 24px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.legal-preview-modal {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 760px;
|
||||||
|
margin-top: 4vh;
|
||||||
|
margin-bottom: 4vh;
|
||||||
|
background: var(--surface);
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.2);
|
||||||
|
max-height: min(92vh, 960px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.legal-preview-modal__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.legal-preview-modal__body {
|
||||||
|
padding: 16px 18px 20px;
|
||||||
|
overflow-y: auto;
|
||||||
|
min-height: 0;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.form-input {
|
.form-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
|
||||||
111
frontend/src/components/LegalDocumentPreview.jsx
Normal file
111
frontend/src/components/LegalDocumentPreview.jsx
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
import { useEffect } from 'react'
|
||||||
|
import LegalDocumentBody from './LegalDocumentBody'
|
||||||
|
import { legalSectionNumber } from '../utils/legalPdfExport'
|
||||||
|
|
||||||
|
/** Inhalt wie auf der öffentlichen Rechtstextseite (inkl. §-Nummerierung). */
|
||||||
|
export function LegalDocumentPublicPreviewContent({
|
||||||
|
title,
|
||||||
|
sections,
|
||||||
|
showDraftNotice = true,
|
||||||
|
metaLine,
|
||||||
|
}) {
|
||||||
|
const safeTitle = (title || '').trim() || 'Ohne Titel'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="legal-admin-preview-inner">
|
||||||
|
{showDraftNotice && (
|
||||||
|
<div
|
||||||
|
className="card"
|
||||||
|
style={{
|
||||||
|
marginBottom: '1.25rem',
|
||||||
|
borderLeft: '4px solid var(--warn)',
|
||||||
|
background: 'var(--surface)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<strong style={{ color: 'var(--text1)' }}>Vorschau</strong>
|
||||||
|
<p style={{ margin: '0.35rem 0 0', fontSize: '0.88rem', color: 'var(--text2)' }}>
|
||||||
|
So erscheint der Text für Besucher nach Veröffentlichung (Markdown wird gerendert, §-Nummern wie online).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{metaLine && (
|
||||||
|
<p style={{ fontSize: '0.85rem', color: 'var(--text3)', margin: '0 0 1rem' }}>{metaLine}</p>
|
||||||
|
)}
|
||||||
|
<h1 style={{ margin: '0 0 1.5rem', color: 'var(--text1)', fontSize: '1.5rem' }}>{safeTitle}</h1>
|
||||||
|
{(sections || []).map((section, i) => (
|
||||||
|
<div key={i} style={{ marginBottom: '1.75rem' }}>
|
||||||
|
<h2 style={{ fontSize: '1.05rem', marginBottom: '0.4rem', color: 'var(--text1)' }}>
|
||||||
|
{section.heading?.trim()
|
||||||
|
? `${legalSectionNumber(i)} ${section.heading}`
|
||||||
|
: legalSectionNumber(i)}
|
||||||
|
</h2>
|
||||||
|
<LegalDocumentBody content={section.content} />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{sections?.length === 0 && (
|
||||||
|
<p style={{ color: 'var(--text3)', fontSize: '0.9rem' }}>Noch keine Abschnitte.</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modal: gerenderte Rechtstext-Vorschau (Editor oder gespeicherte Version).
|
||||||
|
*/
|
||||||
|
export function LegalPreviewModal({
|
||||||
|
open,
|
||||||
|
onClose,
|
||||||
|
title,
|
||||||
|
sections,
|
||||||
|
metaLine,
|
||||||
|
loading,
|
||||||
|
showDraftNotice = true,
|
||||||
|
}) {
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open) return
|
||||||
|
const onKey = (e) => {
|
||||||
|
if (e.key === 'Escape') onClose()
|
||||||
|
}
|
||||||
|
window.addEventListener('keydown', onKey)
|
||||||
|
return () => window.removeEventListener('keydown', onKey)
|
||||||
|
}, [open, onClose])
|
||||||
|
|
||||||
|
if (!open) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="legal-preview-modal-backdrop"
|
||||||
|
role="presentation"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="legal-preview-modal"
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-labelledby="legal-preview-modal-title"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<div className="legal-preview-modal__header">
|
||||||
|
<h3 id="legal-preview-modal-title" style={{ margin: 0, fontSize: '1.05rem' }}>
|
||||||
|
Öffentliche Darstellung
|
||||||
|
</h3>
|
||||||
|
<button type="button" className="btn btn-secondary" onClick={onClose}>
|
||||||
|
Schließen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="legal-preview-modal__body">
|
||||||
|
{loading ? (
|
||||||
|
<div className="spinner" style={{ margin: '2rem auto' }} />
|
||||||
|
) : (
|
||||||
|
<LegalDocumentPublicPreviewContent
|
||||||
|
title={title}
|
||||||
|
sections={sections}
|
||||||
|
metaLine={metaLine}
|
||||||
|
showDraftNotice={showDraftNotice}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import { useState, useEffect, useCallback } from 'react'
|
import { useState, useEffect, useCallback } from 'react'
|
||||||
import { FileText, Plus, Edit2, Archive, CheckCircle, Clock, Copy, Download, ChevronUp, ChevronDown } from 'lucide-react'
|
import { FileText, Plus, Edit2, Archive, CheckCircle, Clock, Copy, Download, ChevronUp, ChevronDown, Eye } from 'lucide-react'
|
||||||
import api from '../utils/api'
|
import api from '../utils/api'
|
||||||
import { generateLegalPdf } from '../utils/legalPdfExport'
|
import { generateLegalPdf, legalSectionNumber } from '../utils/legalPdfExport'
|
||||||
|
import LegalDocumentBody from '../components/LegalDocumentBody'
|
||||||
|
import { LegalPreviewModal } from '../components/LegalDocumentPreview'
|
||||||
|
|
||||||
const PDF_STATUS_META = { published: 'Gültig seit', draft: 'Entwurf — Stand', archived: 'Archiviert — Stand' }
|
const PDF_STATUS_META = { published: 'Gültig seit', draft: 'Entwurf — Stand', archived: 'Archiviert — Stand' }
|
||||||
|
|
||||||
|
|
@ -109,18 +111,35 @@ function SectionEditor({ sections, onChange }) {
|
||||||
placeholder="Abschnittsüberschrift"
|
placeholder="Abschnittsüberschrift"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-row">
|
<div className="legal-section-split">
|
||||||
<label className="form-label">
|
<div className="legal-section-split__editor">
|
||||||
Inhalt
|
<label className="form-label">
|
||||||
<span className="form-sub">
|
Inhalt
|
||||||
Einfache Markdown-Formatierung: **fett**, *kursiv*, Listen (- oder 1.), [Link](https://…), Zeilenumbruch mit Leerzeile.
|
<span className="form-sub">
|
||||||
|
Einfache Markdown-Formatierung: **fett**, *kursiv*, Listen (- oder 1.), [Link](https://…), Zeilenumbruch mit Leerzeile.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
className="form-input" rows={6} value={sec.content}
|
||||||
|
onChange={e => update(i, 'content', e.target.value)}
|
||||||
|
placeholder="Textinhalt des Abschnitts"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="legal-section-split__preview">
|
||||||
|
<span className="form-label" style={{ fontSize: '0.8rem', display: 'block', marginBottom: '6px' }}>
|
||||||
|
Live-Vorschau
|
||||||
</span>
|
</span>
|
||||||
</label>
|
<div className="legal-section-preview-box" style={{ marginBottom: 0 }}>
|
||||||
<textarea
|
<h4>
|
||||||
className="form-input" rows={4} value={sec.content}
|
{sec.heading?.trim()
|
||||||
onChange={e => update(i, 'content', e.target.value)}
|
? `${legalSectionNumber(i)} ${sec.heading}`
|
||||||
placeholder="Textinhalt des Abschnitts"
|
: legalSectionNumber(i)}
|
||||||
/>
|
</h4>
|
||||||
|
{sec.content
|
||||||
|
? <LegalDocumentBody content={sec.content} />
|
||||||
|
: <span style={{ color: 'var(--text3)', fontSize: '0.85rem' }}>(noch kein Text)</span>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<InsertButton afterIndex={i} />
|
<InsertButton afterIndex={i} />
|
||||||
|
|
@ -135,7 +154,7 @@ function SectionEditor({ sections, onChange }) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function DocumentRow({ doc, onPublish, onArchive, onEdit, onCopy, onDownload, onViewAudit }) {
|
function DocumentRow({ doc, onPublish, onArchive, onEdit, onCopy, onDownload, onViewAudit, onRenderedPreview }) {
|
||||||
const [downloading, setDownloading] = useState(false)
|
const [downloading, setDownloading] = useState(false)
|
||||||
|
|
||||||
const handleDownload = async () => {
|
const handleDownload = async () => {
|
||||||
|
|
@ -183,6 +202,10 @@ function DocumentRow({ doc, onPublish, onArchive, onEdit, onCopy, onDownload, on
|
||||||
<Archive size={13} />
|
<Archive size={13} />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
<button className="btn btn-secondary" style={{ padding: '4px 10px', fontSize: '0.78rem' }}
|
||||||
|
onClick={() => onRenderedPreview(doc)} title="Gerenderte Vorschau">
|
||||||
|
<Eye size={13} />
|
||||||
|
</button>
|
||||||
<button className="btn btn-secondary" style={{ padding: '4px 10px', fontSize: '0.78rem' }}
|
<button className="btn btn-secondary" style={{ padding: '4px 10px', fontSize: '0.78rem' }}
|
||||||
onClick={() => onCopy(doc)} title="Als neuen Entwurf kopieren">
|
onClick={() => onCopy(doc)} title="Als neuen Entwurf kopieren">
|
||||||
<Copy size={13} />
|
<Copy size={13} />
|
||||||
|
|
@ -200,7 +223,7 @@ function DocumentRow({ doc, onPublish, onArchive, onEdit, onCopy, onDownload, on
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function EditForm({ docType, editDoc, onSaved, onCancel }) {
|
function EditForm({ docType, editDoc, onSaved, onCancel, onShowRenderedPreview }) {
|
||||||
const [title, setTitle] = useState(editDoc ? editDoc.title : docType.defaultTitle)
|
const [title, setTitle] = useState(editDoc ? editDoc.title : docType.defaultTitle)
|
||||||
const [sections, setSections] = useState([])
|
const [sections, setSections] = useState([])
|
||||||
const [changeNote, setChangeNote] = useState('')
|
const [changeNote, setChangeNote] = useState('')
|
||||||
|
|
@ -266,10 +289,23 @@ function EditForm({ docType, editDoc, onSaved, onCancel }) {
|
||||||
onChange={e => setChangeNote(e.target.value)}
|
onChange={e => setChangeNote(e.target.value)}
|
||||||
placeholder="z. B. Erste Version nach juristischer Prüfung" />
|
placeholder="z. B. Erste Version nach juristischer Prüfung" />
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: 'flex', gap: '0.75rem', marginTop: '0.5rem' }}>
|
<div style={{ display: 'flex', gap: '0.75rem', marginTop: '0.5rem', flexWrap: 'wrap' }}>
|
||||||
<button className="btn btn-primary" onClick={handleSave} disabled={saving || !title.trim()}>
|
<button className="btn btn-primary" onClick={handleSave} disabled={saving || !title.trim()}>
|
||||||
{saving ? 'Speichern…' : 'Entwurf speichern'}
|
{saving ? 'Speichern…' : 'Entwurf speichern'}
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-secondary"
|
||||||
|
onClick={() =>
|
||||||
|
onShowRenderedPreview?.({
|
||||||
|
title,
|
||||||
|
sections,
|
||||||
|
metaLine: 'Aktueller Editorstand (nicht automatisch gespeichert)',
|
||||||
|
})}
|
||||||
|
style={{ display: 'flex', alignItems: 'center', gap: '0.35rem' }}
|
||||||
|
>
|
||||||
|
<Eye size={15} /> Vollständige Vorschau
|
||||||
|
</button>
|
||||||
<button className="btn btn-secondary" onClick={onCancel}>Abbrechen</button>
|
<button className="btn btn-secondary" onClick={onCancel}>Abbrechen</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -329,6 +365,13 @@ export default function AdminLegalDocumentsPage() {
|
||||||
const [editDoc, setEditDoc] = useState(null)
|
const [editDoc, setEditDoc] = useState(null)
|
||||||
const [auditDocId, setAuditDocId] = useState(null)
|
const [auditDocId, setAuditDocId] = useState(null)
|
||||||
const [confirmPublish, setConfirmPublish] = useState(null)
|
const [confirmPublish, setConfirmPublish] = useState(null)
|
||||||
|
const [legalPreview, setLegalPreview] = useState({
|
||||||
|
open: false,
|
||||||
|
loading: false,
|
||||||
|
title: '',
|
||||||
|
sections: [],
|
||||||
|
metaLine: '',
|
||||||
|
})
|
||||||
|
|
||||||
const activeDocType = DOC_TYPES.find(d => d.key === activeType)
|
const activeDocType = DOC_TYPES.find(d => d.key === activeType)
|
||||||
|
|
||||||
|
|
@ -376,6 +419,42 @@ export default function AdminLegalDocumentsPage() {
|
||||||
catch (e) { alert('Fehler: ' + e.message) }
|
catch (e) { alert('Fehler: ' + e.message) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const closeLegalPreview = () =>
|
||||||
|
setLegalPreview({ open: false, loading: false, title: '', sections: [], metaLine: '' })
|
||||||
|
|
||||||
|
const openLegalPreviewFromEditor = (payload) => {
|
||||||
|
setLegalPreview({
|
||||||
|
open: true,
|
||||||
|
loading: false,
|
||||||
|
title: payload.title,
|
||||||
|
sections: payload.sections,
|
||||||
|
metaLine: payload.metaLine || '',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const openLegalPreviewFromList = async (doc) => {
|
||||||
|
setLegalPreview({
|
||||||
|
open: true,
|
||||||
|
loading: true,
|
||||||
|
title: '',
|
||||||
|
sections: [],
|
||||||
|
metaLine: 'Laden…',
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
const full = await api.getLegalDocument(doc.id)
|
||||||
|
setLegalPreview({
|
||||||
|
open: true,
|
||||||
|
loading: false,
|
||||||
|
title: full.title,
|
||||||
|
sections: full.content_sections || [],
|
||||||
|
metaLine: `Version ${full.version} · ${STATUS_LABELS[full.status]?.label || full.status}`,
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
closeLegalPreview()
|
||||||
|
alert('Fehler: ' + e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleDownload = async (doc) => {
|
const handleDownload = async (doc) => {
|
||||||
const full = await api.getLegalDocument(doc.id)
|
const full = await api.getLegalDocument(doc.id)
|
||||||
const dateStr = full.published_at
|
const dateStr = full.published_at
|
||||||
|
|
@ -473,6 +552,7 @@ export default function AdminLegalDocumentsPage() {
|
||||||
onCopy={handleCopy}
|
onCopy={handleCopy}
|
||||||
onDownload={handleDownload}
|
onDownload={handleDownload}
|
||||||
onViewAudit={handleViewAudit}
|
onViewAudit={handleViewAudit}
|
||||||
|
onRenderedPreview={openLegalPreviewFromList}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
|
|
@ -483,9 +563,19 @@ export default function AdminLegalDocumentsPage() {
|
||||||
editDoc={editDoc}
|
editDoc={editDoc}
|
||||||
onSaved={handleSaved}
|
onSaved={handleSaved}
|
||||||
onCancel={() => { setShowForm(false); setEditDoc(null) }}
|
onCancel={() => { setShowForm(false); setEditDoc(null) }}
|
||||||
|
onShowRenderedPreview={openLegalPreviewFromEditor}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<LegalPreviewModal
|
||||||
|
open={legalPreview.open}
|
||||||
|
onClose={closeLegalPreview}
|
||||||
|
title={legalPreview.title}
|
||||||
|
sections={legalPreview.sections}
|
||||||
|
metaLine={legalPreview.metaLine}
|
||||||
|
loading={legalPreview.loading}
|
||||||
|
/>
|
||||||
|
|
||||||
{auditDocId && (
|
{auditDocId && (
|
||||||
<AuditLog docId={auditDocId} onClose={() => setAuditDocId(null)} />
|
<AuditLog docId={auditDocId} onClose={() => setAuditDocId(null)} />
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user