(null)
const { accept, maxSize, storagePath } = getUploadConfig(field)
const endpoint = field.searchEndpoint || DEFAULT_UPLOAD_ENDPOINT
const hasValue = typeof value === 'string' && value !== ''
const handleFile = useCallback(
async (file: File | null) => {
if (!file) {
setError(null)
onChange('')
return
}
setError(null)
if (maxSize && file.size > maxSize) {
const mb = (maxSize / (1024 * 1024)).toFixed(1)
setError(
t('common.upload.too_large', {
mb,
defaultValue: `File too large (max {{mb}} MB).`,
}),
)
return
}
const form = new FormData()
form.append('file', file)
// The destination subfolder, declared per field via the manifest
// (`storage_path`). Sent under BOTH keys: `folder` is what the ops
// host reads (`c.FormValue("folder")`), `storage_path` is the
// canonical SDK name kept for hosts that read it.
if (storagePath) {
form.append('folder', storagePath)
form.append('storage_path', storagePath)
}
setUploading(true)
try {
const res = await api.post(endpoint, form, {
headers: { 'Content-Type': 'multipart/form-data' },
})
const body = (res as { data?: any })?.data
if (body && body.success === false) {
setError(
body.message ||
t('common.upload.failed', { defaultValue: 'Could not upload the file.' }),
)
return
}
const stored = extractUploadedValue(body)
if (!stored) {
setError(
t('common.upload.invalid', {
defaultValue: 'Invalid upload response.',
}),
)
return
}
onChange(stored)
} catch (err: any) {
setError(
err?.response?.data?.message ||
t('common.upload.failed', { defaultValue: 'Could not upload the file.' }),
)
} finally {
setUploading(false)
}
},
[api, endpoint, maxSize, storagePath, onChange, t],
)
return (
void handleFile(f)}
pickLabel={field.placeholder || undefined}
/>
{error && (
{error}
)}
)
}