Files
Document_editor/frontend/src/pages/DocumentDetailPage.tsx

164 lines
5.2 KiB
TypeScript

import { useEffect, useState } from 'react'
import { Link, useNavigate, useParams } from 'react-router-dom'
import { api, downloadWithAuth } from '../api'
import ConfirmDialog from '../components/ConfirmDialog'
import { useAuth } from '../context/AuthContext'
import { useI18n } from '../context/I18nContext'
import type { DocumentTemplate, FilledDocument } from '../types'
export default function DocumentDetailPage() {
const { id } = useParams<{ id: string }>()
const navigate = useNavigate()
const { user } = useAuth()
const { t, formatDateTime } = useI18n()
const [document, setDocument] = useState<FilledDocument | null>(null)
const [template, setTemplate] = useState<DocumentTemplate | null>(null)
const [previewHtml, setPreviewHtml] = useState<string | null>(null)
const [error, setError] = useState('')
const [loading, setLoading] = useState(true)
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false)
useEffect(() => {
const load = async () => {
try {
const doc = await api.getDocument(Number(id))
setDocument(doc)
const loadedTemplate = await api.getTemplate(doc.template_id)
setTemplate(loadedTemplate)
const preview = await api.previewDocument({
template_id: doc.template_id,
name: doc.name,
field_data: doc.field_data,
})
setPreviewHtml(preview.html)
} catch (err) {
setError(err instanceof Error ? err.message : t('errors.loadFailed'))
} finally {
setLoading(false)
}
}
load()
}, [id])
const handleExportDocx = async () => {
if (!document) return
try {
await downloadWithAuth(
api.getExportDocxUrl(document.id),
`${document.name}.docx`,
)
} catch (err) {
alert(err instanceof Error ? err.message : t('errors.exportFailed'))
}
}
const handleExportPdf = async () => {
if (!document) return
try {
await downloadWithAuth(
api.getExportPdfUrl(document.id),
`${document.name}.pdf`,
)
} catch (err) {
alert(err instanceof Error ? err.message : t('errors.exportPdfFailed'))
}
}
const handleDelete = async () => {
if (!document) return
try {
await api.deleteDocument(document.id)
navigate('/documents')
} catch (err) {
alert(err instanceof Error ? err.message : t('errors.deleteFailed'))
}
}
const canManage =
user?.role === 'admin' || document?.owner_id === user?.id
if (loading) return <div className="container"><p>{t('common.loading')}</p></div>
if (!document) return <div className="container"><div className="error">{error}</div></div>
return (
<div className="container">
<ConfirmDialog
open={showDeleteConfirm}
title={t('documents.deleteTitle')}
message={t('documents.deleteMessage', {
name: document.name,
cannotUndo: t('common.cannotUndo'),
})}
confirmLabel={t('common.delete')}
danger
onConfirm={() => void handleDelete()}
onCancel={() => setShowDeleteConfirm(false)}
/>
<div className="page-header">
<div>
<h1>{document.name}</h1>
{template && (
<div className="style-hint">
{t('documents.fromTemplate', { name: template.name })}
{document.owner_username && ` · ${t('documents.ownerLine', { name: document.owner_username })}`}
{' · '}
{t('documents.saved', { date: formatDateTime(document.created_at) })}
</div>
)}
</div>
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
<button className="btn btn-primary" onClick={handleExportDocx}>
{t('documents.exportDocx')}
</button>
<button className="btn btn-secondary" onClick={handleExportPdf}>
{t('documents.exportPdf')}
</button>
<Link to={`/documents/${document.id}/edit`} className="btn btn-secondary">
{t('common.edit')}
</Link>
{template && (
<Link to={`/templates/${template.id}/fill`} className="btn btn-secondary">
{t('documents.reuseTemplate')}
</Link>
)}
{canManage && (
<button className="btn btn-danger" onClick={() => setShowDeleteConfirm(true)}>
{t('common.delete')}
</button>
)}
</div>
</div>
{error && <div className="error">{error}</div>}
<div className="grid-2">
<div className="card">
<h2 style={{ marginTop: 0 }}>{t('documents.preview')}</h2>
{previewHtml ? (
<div
className="doc-preview"
dangerouslySetInnerHTML={{ __html: previewHtml }}
/>
) : (
<p>{t('documents.previewUnavailable')}</p>
)}
</div>
<div className="card">
<h2 style={{ marginTop: 0 }}>{t('documents.fieldData')}</h2>
<pre style={{
background: '#f8fafc',
padding: 16,
borderRadius: 8,
overflow: 'auto',
fontSize: '0.85rem',
}}>
{JSON.stringify(document.field_data, null, 2)}
</pre>
</div>
</div>
</div>
)
}