diff --git a/app/apis/admin/upload-file.ts b/app/apis/admin/upload-file.ts index e94f376..f96471a 100644 --- a/app/apis/admin/upload-file.ts +++ b/app/apis/admin/upload-file.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import type { TUploadSchema } from '~/layouts/admin/form-upload' +import type { TUploadSchema } from '~/layouts/admin/dialog-upload' import { HttpServer, type THttpServer } from '~/libs/http-server' const uploadResponseSchema = z.object({ diff --git a/app/layouts/admin/dashboard.tsx b/app/layouts/admin/dashboard.tsx index 642918b..8c9c3a4 100644 --- a/app/layouts/admin/dashboard.tsx +++ b/app/layouts/admin/dashboard.tsx @@ -1,15 +1,11 @@ -import { Dialog, DialogBackdrop, DialogPanel } from '@headlessui/react' import type { PropsWithChildren } from 'react' -import { useAdminContext } from '~/contexts/admin' - -import { FormUpload } from './form-upload' +import { DialogUpload } from './dialog-upload' import { Navbar } from './navbar' import { Sidebar } from './sidebar' export const AdminDashboardLayout = (properties: PropsWithChildren) => { const { children } = properties - const { isUploadOpen, setIsUploadOpen } = useAdminContext() return (
@@ -18,27 +14,7 @@ export const AdminDashboardLayout = (properties: PropsWithChildren) => {
{children}
- { - setIsUploadOpen(undefined) - }} - className="relative z-50" - transition - > - -
- - - -
-
+ ) } diff --git a/app/layouts/admin/dialog-upload.tsx b/app/layouts/admin/dialog-upload.tsx new file mode 100644 index 0000000..2ff72c0 --- /dev/null +++ b/app/layouts/admin/dialog-upload.tsx @@ -0,0 +1,143 @@ +import { Dialog, DialogBackdrop, DialogPanel, Input } 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 { Button } from '~/components/ui/button' +import { uploadCategorySchema, useAdminContext } from '~/contexts/admin' + +export const uploadSchema = z.object({ + file: z.instanceof(File), + category: uploadCategorySchema, +}) + +export type TUploadSchema = z.infer + +export const DialogUpload = () => { + const { isUploadOpen, setUploadedFile, setIsUploadOpen } = useAdminContext() + const fetcher = useFetcher() + const [error, setError] = useState() + const maxFileSize = 10 * 1024 // 10MB + + const formMethods = useRemixForm({ + mode: 'onSubmit', + fetcher, + resolver: zodResolver(uploadSchema), + }) + + const { handleSubmit, register, setValue } = formMethods + + useEffect(() => { + if (!fetcher.data?.success) { + setError(fetcher.data?.message) + return + } + + setUploadedFile(fetcher.data.uploadData.data.file_url) + + setError(undefined) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [fetcher]) + + const handleChange = async function (event: ChangeEvent) { + 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, + ) { + 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) => { + const files = event.target.files + if (files && files.length > 0) { + const file = files[0] + setValue('file', file) + } + } + + return ( + { + if (fetcher.state === 'idle') { + setIsUploadOpen(undefined) + } + }} + className="relative z-50" + transition + > + +
+ + + + {error && ( +
{error}
+ )} + + + +
+
+
+
+
+ ) +} diff --git a/app/layouts/admin/form-upload.tsx b/app/layouts/admin/form-upload.tsx deleted file mode 100644 index 0d24d42..0000000 --- a/app/layouts/admin/form-upload.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import { Input } 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 { Button } from '~/components/ui/button' -import { uploadCategorySchema, useAdminContext } from '~/contexts/admin' - -export const uploadSchema = z.object({ - file: z.instanceof(File), - category: uploadCategorySchema, -}) - -export type TUploadSchema = z.infer - -export const FormUpload = () => { - const { isUploadOpen, setUploadedFile } = useAdminContext() - const fetcher = useFetcher() - const [error, setError] = useState() - const maxFileSize = 10 * 1024 // 10MB - - const formMethods = useRemixForm({ - mode: 'onSubmit', - fetcher, - resolver: zodResolver(uploadSchema), - }) - - const { handleSubmit, register, setValue } = formMethods - - useEffect(() => { - if (!fetcher.data?.success) { - setError(fetcher.data?.message) - return - } - - setUploadedFile(fetcher.data.uploadData.data.file_url) - - setError(undefined) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [fetcher]) - - const handleChange = async function (event: ChangeEvent) { - 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, - ) { - 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) => { - const files = event.target.files - if (files && files.length > 0) { - const file = files[0] - setValue('file', file) - } - } - - return ( - - - {error && ( -
{error}
- )} - - - -
-
- ) -} diff --git a/app/routes/actions.admin.upload.tsx b/app/routes/actions.admin.upload.tsx index 6faa5d9..e31310f 100644 --- a/app/routes/actions.admin.upload.tsx +++ b/app/routes/actions.admin.upload.tsx @@ -4,7 +4,7 @@ import { getValidatedFormData } from 'remix-hook-form' import { XiorError } from 'xior' import { uploadFileRequest } from '~/apis/admin/upload-file' -import { uploadSchema, type TUploadSchema } from '~/layouts/admin/form-upload' +import { uploadSchema, type TUploadSchema } from '~/layouts/admin/dialog-upload' import { handleCookie } from '~/libs/cookies' import type { Route } from './+types/actions.register'