166 lines
4.6 KiB
TypeScript
166 lines
4.6 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useRef, useCallback, useEffect } from 'react'
|
|
import useAuthStore from '../../../../store/authStore'
|
|
import { useUserStatus } from '../../../../hooks/useUserStatus'
|
|
|
|
export function usePersonalUploadId() {
|
|
// Auth and status
|
|
const token = useAuthStore(s => s.accessToken)
|
|
const { refreshStatus } = useUserStatus()
|
|
|
|
// Form state
|
|
const [idNumber, setIdNumber] = useState('')
|
|
const [idType, setIdType] = useState('')
|
|
const [expiry, setExpiry] = useState('')
|
|
const [hasBack, setHasBack] = useState(true)
|
|
|
|
// Files + previews
|
|
const [frontFile, setFrontFile] = useState<File | null>(null)
|
|
const [backFile, setBackFile] = useState<File | null>(null)
|
|
const [frontPreview, setFrontPreview] = useState<string | null>(null)
|
|
const [backPreview, setBackPreview] = useState<string | null>(null)
|
|
|
|
// UI state
|
|
const [submitting, setSubmitting] = useState(false)
|
|
const [error, setError] = useState('')
|
|
const [success, setSuccess] = useState(false)
|
|
|
|
// Refs
|
|
const frontInputRef = useRef<HTMLInputElement | null>(null)
|
|
const backInputRef = useRef<HTMLInputElement | null>(null)
|
|
|
|
// Shared input style
|
|
const inputBase =
|
|
'w-full h-11 rounded-lg border border-gray-300 px-4 text-sm text-gray-900 placeholder:text-gray-900 placeholder:opacity-100 focus:ring-2 focus:ring-indigo-500 focus:border-transparent bg-white'
|
|
|
|
// File handlers
|
|
const handleFile = (f: File, side: 'front' | 'back') => {
|
|
if (f.size > 10 * 1024 * 1024) {
|
|
setError('File size exceeds 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 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()
|
|
}, [])
|
|
|
|
// Previews
|
|
useEffect(() => {
|
|
if (!frontFile) { setFrontPreview(null); return }
|
|
const url = URL.createObjectURL(frontFile)
|
|
setFrontPreview(url)
|
|
return () => URL.revokeObjectURL(url)
|
|
}, [frontFile])
|
|
|
|
useEffect(() => {
|
|
if (!backFile) { setBackPreview(null); return }
|
|
const url = URL.createObjectURL(backFile)
|
|
setBackPreview(url)
|
|
return () => URL.revokeObjectURL(url)
|
|
}, [backFile])
|
|
|
|
// Validation
|
|
const validate = () => {
|
|
if (!idNumber.trim() || !idType || !expiry) {
|
|
setError('Please fill out all required fields.')
|
|
return false
|
|
}
|
|
if (!frontFile) {
|
|
setError('Please upload the front side.')
|
|
return false
|
|
}
|
|
if (hasBack && !backFile) {
|
|
setError('Please upload the back side or disable the switch.')
|
|
return false
|
|
}
|
|
setError('')
|
|
return true
|
|
}
|
|
|
|
// Submit
|
|
const submit = async (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
if (!validate()) return
|
|
if (!token) {
|
|
setError('Not authenticated. Please log in again.')
|
|
return
|
|
}
|
|
|
|
setSubmitting(true)
|
|
setError('')
|
|
|
|
try {
|
|
const formData = new FormData()
|
|
if (frontFile) formData.append('front', frontFile)
|
|
if (hasBack && backFile) formData.append('back', backFile)
|
|
formData.append('idType', idType)
|
|
formData.append('idNumber', idNumber)
|
|
formData.append('expiryDate', expiry)
|
|
|
|
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/api/upload/personal-id`, {
|
|
method: 'POST',
|
|
headers: { 'Authorization': `Bearer ${token}` },
|
|
body: formData
|
|
})
|
|
|
|
const data = await response.json().catch(() => ({}))
|
|
|
|
if (response.ok && data.success) {
|
|
setSuccess(true)
|
|
await refreshStatus()
|
|
setTimeout(() => {
|
|
// Check if we came from tutorial
|
|
const urlParams = new URLSearchParams(window.location.search)
|
|
const fromTutorial = urlParams.get('tutorial') === 'true'
|
|
|
|
if (fromTutorial) {
|
|
window.location.href = '/quickaction-dashboard?tutorial=true'
|
|
} else {
|
|
window.location.href = '/quickaction-dashboard'
|
|
}
|
|
}, 2000)
|
|
} else {
|
|
setError(data.message || 'Upload failed. Please try again.')
|
|
}
|
|
} catch (err) {
|
|
console.error('Upload error:', err)
|
|
setError('Network error. Please try again.')
|
|
} finally {
|
|
setSubmitting(false)
|
|
}
|
|
}
|
|
|
|
return {
|
|
// values
|
|
idNumber, idType, expiry, hasBack,
|
|
frontFile, backFile,
|
|
frontPreview, backPreview,
|
|
submitting, error, success,
|
|
frontInputRef, backInputRef,
|
|
inputBase,
|
|
// setters
|
|
setIdNumber, setIdType, setExpiry, setHasBack,
|
|
// handlers
|
|
handleFile, onDrop, clearFile, dropEvents, openPicker, submit,
|
|
}
|
|
}
|