Merge pull request 'dev' (#19) from dev into main

Reviewed-on: #19
This commit is contained in:
Seazn 2026-03-20 15:29:08 +00:00
commit f4c4433925
3 changed files with 39 additions and 10 deletions

View File

@ -6,9 +6,11 @@ type Props = {
value: string
onChange: (dataUrl: string) => void
className?: string
required?: boolean
error?: string | null
}
export default function SignaturePad({ value, onChange, className }: Props) {
export default function SignaturePad({ value, onChange, className, required = false, error = null }: Props) {
const canvasRef = useRef<HTMLCanvasElement | null>(null)
const isDrawing = useRef(false)
@ -137,7 +139,9 @@ export default function SignaturePad({ value, onChange, className }: Props) {
return (
<div className={className}>
<div className="flex items-center justify-between gap-2 mb-2">
<p className="text-sm font-medium text-gray-900">Signature</p>
<p className="text-sm font-medium text-gray-900">
Signature{required ? ' *' : ''}
</p>
<button
type="button"
onClick={clear}
@ -146,7 +150,7 @@ export default function SignaturePad({ value, onChange, className }: Props) {
Clear
</button>
</div>
<div className="rounded-lg border border-gray-300 bg-white">
<div className={`rounded-lg border bg-white ${error ? 'border-red-400 ring-1 ring-red-200' : 'border-gray-300'}`}>
<canvas
ref={canvasRef}
className="block w-full h-36 touch-none"
@ -159,8 +163,8 @@ export default function SignaturePad({ value, onChange, className }: Props) {
onTouchEnd={endDrawing}
/>
</div>
<p className="mt-2 text-xs text-gray-500">
{value ? 'Signature captured.' : 'Draw your signature in the box.'}
<p className={`mt-2 text-xs ${error ? 'text-red-700' : 'text-gray-500'}`}>
{error || (value ? 'Signature captured.' : 'Draw your signature in the box.')}
</p>
</div>
)

View File

@ -70,6 +70,14 @@ export async function subscribeAbo(input: SubscribeAboInput) {
throw new Error(`Missing required fields: ${missing.join(', ')}`)
}
if (typeof input.signingCity !== 'string' || input.signingCity.trim() === '') {
throw new Error('signingCity is required')
}
if (typeof input.signatureDataUrl !== 'string' || input.signatureDataUrl.trim() === '') {
throw new Error('signatureDataUrl is required')
}
const body: any = {
billing_interval: input.billing_interval ?? 'month',
interval_count: input.interval_count ?? 1,

View File

@ -510,12 +510,16 @@ export default function SummaryPage() {
const hasRequiredSelfFields = requiredSelfFields.every(k => String(form[k]).trim() !== '')
const hasRequiredInvoiceFields = form.invoiceSameAsShipping || form.invoiceEmail.trim() !== ''
const hasSigningCity = form.signingCity.trim() !== ''
const hasSignature = signatureDataUrl.trim() !== ''
const canSubmit =
selectedEntries.length > 0 &&
totalPacks === requiredPacks &&
hasRequiredSelfFields &&
hasRequiredInvoiceFields;
hasRequiredInvoiceFields &&
hasSigningCity &&
hasSignature;
const backToSelection = () => router.push('/coffee-abonnements');
@ -527,6 +531,16 @@ export default function SummaryPage() {
return
}
if (!hasSigningCity) {
setSubmitError('Signing city is required.')
return
}
if (!hasSignature) {
setSubmitError('Signature is required.')
return
}
setSubmitError(null)
setSubmitLoading(true)
try {
@ -781,10 +795,13 @@ export default function SummaryPage() {
<div className="mt-4 space-y-3">
<div>
<label className="block text-sm font-medium mb-1">Ort (Signing City)</label>
<input type="text" name="signingCity" value={form.signingCity} onChange={handleInput} className="w-full max-w-xs rounded border px-3 py-2 bg-white border-gray-300 focus:outline-none focus:ring-2 focus:ring-[#1C2B4A]" placeholder="z.B. Wien" />
<label className="block text-sm font-medium mb-1">Ort (Signing City) *</label>
<input type="text" name="signingCity" value={form.signingCity} onChange={handleInput} className={`w-full max-w-xs rounded border px-3 py-2 bg-white focus:outline-none focus:ring-2 focus:ring-[#1C2B4A] ${!hasSigningCity && submitError ? 'border-red-400' : 'border-gray-300'}`} placeholder="z.B. Wien" />
{!hasSigningCity && submitError && (
<p className="mt-1 text-xs text-red-700">Ort ist erforderlich.</p>
)}
</div>
<SignaturePad value={signatureDataUrl} onChange={setSignatureDataUrl} />
<SignaturePad value={signatureDataUrl} onChange={setSignatureDataUrl} required error={!hasSignature && submitError ? 'Signature is required.' : null} />
</div>
</div>
@ -838,7 +855,7 @@ export default function SummaryPage() {
</button>
{!canSubmit && (
<p className="text-xs text-gray-500 mt-2">
Please select coffees and fill all required buyer fields.
Please select coffees and fill all required buyer fields, signing city, and signature.
</p>
)}
</div>