feat: remove unused state variables and improve signature drawing logic

This commit is contained in:
seaznCode 2026-01-14 18:42:55 +01:00
parent 0c8b00d007
commit fa37a844e8
2 changed files with 21 additions and 103 deletions

View File

@ -16,11 +16,6 @@ export default function CompanySignContractPage() {
const { userStatus, loading: statusLoading, refreshStatus } = useUserStatus()
const { showToast } = useToast()
const [companyName, setCompanyName] = useState('')
const [repName, setRepName] = useState('')
const [repTitle, setRepTitle] = useState('')
const [location, setLocation] = useState('')
const [note, setNote] = useState('')
const [date, setDate] = useState('')
const [signatureDataUrl, setSignatureDataUrl] = useState('')
const [agreeContract, setAgreeContract] = useState(false)
@ -29,12 +24,12 @@ export default function CompanySignContractPage() {
const [submitting, setSubmitting] = useState(false)
const [success, setSuccess] = useState(false)
const [error, setError] = useState('')
const [previewLoading, setPreviewLoading] = useState(false)
const [activeTab, setActiveTab] = useState<'contract' | 'gdpr'>('contract')
const [previewState, setPreviewState] = useState({
contract: { loading: false, html: null as string | null, error: null as string | null },
gdpr: { loading: false, html: null as string | null, error: null as string | null }
})
const [previewsReady, setPreviewsReady] = useState(false)
useEffect(() => {
setDate(new Date().toISOString().slice(0, 10))
@ -42,7 +37,6 @@ export default function CompanySignContractPage() {
const canvasRef = useRef<HTMLCanvasElement | null>(null)
const isDrawing = useRef(false)
const scaleRef = useRef(1)
const getPos = (e: React.MouseEvent<HTMLCanvasElement> | React.TouchEvent<HTMLCanvasElement>) => {
const canvas = canvasRef.current
@ -50,8 +44,9 @@ export default function CompanySignContractPage() {
const rect = canvas.getBoundingClientRect()
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY
const x = (clientX - rect.left) * scaleRef.current
const y = (clientY - rect.top) * scaleRef.current
// Coordinates should be in CSS pixels. The canvas context is already DPR-scaled via setTransform.
const x = clientX - rect.left
const y = clientY - rect.top
return { x, y }
}
@ -70,7 +65,6 @@ export default function CompanySignContractPage() {
ctx.lineCap = 'round'
ctx.strokeStyle = '#0f172a'
}
scaleRef.current = dpr
}
resize()
window.addEventListener('resize', resize)
@ -155,16 +149,18 @@ export default function CompanySignContractPage() {
// Load latest contract + GDPR previews for company user
useEffect(() => {
if (!accessToken) return
loadPreview('contract')
loadPreview('gdpr')
setPreviewLoading(true)
Promise.all([
loadPreview('contract'),
loadPreview('gdpr')
]).finally(() => setPreviewLoading(false))
}, [accessToken])
useEffect(() => {
const doneLoading = !previewState.contract.loading && !previewState.gdpr.loading
const doneLoading = !previewState.contract.loading && !previewState.gdpr.loading && !previewLoading
const anyAvailable = !!previewState.contract.html || !!previewState.gdpr.html
const blockingMsg = 'Temporarily unable to sign contracts. No active documents are available at this moment.'
if (doneLoading) {
setPreviewsReady(true)
// If one preview is missing, default to showing the available one
if (!previewState.contract.html && previewState.gdpr.html) {
setActiveTab('gdpr')
@ -180,13 +176,9 @@ export default function CompanySignContractPage() {
setError(blockingMsg)
}
}
}, [previewState])
}, [previewState, previewLoading])
const valid = () => {
const companyValid = companyName.trim().length >= 3
const repNameValid = repName.trim().length >= 3
const repTitleValid = repTitle.trim().length >= 2
const locationValid = location.trim().length >= 2
const contractAvailable = !!previewState.contract.html
const gdprAvailable = !!previewState.gdpr.html
@ -197,7 +189,7 @@ export default function CompanySignContractPage() {
const signatureDrawn = !!signatureDataUrl
const anyPreview = contractAvailable || gdprAvailable
return companyValid && repNameValid && repTitleValid && locationValid && contractChecked && dataChecked && signatureChecked && signatureDrawn && anyPreview
return contractChecked && dataChecked && signatureChecked && signatureDrawn && anyPreview
}
const handleSubmit = async (e: React.FormEvent) => {
@ -219,10 +211,6 @@ export default function CompanySignContractPage() {
return
}
if (companyName.trim().length < 3) issues.push('Company name (min 3 characters)')
if (repName.trim().length < 3) issues.push('Representative name (min 3 characters)')
if (repTitle.trim().length < 2) issues.push('Representative title (min 2 characters)')
if (location.trim().length < 2) issues.push('Location (min 2 characters)')
if (contractAvailable && !agreeContract) issues.push('Contract read and understood')
if (gdprAvailable && !agreeData) issues.push('Privacy policy accepted')
if (!confirmSignature) issues.push('Electronic signature confirmed')
@ -254,18 +242,8 @@ export default function CompanySignContractPage() {
try {
const contractData = {
companyName: companyName.trim(),
representativeName: repName.trim(),
representativeTitle: repTitle.trim(),
date,
location: location.trim(),
note: note.trim(),
contractType: 'company',
confirmations: {
agreeContract,
agreeData,
confirmSignature
}
}
// Create FormData for the existing backend endpoint
@ -483,7 +461,7 @@ export default function CompanySignContractPage() {
</button>
</div>
</div>
{previewState[activeTab].loading ? (
{previewLoading || previewState[activeTab].loading ? (
<div className="h-72 flex items-center justify-center text-xs text-gray-500">Loading preview</div>
) : previewState[activeTab].error ? (
<div className="h-72 flex items-center justify-center text-xs text-red-600 px-4 text-center">{previewState[activeTab].error}</div>
@ -499,71 +477,12 @@ export default function CompanySignContractPage() {
<hr className="my-10 border-gray-200" />
<section>
<h2 className="text-sm font-semibold text-[#0F2460] mb-5">Company & Representative</h2>
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
<div className="sm:col-span-2 lg:col-span-2">
<label className="block text-sm font-medium text-gray-700 mb-1">Company Name *</label>
<input
value={companyName}
onChange={e => { setCompanyName(e.target.value); setError('') }}
placeholder="Firmenname offiziell"
className="w-full rounded-lg border border-gray-300 px-4 py-2.5 text-sm focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Date *</label>
<input
type="date"
value={date}
onChange={e => setDate(e.target.value)}
className="w-full rounded-lg border border-gray-300 px-4 py-2.5 text-sm focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Location *</label>
<input
value={location}
onChange={e => { setLocation(e.target.value); setError('') }}
placeholder="z.B. München"
className="w-full rounded-lg border border-gray-300 px-4 py-2.5 text-sm focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
required
/>
</div>
<div className="sm:col-span-2 lg:col-span-1">
<label className="block text-sm font-medium text-gray-700 mb-1">Representative Name *</label>
<input
value={repName}
onChange={e => { setRepName(e.target.value); setError('') }}
placeholder="Vor- und Nachname"
className="w-full rounded-lg border border-gray-300 px-4 py-2.5 text-sm focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
required
/>
</div>
<div className="sm:col-span-2 lg:col-span-2">
<label className="block text-sm font-medium text-gray-700 mb-1">Representative Position / Title *</label>
<input
value={repTitle}
onChange={e => { setRepTitle(e.target.value); setError('') }}
placeholder="z.B. Geschäftsführer, Authorized Signatory"
className="w-full rounded-lg border border-gray-300 px-4 py-2.5 text-sm focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
required
/>
</div>
<div className="sm:col-span-2 lg:col-span-3">
<label className="block text-sm font-medium text-gray-700 mb-1">Note (optional)</label>
<input
value={note}
onChange={e => setNote(e.target.value)}
placeholder="Interne Referenz / Zusatz"
className="w-full rounded-lg border border-gray-300 px-4 py-2.5 text-sm focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
/>
</div>
</div>
<h2 className="text-sm font-semibold text-[#0F2460] mb-5">Signature</h2>
<div className="mt-8">
<label className="block text-sm font-medium text-gray-700 mb-2">Draw Signature *</label>
<div className="">
<label className="block text-sm font-medium text-gray-700 mb-2">
Draw Signature *
</label>
<div className="rounded-lg border border-gray-200 bg-white p-4">
<canvas
ref={canvasRef}

View File

@ -41,7 +41,6 @@ export default function PersonalSignContractPage() {
const canvasRef = useRef<HTMLCanvasElement | null>(null)
const isDrawing = useRef(false)
const scaleRef = useRef(1)
const getPos = (e: React.MouseEvent<HTMLCanvasElement> | React.TouchEvent<HTMLCanvasElement>) => {
const canvas = canvasRef.current
@ -49,8 +48,9 @@ export default function PersonalSignContractPage() {
const rect = canvas.getBoundingClientRect()
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY
const x = (clientX - rect.left) * scaleRef.current
const y = (clientY - rect.top) * scaleRef.current
// Coordinates should be in CSS pixels. The canvas context is already DPR-scaled via setTransform.
const x = clientX - rect.left
const y = clientY - rect.top
return { x, y }
}
@ -70,7 +70,6 @@ export default function PersonalSignContractPage() {
ctx.lineCap = 'round'
ctx.strokeStyle = '#0f172a'
}
scaleRef.current = dpr
}
resize()
window.addEventListener('resize', resize)