From b14c72cb8d46b7df52b569c32fcc5763ff4c9408 Mon Sep 17 00:00:00 2001 From: DeathKaioken Date: Sat, 4 Oct 2025 00:31:09 +0200 Subject: [PATCH] feature: add personal & company register upload id page --- .../register-upload-id/company/page.tsx | 270 +++++++++++++++ .../register-upload-id/personal/page.tsx | 325 ++++++++++++++++++ 2 files changed, 595 insertions(+) create mode 100644 src/app/quickaction-dashboard/register-upload-id/company/page.tsx create mode 100644 src/app/quickaction-dashboard/register-upload-id/personal/page.tsx diff --git a/src/app/quickaction-dashboard/register-upload-id/company/page.tsx b/src/app/quickaction-dashboard/register-upload-id/company/page.tsx new file mode 100644 index 0000000..6984bb7 --- /dev/null +++ b/src/app/quickaction-dashboard/register-upload-id/company/page.tsx @@ -0,0 +1,270 @@ +'use client' + +import { useState, useRef } from 'react' +import PageLayout from '../../../components/PageLayout' +import { DocumentArrowUpIcon, XMarkIcon } from '@heroicons/react/24/outline' + +const DOC_TYPES = ['Handelsregisterauszug', 'Gewerbeanmeldung', 'Steuerbescheid', 'Sonstiges'] + +export default function CompanyIdUploadPage() { + const [docNumber, setDocNumber] = useState('') + const [docType, setDocType] = useState('') + const [issueDate, setIssueDate] = useState('') + const [frontFile, setFrontFile] = useState(null) + const [extraFile, setExtraFile] = useState(null) + const [submitting, setSubmitting] = useState(false) + const [error, setError] = useState('') + const [success, setSuccess] = useState(false) + + const frontRef = useRef(null) + const extraRef = useRef(null) + + const handleFile = (file: File, which: 'front' | 'extra') => { + if (file.size > 10 * 1024 * 1024) { + setError('Datei größer als 10 MB.') + return + } + setError('') + which === 'front' ? setFrontFile(file) : setExtraFile(file) + } + + const dropHandlers = { + onDragOver: (e: React.DragEvent) => e.preventDefault(), + onDragEnter: (e: React.DragEvent) => e.preventDefault() + } + + const submit = async (e: React.FormEvent) => { + e.preventDefault() + if (!docNumber.trim() || !docType || !issueDate || !frontFile) { + setError('Bitte alle Pflichtfelder (mit *) ausfüllen.') + return + } + setError('') + setSubmitting(true) + try { + // TODO: API upload + await new Promise(r => setTimeout(r, 1200)) + setSuccess(true) + } catch { + setError('Upload fehlgeschlagen.') + } finally { + setSubmitting(false) + } + } + + return ( + +
+ {/* Background (same as personal) */} +
+
+ + + +
+
+

+ Company Document Verification +

+

+ Upload a valid company registration or compliance document +

+ +
+
+ + setDocNumber(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" + placeholder="Enter reference number" + required + /> +
+ +
+ + +
+ +
+ + setIssueDate(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 + /> +
+ +
+
+ + {/* Upload Areas */} +
+
{ + e.preventDefault() + const f = e.dataTransfer.files?.[0] + if (f) handleFile(f, 'front') + }} + onClick={() => frontRef.current?.click()} + className="group flex flex-col items-center justify-center rounded-lg border-2 border-dashed border-gray-300 bg-gray-50/60 px-4 py-10 text-center hover:border-indigo-400 hover:bg-indigo-50/40 cursor-pointer transition" + > + { + const f = e.target.files?.[0] + if (f) handleFile(f, 'front') + }} + /> + {frontFile ? ( +
+ +

{frontFile.name}

+ +
+ ) : ( + <> + +

+ Upload primary document * +

+

+ PNG, JPG, PDF up to 10MB +

+ + )} +
+ +
{ + e.preventDefault() + const f = e.dataTransfer.files?.[0] + if (f) handleFile(f, 'extra') + }} + onClick={() => extraRef.current?.click()} + className="group flex flex-col items-center justify-center rounded-lg border-2 border-dashed border-gray-300 bg-gray-50/60 px-4 py-10 text-center hover:border-indigo-400 hover:bg-indigo-50/40 cursor-pointer transition" + > + { + const f = e.target.files?.[0] + if (f) handleFile(f, 'extra') + }} + /> + {extraFile ? ( +
+ +

{extraFile.name}

+ +
+ ) : ( + <> + +

+ Optional supporting file +

+

+ (Invoice, license, certificate…) +

+ + )} +
+ + {extraFile &&
+ Extra space for clarity +
} +
+ + {/* Info */} +
+

+ Guidelines: +

+
    +
  • Document must be valid & readable
  • +
  • Complete page (no cropping)
  • +
  • Good lighting (no glare)
  • +
  • PDF or clear image
  • +
  • Supplementary evidence speeds review
  • +
+
+ + {error && ( +
+ {error} +
+ )} + {success && ( +
+ Dokumente erfolgreich hochgeladen. +
+ )} + +
+ +
+
+
+
+ + ) +} diff --git a/src/app/quickaction-dashboard/register-upload-id/personal/page.tsx b/src/app/quickaction-dashboard/register-upload-id/personal/page.tsx new file mode 100644 index 0000000..59a8439 --- /dev/null +++ b/src/app/quickaction-dashboard/register-upload-id/personal/page.tsx @@ -0,0 +1,325 @@ +'use client' + +import { useState, useRef, useCallback } from 'react' +import PageLayout from '../../../components/PageLayout' +import { + DocumentArrowUpIcon, + XMarkIcon +} from '@heroicons/react/24/outline' + +const ID_TYPES = ['Personalausweis', 'Reisepass', 'Führerschein', 'Aufenthaltstitel'] + +export default function PersonalIdUploadPage() { + const [idNumber, setIdNumber] = useState('') + const [idType, setIdType] = useState('') + const [expiry, setExpiry] = useState('') + const [hasBack, setHasBack] = useState(true) + const [frontFile, setFrontFile] = useState(null) + const [backFile, setBackFile] = useState(null) + const [submitting, setSubmitting] = useState(false) + const [error, setError] = useState('') + const [success, setSuccess] = useState(false) + + const frontInputRef = useRef(null) + const backInputRef = useRef(null) + + const handleFile = (f: File, side: 'front' | 'back') => { + if (f.size > 10 * 1024 * 1024) { + setError('Datei größer als 10 MB.') + return + } + setError('') + side === 'front' ? setFrontFile(f) : setBackFile(f) + } + + const onDrop = (e: React.DragEvent, side: 'front' | 'back') => { + e.preventDefault() + const f = e.dataTransfer.files?.[0] + if (f) handleFile(f, side) + } + + const validate = () => { + if (!idNumber.trim() || !idType || !expiry) { + setError('Bitte alle Pflichtfelder ausfüllen.') + return false + } + if (!frontFile) { + setError('Vorderseite hochladen.') + return false + } + if (hasBack && !backFile) { + setError('Rückseite hochladen oder Schalter deaktivieren.') + return false + } + setError('') + return true + } + + const submit = async (e: React.FormEvent) => { + e.preventDefault() + if (!validate()) return + setSubmitting(true) + try { + // TODO: Upload logic (multipart/form-data) + await new Promise(r => setTimeout(r, 1200)) + setSuccess(true) + } catch { + setError('Upload fehlgeschlagen. Bitte erneut versuchen.') + } finally { + setSubmitting(false) + } + } + + const clearFile = (side: 'front' | 'back') => { + side === 'front' ? setFrontFile(null) : setBackFile(null) + } + + const dropEvents = { + onDragOver: (e: React.DragEvent) => e.preventDefault(), + onDragEnter: (e: React.DragEvent) => e.preventDefault() + } + + const openPicker = useCallback((side: 'front' | 'back') => { + (side === 'front' ? frontInputRef.current : backInputRef.current)?.click() + }, []) + + return ( + +
+ {/* Background */} +
+
+ + + +
+
+

+ Personal Identity Verification +

+

+ Please upload clear photos of both sides of your government‑issued ID +

+ + {/* Grid Fields */} +
+
+ + setIdNumber(e.target.value)} + placeholder="Enter your ID number" + 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 + /> +

+ Enter the number exactly as shown on your ID +

+
+ +
+ + +
+ +
+ + setExpiry(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 + /> +
+
+
+ + {/* Back side toggle */} +
+ + Does ID have a Backside? + + + {hasBack ? 'Yes' : 'No'} +
+ + {/* Upload Areas */} +
+ {/* Front */} +
onDrop(e, 'front')} + onClick={() => openPicker('front')} + className="group relative flex flex-col items-center justify-center rounded-lg border-2 border-dashed border-gray-300 bg-gray-50/60 px-4 py-10 text-center hover:border-indigo-400 hover:bg-indigo-50/40 cursor-pointer transition" + > + { + const f = e.target.files?.[0] + if (f) handleFile(f, 'front') + }} + /> + {frontFile ? ( +
+ +

{frontFile.name}

+ +
+ ) : ( + <> + +

+ Click to upload front side +

+

+ or drag and drop +
+ PNG, JPG, JPEG up to 10MB +

+ + )} +
+ + {/* Back */} + {hasBack && ( +
onDrop(e, 'back')} + onClick={() => openPicker('back')} + className="group relative flex flex-col items-center justify-center rounded-lg border-2 border-dashed border-gray-300 bg-gray-50/60 px-4 py-10 text-center hover:border-indigo-400 hover:bg-indigo-50/40 cursor-pointer transition" + > + { + const f = e.target.files?.[0] + if (f) handleFile(f, 'back') + }} + /> + {backFile ? ( +
+ +

{backFile.name}

+ +
+ ) : ( + <> + +

+ Click to upload back side +

+

+ or drag and drop +
+ PNG, JPG, JPEG up to 10MB +

+ + )} +
+ )} + {hasBack &&
+ Extra space for clarity +
} +
+ + {/* Info Box */} +
+

+ Please ensure your ID documents: +

+
    +
  • Are clearly visible and readable
  • +
  • Show all four corners
  • +
  • Are not expired
  • +
  • Have good lighting (no shadows or glare)
  • +
  • {hasBack ? 'Both front and back sides are uploaded' : 'Front side is uploaded'}
  • +
+
+ + {error && ( +
+ {error} +
+ )} + {success && ( +
+ Upload erfolgreich gespeichert. +
+ )} + +
+ +
+
+
+
+ + ) +}