'use client'; import React, { useEffect, useRef, useState } from 'react'; import useContractManagement from '../hooks/useContractManagement'; type Props = { onSaved?: () => void; }; export default function ContractEditor({ onSaved }: Props) { const [name, setName] = useState(''); const [htmlCode, setHtmlCode] = useState(''); const [isPreview, setIsPreview] = useState(false); const [saving, setSaving] = useState(false); const [statusMsg, setStatusMsg] = useState(null); const [lang, setLang] = useState<'en' | 'de'>('en'); const [type, setType] = useState<'contract' | 'bill' | 'other'>('contract'); const [userType, setUserType] = useState<'personal' | 'company' | 'both'>('personal'); const [description, setDescription] = useState(''); const iframeRef = useRef(null); const { uploadTemplate, updateTemplateState } = useContractManagement(); // Build a full HTML doc if user pasted only a snippet const wrapIfNeeded = (src: string) => { const hasDoc = /]/i.test(src); if (hasDoc) return src; // Minimal A4 skeleton so snippets render and print correctly return ` Preview
${src}
`; }; // Write/refresh iframe preview useEffect(() => { if (!isPreview) return; const iframe = iframeRef.current; if (!iframe) return; const doc = iframe.contentDocument || iframe.contentWindow?.document; if (!doc) return; const html = wrapIfNeeded(htmlCode); doc.open(); doc.write(html); doc.close(); const resize = () => { // Allow time for layout/styles requestAnimationFrame(() => { const h = doc.body ? Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight) : 1200; iframe.style.height = Math.min(Math.max(h, 1123), 2000) + 'px'; // clamp for UX }); }; // Initial resize and after load resize(); const onLoad = () => resize(); iframe.addEventListener('load', onLoad); // Also observe mutations to adjust height if content changes const mo = new MutationObserver(resize); mo.observe(doc.documentElement, { childList: true, subtree: true, attributes: true, characterData: true }); return () => { iframe.removeEventListener('load', onLoad); mo.disconnect(); }; }, [isPreview, htmlCode]); const printPreview = () => { const w = iframeRef.current?.contentWindow; w?.focus(); w?.print(); }; const slug = (s: string) => s.toLowerCase().trim().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'template'; // NEW: all-fields-required guard const canSave = Boolean( name.trim() && htmlCode.trim() && description.trim() && type && userType && lang ) const save = async (publish: boolean) => { const html = htmlCode.trim(); // NEW: validate all fields if (!canSave) { setStatusMsg('Please fill all required fields (name, HTML, type, user type, language, description).'); return; } setSaving(true); setStatusMsg(null); try { // Build a file from HTML code const file = new File([html], `${slug(name)}.html`, { type: 'text/html' }); const created = await uploadTemplate({ file, name, type, lang, description: description || undefined, user_type: userType, }); if (publish && created?.id) { await updateTemplateState(created.id, 'active'); } setStatusMsg(publish ? 'Template created and activated.' : 'Template created.'); if (onSaved) onSaved(); // Optionally clear fields // setName(''); setHtmlCode(''); setDescription(''); setType('contract'); setLang('en'); } catch (e: any) { setStatusMsg(e?.message || 'Save failed.'); } finally { setSaving(false); } }; return (
setName(e.target.value)} required className="w-full sm:w-1/2 rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm text-gray-900 outline-none focus:ring-2 focus:ring-indigo-500/50 shadow" />
{isPreview && ( )}
{/* New metadata inputs */}
setDescription(e.target.value)} required className="w-full rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm text-gray-900 outline-none focus:ring-2 focus:ring-indigo-500/50 shadow" />
{!isPreview && (