122 lines
3.1 KiB
TypeScript
122 lines
3.1 KiB
TypeScript
import { Button } from '@headlessui/react'
|
|
import { zodResolver } from '@hookform/resolvers/zod'
|
|
import { useEffect, useState, type ChangeEvent } from 'react'
|
|
import { useFetcher } from 'react-router'
|
|
import { RemixFormProvider, useRemixForm } from 'remix-hook-form'
|
|
import { z } from 'zod'
|
|
|
|
import { uploadCategorySchema, useAdminContext } from '~/contexts/admin'
|
|
|
|
export const uploadSchema = z.object({
|
|
file: z.instanceof(File),
|
|
category: uploadCategorySchema,
|
|
})
|
|
|
|
export type TUploadSchema = z.infer<typeof uploadSchema>
|
|
|
|
export const FormUpload = () => {
|
|
const { isUploadOpen, setUploadedFile } = useAdminContext()
|
|
const fetcher = useFetcher()
|
|
const [disabled, setDisabled] = useState(false)
|
|
const [error, setError] = useState<string>()
|
|
const maxFileSize = 10 * 1024 // 10MB
|
|
|
|
const formMethods = useRemixForm<TUploadSchema>({
|
|
mode: 'onSubmit',
|
|
fetcher,
|
|
resolver: zodResolver(uploadSchema),
|
|
})
|
|
|
|
const { handleSubmit, register, setValue } = formMethods
|
|
|
|
useEffect(() => {
|
|
if (!fetcher.data?.success) {
|
|
setError(fetcher.data?.message)
|
|
setDisabled(false)
|
|
return
|
|
}
|
|
|
|
setUploadedFile(fetcher.data.uploadData.data.file_url)
|
|
|
|
setDisabled(true)
|
|
setError(undefined)
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [fetcher])
|
|
|
|
const handleChange = async function (event: ChangeEvent<HTMLInputElement>) {
|
|
event.preventDefault()
|
|
if (event.target.files && event.target.files[0]) {
|
|
const files: File[] = [...event.target.files]
|
|
|
|
onChange(files, event)
|
|
}
|
|
}
|
|
|
|
const onChange = async function (
|
|
files: File[],
|
|
event: ChangeEvent<HTMLInputElement>,
|
|
) {
|
|
const file = files[0]
|
|
const img = new Image()
|
|
|
|
if (!file.type.startsWith('image/')) {
|
|
setError('Please upload an image file.')
|
|
return
|
|
}
|
|
|
|
if (file.size > maxFileSize * 1024) {
|
|
setError(`File size is too big!`)
|
|
return
|
|
}
|
|
|
|
img.addEventListener('load', () => {
|
|
handleFiles(event)
|
|
})
|
|
|
|
img.src = URL.createObjectURL(file)
|
|
}
|
|
|
|
const handleFiles = (event: ChangeEvent<HTMLInputElement>) => {
|
|
const files = event.target.files
|
|
if (files && files.length > 0) {
|
|
const file = files[0]
|
|
setValue('file', file)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<RemixFormProvider {...formMethods}>
|
|
<fetcher.Form
|
|
method="post"
|
|
onSubmit={handleSubmit}
|
|
className="space-y-4"
|
|
action="/actions/admin/upload"
|
|
encType="multipart/form-data"
|
|
>
|
|
{error && (
|
|
<div className="text-sm text-red-500 capitalize">{error}</div>
|
|
)}
|
|
<input
|
|
type="file"
|
|
id="input-file-upload"
|
|
accept="image/*"
|
|
onChange={handleChange}
|
|
/>
|
|
<input
|
|
type="hidden"
|
|
id="input-file-upload-type"
|
|
value={isUploadOpen}
|
|
{...register('category')}
|
|
/>
|
|
<Button
|
|
disabled={disabled}
|
|
type="submit"
|
|
className="w-full rounded-md bg-[#2E2F7C] py-2 text-white transition hover:bg-blue-800"
|
|
>
|
|
Upload
|
|
</Button>
|
|
</fetcher.Form>
|
|
</RemixFormProvider>
|
|
)
|
|
}
|