feat: remove unused state variables and improve signature drawing logic
This commit is contained in:
parent
0c8b00d007
commit
fa37a844e8
@ -16,11 +16,6 @@ export default function CompanySignContractPage() {
|
|||||||
const { userStatus, loading: statusLoading, refreshStatus } = useUserStatus()
|
const { userStatus, loading: statusLoading, refreshStatus } = useUserStatus()
|
||||||
const { showToast } = useToast()
|
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 [date, setDate] = useState('')
|
||||||
const [signatureDataUrl, setSignatureDataUrl] = useState('')
|
const [signatureDataUrl, setSignatureDataUrl] = useState('')
|
||||||
const [agreeContract, setAgreeContract] = useState(false)
|
const [agreeContract, setAgreeContract] = useState(false)
|
||||||
@ -29,12 +24,12 @@ export default function CompanySignContractPage() {
|
|||||||
const [submitting, setSubmitting] = useState(false)
|
const [submitting, setSubmitting] = useState(false)
|
||||||
const [success, setSuccess] = useState(false)
|
const [success, setSuccess] = useState(false)
|
||||||
const [error, setError] = useState('')
|
const [error, setError] = useState('')
|
||||||
|
const [previewLoading, setPreviewLoading] = useState(false)
|
||||||
const [activeTab, setActiveTab] = useState<'contract' | 'gdpr'>('contract')
|
const [activeTab, setActiveTab] = useState<'contract' | 'gdpr'>('contract')
|
||||||
const [previewState, setPreviewState] = useState({
|
const [previewState, setPreviewState] = useState({
|
||||||
contract: { loading: false, html: null as string | null, error: null as string | null },
|
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 }
|
gdpr: { loading: false, html: null as string | null, error: null as string | null }
|
||||||
})
|
})
|
||||||
const [previewsReady, setPreviewsReady] = useState(false)
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setDate(new Date().toISOString().slice(0, 10))
|
setDate(new Date().toISOString().slice(0, 10))
|
||||||
@ -42,7 +37,6 @@ export default function CompanySignContractPage() {
|
|||||||
|
|
||||||
const canvasRef = useRef<HTMLCanvasElement | null>(null)
|
const canvasRef = useRef<HTMLCanvasElement | null>(null)
|
||||||
const isDrawing = useRef(false)
|
const isDrawing = useRef(false)
|
||||||
const scaleRef = useRef(1)
|
|
||||||
|
|
||||||
const getPos = (e: React.MouseEvent<HTMLCanvasElement> | React.TouchEvent<HTMLCanvasElement>) => {
|
const getPos = (e: React.MouseEvent<HTMLCanvasElement> | React.TouchEvent<HTMLCanvasElement>) => {
|
||||||
const canvas = canvasRef.current
|
const canvas = canvasRef.current
|
||||||
@ -50,8 +44,9 @@ export default function CompanySignContractPage() {
|
|||||||
const rect = canvas.getBoundingClientRect()
|
const rect = canvas.getBoundingClientRect()
|
||||||
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX
|
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX
|
||||||
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY
|
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY
|
||||||
const x = (clientX - rect.left) * scaleRef.current
|
// Coordinates should be in CSS pixels. The canvas context is already DPR-scaled via setTransform.
|
||||||
const y = (clientY - rect.top) * scaleRef.current
|
const x = clientX - rect.left
|
||||||
|
const y = clientY - rect.top
|
||||||
return { x, y }
|
return { x, y }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +65,6 @@ export default function CompanySignContractPage() {
|
|||||||
ctx.lineCap = 'round'
|
ctx.lineCap = 'round'
|
||||||
ctx.strokeStyle = '#0f172a'
|
ctx.strokeStyle = '#0f172a'
|
||||||
}
|
}
|
||||||
scaleRef.current = dpr
|
|
||||||
}
|
}
|
||||||
resize()
|
resize()
|
||||||
window.addEventListener('resize', resize)
|
window.addEventListener('resize', resize)
|
||||||
@ -155,16 +149,18 @@ export default function CompanySignContractPage() {
|
|||||||
// Load latest contract + GDPR previews for company user
|
// Load latest contract + GDPR previews for company user
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!accessToken) return
|
if (!accessToken) return
|
||||||
loadPreview('contract')
|
setPreviewLoading(true)
|
||||||
|
Promise.all([
|
||||||
|
loadPreview('contract'),
|
||||||
loadPreview('gdpr')
|
loadPreview('gdpr')
|
||||||
|
]).finally(() => setPreviewLoading(false))
|
||||||
}, [accessToken])
|
}, [accessToken])
|
||||||
|
|
||||||
useEffect(() => {
|
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 anyAvailable = !!previewState.contract.html || !!previewState.gdpr.html
|
||||||
const blockingMsg = 'Temporarily unable to sign contracts. No active documents are available at this moment.'
|
const blockingMsg = 'Temporarily unable to sign contracts. No active documents are available at this moment.'
|
||||||
if (doneLoading) {
|
if (doneLoading) {
|
||||||
setPreviewsReady(true)
|
|
||||||
// If one preview is missing, default to showing the available one
|
// If one preview is missing, default to showing the available one
|
||||||
if (!previewState.contract.html && previewState.gdpr.html) {
|
if (!previewState.contract.html && previewState.gdpr.html) {
|
||||||
setActiveTab('gdpr')
|
setActiveTab('gdpr')
|
||||||
@ -180,13 +176,9 @@ export default function CompanySignContractPage() {
|
|||||||
setError(blockingMsg)
|
setError(blockingMsg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [previewState])
|
}, [previewState, previewLoading])
|
||||||
|
|
||||||
const valid = () => {
|
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 contractAvailable = !!previewState.contract.html
|
||||||
const gdprAvailable = !!previewState.gdpr.html
|
const gdprAvailable = !!previewState.gdpr.html
|
||||||
|
|
||||||
@ -197,7 +189,7 @@ export default function CompanySignContractPage() {
|
|||||||
const signatureDrawn = !!signatureDataUrl
|
const signatureDrawn = !!signatureDataUrl
|
||||||
|
|
||||||
const anyPreview = contractAvailable || gdprAvailable
|
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) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
@ -219,10 +211,6 @@ export default function CompanySignContractPage() {
|
|||||||
return
|
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 (contractAvailable && !agreeContract) issues.push('Contract read and understood')
|
||||||
if (gdprAvailable && !agreeData) issues.push('Privacy policy accepted')
|
if (gdprAvailable && !agreeData) issues.push('Privacy policy accepted')
|
||||||
if (!confirmSignature) issues.push('Electronic signature confirmed')
|
if (!confirmSignature) issues.push('Electronic signature confirmed')
|
||||||
@ -254,18 +242,8 @@ export default function CompanySignContractPage() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const contractData = {
|
const contractData = {
|
||||||
companyName: companyName.trim(),
|
|
||||||
representativeName: repName.trim(),
|
|
||||||
representativeTitle: repTitle.trim(),
|
|
||||||
date,
|
date,
|
||||||
location: location.trim(),
|
|
||||||
note: note.trim(),
|
|
||||||
contractType: 'company',
|
contractType: 'company',
|
||||||
confirmations: {
|
|
||||||
agreeContract,
|
|
||||||
agreeData,
|
|
||||||
confirmSignature
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create FormData for the existing backend endpoint
|
// Create FormData for the existing backend endpoint
|
||||||
@ -483,7 +461,7 @@ export default function CompanySignContractPage() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
<div className="h-72 flex items-center justify-center text-xs text-gray-500">Loading preview…</div>
|
||||||
) : previewState[activeTab].error ? (
|
) : 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>
|
<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" />
|
<hr className="my-10 border-gray-200" />
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-sm font-semibold text-[#0F2460] mb-5">Company & Representative</h2>
|
<h2 className="text-sm font-semibold text-[#0F2460] mb-5">Signature</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>
|
|
||||||
|
|
||||||
<div className="mt-8">
|
<div className="">
|
||||||
<label className="block text-sm font-medium text-gray-700 mb-2">Draw Signature *</label>
|
<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">
|
<div className="rounded-lg border border-gray-200 bg-white p-4">
|
||||||
<canvas
|
<canvas
|
||||||
ref={canvasRef}
|
ref={canvasRef}
|
||||||
|
|||||||
@ -41,7 +41,6 @@ export default function PersonalSignContractPage() {
|
|||||||
|
|
||||||
const canvasRef = useRef<HTMLCanvasElement | null>(null)
|
const canvasRef = useRef<HTMLCanvasElement | null>(null)
|
||||||
const isDrawing = useRef(false)
|
const isDrawing = useRef(false)
|
||||||
const scaleRef = useRef(1)
|
|
||||||
|
|
||||||
const getPos = (e: React.MouseEvent<HTMLCanvasElement> | React.TouchEvent<HTMLCanvasElement>) => {
|
const getPos = (e: React.MouseEvent<HTMLCanvasElement> | React.TouchEvent<HTMLCanvasElement>) => {
|
||||||
const canvas = canvasRef.current
|
const canvas = canvasRef.current
|
||||||
@ -49,8 +48,9 @@ export default function PersonalSignContractPage() {
|
|||||||
const rect = canvas.getBoundingClientRect()
|
const rect = canvas.getBoundingClientRect()
|
||||||
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX
|
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX
|
||||||
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY
|
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY
|
||||||
const x = (clientX - rect.left) * scaleRef.current
|
// Coordinates should be in CSS pixels. The canvas context is already DPR-scaled via setTransform.
|
||||||
const y = (clientY - rect.top) * scaleRef.current
|
const x = clientX - rect.left
|
||||||
|
const y = clientY - rect.top
|
||||||
return { x, y }
|
return { x, y }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +70,6 @@ export default function PersonalSignContractPage() {
|
|||||||
ctx.lineCap = 'round'
|
ctx.lineCap = 'round'
|
||||||
ctx.strokeStyle = '#0f172a'
|
ctx.strokeStyle = '#0f172a'
|
||||||
}
|
}
|
||||||
scaleRef.current = dpr
|
|
||||||
}
|
}
|
||||||
resize()
|
resize()
|
||||||
window.addEventListener('resize', resize)
|
window.addEventListener('resize', resize)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user