191 lines
5.4 KiB
TypeScript
191 lines
5.4 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useRef, useEffect, useCallback } from 'react'
|
|
import useAuthStore from '../../../../store/authStore'
|
|
import { useUserStatus } from '../../../../hooks/useUserStatus'
|
|
import { useToast } from '../../../../components/toast/toastComponent'
|
|
|
|
export function useCompanyUploadId() {
|
|
// Auth + status
|
|
const { accessToken } = useAuthStore()
|
|
const { refreshStatus } = useUserStatus()
|
|
const { showToast } = useToast()
|
|
|
|
// Form state
|
|
const [idNumber, setIdNumber] = useState('')
|
|
const [idType, setIdType] = useState('')
|
|
const [expiryDate, setExpiryDate] = useState('')
|
|
const [hasBack, setHasBack] = useState(true)
|
|
|
|
// Files + previews
|
|
const [frontFile, setFrontFile] = useState<File | null>(null)
|
|
const [extraFile, setExtraFile] = useState<File | null>(null)
|
|
const [frontPreview, setFrontPreview] = useState<string | null>(null)
|
|
const [extraPreview, setExtraPreview] = useState<string | null>(null)
|
|
|
|
// UI state
|
|
const [submitting, setSubmitting] = useState(false)
|
|
const [error, setError] = useState('')
|
|
const [success, setSuccess] = useState(false)
|
|
|
|
// Refs
|
|
const frontRef = useRef<HTMLInputElement | null>(null)
|
|
const extraRef = useRef<HTMLInputElement | null>(null)
|
|
|
|
// Unified 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 = (file: File, which: 'front' | 'extra') => {
|
|
if (file.size > 10 * 1024 * 1024) {
|
|
const msg = 'File size exceeds 10 MB.'
|
|
setError(msg)
|
|
showToast({
|
|
variant: 'error',
|
|
title: 'File too large',
|
|
message: msg,
|
|
})
|
|
return
|
|
}
|
|
setError('')
|
|
which === 'front' ? setFrontFile(file) : setExtraFile(file)
|
|
}
|
|
|
|
const onDrop = (e: React.DragEvent, which: 'front' | 'extra') => {
|
|
e.preventDefault()
|
|
const f = e.dataTransfer.files?.[0]
|
|
if (f) handleFile(f, which)
|
|
}
|
|
|
|
const clearFile = (which: 'front' | 'extra') => {
|
|
which === 'front' ? setFrontFile(null) : setExtraFile(null)
|
|
}
|
|
|
|
const dropHandlers = {
|
|
onDragOver: (e: React.DragEvent) => e.preventDefault(),
|
|
onDragEnter: (e: React.DragEvent) => e.preventDefault(),
|
|
}
|
|
|
|
const openPicker = useCallback((which: 'front' | 'extra') => {
|
|
(which === 'front' ? frontRef.current : extraRef.current)?.click()
|
|
}, [])
|
|
|
|
// Previews (images only)
|
|
useEffect(() => {
|
|
if (!frontFile || !frontFile.type?.startsWith('image/')) { setFrontPreview(null); return }
|
|
const url = URL.createObjectURL(frontFile)
|
|
setFrontPreview(url)
|
|
return () => URL.revokeObjectURL(url)
|
|
}, [frontFile])
|
|
|
|
useEffect(() => {
|
|
if (!extraFile || !extraFile.type?.startsWith('image/')) { setExtraPreview(null); return }
|
|
const url = URL.createObjectURL(extraFile)
|
|
setExtraPreview(url)
|
|
return () => URL.revokeObjectURL(url)
|
|
}, [extraFile])
|
|
|
|
// Validation
|
|
const validate = () => {
|
|
if (!idNumber.trim() || !idType || !expiryDate || !frontFile) {
|
|
const msg = 'Please complete all required fields (marked with *).'
|
|
setError(msg)
|
|
showToast({
|
|
variant: 'error',
|
|
title: 'Missing information',
|
|
message: msg,
|
|
})
|
|
return false
|
|
}
|
|
setError('')
|
|
return true
|
|
}
|
|
|
|
// Submit
|
|
const submit = async (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
if (!validate()) return
|
|
if (!accessToken) {
|
|
const msg = 'Not authenticated. Please log in again.'
|
|
setError(msg)
|
|
showToast({
|
|
variant: 'error',
|
|
title: 'Authentication error',
|
|
message: msg,
|
|
})
|
|
return
|
|
}
|
|
|
|
setSubmitting(true)
|
|
setError('')
|
|
|
|
try {
|
|
const formData = new FormData()
|
|
if (frontFile) formData.append('front', frontFile)
|
|
if (hasBack && extraFile) formData.append('back', extraFile)
|
|
formData.append('idType', idType)
|
|
formData.append('idNumber', idNumber.trim())
|
|
formData.append('expiryDate', expiryDate)
|
|
|
|
const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/api/upload/company-id`, {
|
|
method: 'POST',
|
|
headers: { 'Authorization': `Bearer ${accessToken}` },
|
|
body: formData,
|
|
})
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json().catch(() => ({ message: 'Upload failed' }))
|
|
const msg = errorData.message || 'Upload failed'
|
|
throw new Error(msg)
|
|
}
|
|
|
|
setSuccess(true)
|
|
showToast({
|
|
variant: 'success',
|
|
title: 'Documents uploaded',
|
|
message: 'Your company ID documents have been uploaded successfully.',
|
|
})
|
|
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 {
|
|
// keep same redirect as page used before
|
|
window.location.href = '/quickaction-dashboard/register-additional-information'
|
|
}
|
|
}, 1500)
|
|
} catch (err: any) {
|
|
console.error('Company ID upload error:', err)
|
|
const msg = err?.message || 'Upload failed.'
|
|
setError(msg)
|
|
showToast({
|
|
variant: 'error',
|
|
title: 'Upload failed',
|
|
message: msg,
|
|
})
|
|
} finally {
|
|
setSubmitting(false)
|
|
}
|
|
}
|
|
|
|
return {
|
|
// values
|
|
idNumber, idType, expiryDate, hasBack,
|
|
frontFile, extraFile,
|
|
frontPreview, extraPreview,
|
|
submitting, error, success,
|
|
frontRef, extraRef,
|
|
inputBase,
|
|
// setters
|
|
setIdNumber, setIdType, setExpiryDate, setHasBack, setExtraFile,
|
|
// handlers
|
|
handleFile, onDrop, clearFile, dropHandlers, openPicker, submit,
|
|
}
|
|
}
|