Compare commits

..

No commits in common. "ea6462f3ea7ba6f0fe6214c14d1f3dd82f21c8ac" and "dfd1a46694c91c79de207fd743e9c3855b7eda11" have entirely different histories.

13 changed files with 177 additions and 283 deletions

View File

@ -1,29 +0,0 @@
import { z } from 'zod'
import { HttpServer, type THttpServer } from '~/libs/http-server'
import type { TSubscribePlanSchema } from '~/pages/form-subscriptions-plan'
const subscribePlanResponseSchema = z.object({
data: z.object({
Message: z.string(),
}),
})
type TTSubscribePlanId = Pick<TSubscribePlanSchema, 'id'>
type TParameters = {
payload: TTSubscribePlanId
} & THttpServer
export const deleteSubscribePlanRequest = async (parameters: TParameters) => {
const { payload, ...restParameters } = parameters
const { id } = payload
try {
const { data } = await HttpServer(restParameters).delete(
`/api/subscribe-plan/${id}/delete`,
)
return subscribePlanResponseSchema.parse(data)
} catch (error) {
// eslint-disable-next-line unicorn/no-useless-promise-resolve-reject
return Promise.reject(error)
}
}

View File

@ -1,30 +0,0 @@
import { z } from 'zod'
import { HttpServer, type THttpServer } from '~/libs/http-server'
import type { TTagSchema } from '~/pages/form-tag'
const deleteTagsResponseSchema = z.object({
data: z.object({
Message: z.string(),
}),
})
type TTagsId = Pick<TTagSchema, 'id'>
type TParameters = {
payload: TTagsId
} & THttpServer
export type TDeleteTagsSchema = z.infer<typeof deleteTagsResponseSchema>
export const deleteTagsRequest = async (parameters: TParameters) => {
const { payload, ...restParameters } = parameters
const { id } = payload
try {
const { data } = await HttpServer(restParameters).delete(
`/api/tag/${id}/delete`,
)
return deleteTagsResponseSchema.parse(data)
} catch (error) {
// eslint-disable-next-line unicorn/no-useless-promise-resolve-reject
return Promise.reject(error)
}
}

View File

@ -1,6 +1,6 @@
import { z } from 'zod' import { z } from 'zod'
import type { TUploadSchema } from '~/layouts/admin/dialog-upload' import type { TUploadSchema } from '~/layouts/admin/form-upload'
import { HttpServer, type THttpServer } from '~/libs/http-server' import { HttpServer, type THttpServer } from '~/libs/http-server'
const uploadResponseSchema = z.object({ const uploadResponseSchema = z.object({

View File

@ -8,9 +8,6 @@ const subscriptionResponseSchema = z.object({
id: z.string(), id: z.string(),
code: z.string(), code: z.string(),
name: z.string(), name: z.string(),
length: z.number().optional(),
price: z.number().optional(),
status: z.number().optional(),
}), }),
), ),
}) })

View File

@ -127,7 +127,7 @@ export const EditorMenuBar = (properties: TProperties) => {
isActive={editor.isActive('bold')} isActive={editor.isActive('bold')}
title="Bold" title="Bold"
> >
<BoldIcon className="size-4" /> <BoldIcon />
</EditorButton> </EditorButton>
<EditorButton <EditorButton
onClick={() => editor.chain().focus().toggleItalic().run()} onClick={() => editor.chain().focus().toggleItalic().run()}
@ -137,7 +137,7 @@ export const EditorMenuBar = (properties: TProperties) => {
isActive={editor.isActive('italic')} isActive={editor.isActive('italic')}
title="Italic" title="Italic"
> >
<ItalicIcon className="size-4" /> <ItalicIcon />
</EditorButton> </EditorButton>
<EditorButton <EditorButton
onClick={() => editor.chain().focus().toggleStrike().run()} onClick={() => editor.chain().focus().toggleStrike().run()}
@ -147,7 +147,7 @@ export const EditorMenuBar = (properties: TProperties) => {
isActive={editor.isActive('strike')} isActive={editor.isActive('strike')}
title="Strike" title="Strike"
> >
<StrikethroughIcon className="size-4" /> <StrikethroughIcon />
</EditorButton> </EditorButton>
<div className="relative"> <div className="relative">
<EditorButton <EditorButton
@ -159,7 +159,7 @@ export const EditorMenuBar = (properties: TProperties) => {
isActive={true} isActive={true}
disabled={disabled} disabled={disabled}
> >
<SwatchIcon className="size-4" /> <SwatchIcon />
</EditorButton> </EditorButton>
{isOpenColor && ( {isOpenColor && (
<div <div
@ -191,7 +191,7 @@ export const EditorMenuBar = (properties: TProperties) => {
isActive={editor.isActive({ textAlign: 'left' })} isActive={editor.isActive({ textAlign: 'left' })}
title="Align Left" title="Align Left"
> >
<Bars3BottomLeftIcon className="size-4" /> <Bars3BottomLeftIcon />
</EditorButton> </EditorButton>
<EditorButton <EditorButton
onClick={() => editor.chain().focus().setTextAlign('center').run()} onClick={() => editor.chain().focus().setTextAlign('center').run()}
@ -202,7 +202,7 @@ export const EditorMenuBar = (properties: TProperties) => {
isActive={editor.isActive({ textAlign: 'center' })} isActive={editor.isActive({ textAlign: 'center' })}
title="Align Center" title="Align Center"
> >
<Bars3Icon className="size-4" /> <Bars3Icon />
</EditorButton> </EditorButton>
<EditorButton <EditorButton
onClick={() => editor.chain().focus().setTextAlign('right').run()} onClick={() => editor.chain().focus().setTextAlign('right').run()}
@ -213,7 +213,7 @@ export const EditorMenuBar = (properties: TProperties) => {
isActive={editor.isActive({ textAlign: 'right' })} isActive={editor.isActive({ textAlign: 'right' })}
title="Align Right" title="Align Right"
> >
<Bars3BottomRightIcon className="size-4" /> <Bars3BottomRightIcon />
</EditorButton> </EditorButton>
<EditorButton <EditorButton
onClick={() => editor.chain().focus().setTextAlign('justify').run()} onClick={() => editor.chain().focus().setTextAlign('justify').run()}
@ -224,7 +224,7 @@ export const EditorMenuBar = (properties: TProperties) => {
isActive={editor.isActive({ textAlign: 'justify' })} isActive={editor.isActive({ textAlign: 'justify' })}
title="Align Justify" title="Align Justify"
> >
<Bars4Icon className="size-4" /> <Bars4Icon />
</EditorButton> </EditorButton>
</div> </div>
<div className="flex max-w-[150px] flex-wrap items-start gap-1 px-1"> <div className="flex max-w-[150px] flex-wrap items-start gap-1 px-1">
@ -236,7 +236,7 @@ export const EditorMenuBar = (properties: TProperties) => {
title="Heading 1" title="Heading 1"
disabled={disabled} disabled={disabled}
> >
<H1Icon className="size-4" /> <H1Icon />
</EditorButton> </EditorButton>
<EditorButton <EditorButton
onClick={() => onClick={() =>
@ -246,7 +246,7 @@ export const EditorMenuBar = (properties: TProperties) => {
title="Heading 2" title="Heading 2"
disabled={disabled} disabled={disabled}
> >
<H2Icon className="size-4" /> <H2Icon />
</EditorButton> </EditorButton>
<EditorButton <EditorButton
onClick={() => onClick={() =>
@ -256,7 +256,7 @@ export const EditorMenuBar = (properties: TProperties) => {
title="Heading 3" title="Heading 3"
disabled={disabled} disabled={disabled}
> >
<H3Icon className="size-4" /> <H3Icon />
</EditorButton> </EditorButton>
{/* <EditorButton {/* <EditorButton
onClick={() => editor.chain().focus().setParagraph().run()} onClick={() => editor.chain().focus().setParagraph().run()}
@ -272,7 +272,7 @@ export const EditorMenuBar = (properties: TProperties) => {
title="Bullet List" title="Bullet List"
disabled={disabled} disabled={disabled}
> >
<ListBulletIcon className="size-4" /> <ListBulletIcon />
</EditorButton> </EditorButton>
<EditorButton <EditorButton
onClick={() => editor.chain().focus().toggleOrderedList().run()} onClick={() => editor.chain().focus().toggleOrderedList().run()}
@ -280,7 +280,7 @@ export const EditorMenuBar = (properties: TProperties) => {
title="Ordered List" title="Ordered List"
disabled={disabled} disabled={disabled}
> >
<NumberedListIcon className="size-4" /> <NumberedListIcon />
</EditorButton> </EditorButton>
<EditorButton <EditorButton
onClick={() => editor.chain().focus().toggleCodeBlock().run()} onClick={() => editor.chain().focus().toggleCodeBlock().run()}
@ -288,7 +288,7 @@ export const EditorMenuBar = (properties: TProperties) => {
title="Code Block" title="Code Block"
disabled={disabled} disabled={disabled}
> >
<CodeBracketIcon className="size-4" /> <CodeBracketIcon />
</EditorButton> </EditorButton>
</div> </div>
{/* <div className="flex items-start gap-1 px-1"> {/* <div className="flex items-start gap-1 px-1">
@ -334,7 +334,7 @@ export const EditorMenuBar = (properties: TProperties) => {
title="Insert Image" title="Insert Image"
disabled={disabled} disabled={disabled}
> >
<PhotoIcon className="size-4" /> <PhotoIcon />
</EditorButton> </EditorButton>
{isOpenImage && ( {isOpenImage && (
<div <div
@ -380,14 +380,14 @@ export const EditorMenuBar = (properties: TProperties) => {
isActive={editor.isActive('link')} isActive={editor.isActive('link')}
title="Set Link" title="Set Link"
> >
<LinkIcon className="size-4" /> <LinkIcon />
</EditorButton> </EditorButton>
<EditorButton <EditorButton
onClick={() => editor.chain().focus().unsetLink().run()} onClick={() => editor.chain().focus().unsetLink().run()}
disabled={disabled || !editor.isActive('link')} disabled={disabled || !editor.isActive('link')}
title="Unset Link" title="Unset Link"
> >
<LinkSlashIcon className="size-4" /> <LinkSlashIcon />
</EditorButton> </EditorButton>
</div> </div>
<div className="flex items-start gap-1 px-1"> <div className="flex items-start gap-1 px-1">
@ -396,14 +396,14 @@ export const EditorMenuBar = (properties: TProperties) => {
disabled={disabled || !editor.can().chain().focus().undo().run()} disabled={disabled || !editor.can().chain().focus().undo().run()}
title="Undo" title="Undo"
> >
<ArrowUturnLeftIcon className="size-4" /> <ArrowUturnLeftIcon />
</EditorButton> </EditorButton>
<EditorButton <EditorButton
onClick={() => editor.chain().focus().redo().run()} onClick={() => editor.chain().focus().redo().run()}
disabled={disabled || !editor.can().chain().focus().redo().run()} disabled={disabled || !editor.can().chain().focus().redo().run()}
title="Redo" title="Redo"
> >
<ArrowUturnRightIcon className="size-4" /> <ArrowUturnRightIcon />
</EditorButton> </EditorButton>
</div> </div>
</div> </div>
@ -413,7 +413,7 @@ export const EditorMenuBar = (properties: TProperties) => {
onClick={() => setIsPlainHTML(true)} onClick={() => setIsPlainHTML(true)}
title="Switch to Plain Text" title="Switch to Plain Text"
> >
<DocumentTextIcon className="size-4" /> <DocumentTextIcon />
</EditorButton> </EditorButton>
</div> </div>
</div> </div>

View File

@ -1,11 +1,15 @@
import { Dialog, DialogBackdrop, DialogPanel } from '@headlessui/react'
import type { PropsWithChildren } from 'react' import type { PropsWithChildren } from 'react'
import { DialogUpload } from './dialog-upload' import { useAdminContext } from '~/contexts/admin'
import { FormUpload } from './form-upload'
import { Navbar } from './navbar' import { Navbar } from './navbar'
import { Sidebar } from './sidebar' import { Sidebar } from './sidebar'
export const AdminDashboardLayout = (properties: PropsWithChildren) => { export const AdminDashboardLayout = (properties: PropsWithChildren) => {
const { children } = properties const { children } = properties
const { isUploadOpen, setIsUploadOpen } = useAdminContext()
return ( return (
<div className="flex flex-col"> <div className="flex flex-col">
<Navbar /> <Navbar />
@ -14,7 +18,27 @@ export const AdminDashboardLayout = (properties: PropsWithChildren) => {
<div className="min-h-[calc(100dvh-80px)] flex-1 p-8">{children}</div> <div className="min-h-[calc(100dvh-80px)] flex-1 p-8">{children}</div>
</div> </div>
<DialogUpload /> <Dialog
open={!!isUploadOpen}
onClose={() => {
setIsUploadOpen(undefined)
}}
className="relative z-50"
transition
>
<DialogBackdrop
className="fixed inset-0 bg-black/50 duration-300 ease-out data-[closed]:opacity-0"
transition
/>
<div className="fixed inset-0 flex w-screen justify-center overflow-y-auto p-0 max-sm:bg-white sm:items-center sm:p-4">
<DialogPanel
transition
className="max-w-lg space-y-6 rounded-lg bg-white p-8 duration-300 ease-out data-[closed]:scale-95 data-[closed]:opacity-0 sm:shadow-lg"
>
<FormUpload />
</DialogPanel>
</div>
</Dialog>
</div> </div>
) )
} }

View File

@ -1,143 +0,0 @@
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<typeof uploadSchema>
export const DialogUpload = () => {
const { isUploadOpen, setUploadedFile, setIsUploadOpen } = useAdminContext()
const fetcher = useFetcher()
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)
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<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 (
<Dialog
open={!!isUploadOpen}
onClose={() => {
if (fetcher.state === 'idle') {
setIsUploadOpen(undefined)
}
}}
className="relative z-50"
transition
>
<DialogBackdrop
className="fixed inset-0 bg-black/50 duration-300 ease-out data-[closed]:opacity-0"
transition
/>
<div className="fixed inset-0 flex w-screen justify-center overflow-y-auto p-0 max-sm:bg-white sm:items-center sm:p-4">
<DialogPanel
transition
className="max-w-lg space-y-6 rounded-lg bg-white p-8 duration-300 ease-out data-[closed]:scale-95 data-[closed]:opacity-0 sm:shadow-lg"
>
<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/*"
className="h-[42px] w-full cursor-pointer rounded-md border border-[#DFDFDF] p-2"
onChange={handleChange}
/>
<input
type="hidden"
id="input-file-upload-type"
value={isUploadOpen}
{...register('category')}
/>
<Button
disabled={fetcher.state !== 'idle'}
isLoading={fetcher.state !== 'idle'}
type="submit"
className="w-full rounded-md py-2"
>
Upload
</Button>
</fetcher.Form>
</RemixFormProvider>
</DialogPanel>
</div>
</Dialog>
)
}

View File

@ -0,0 +1,121 @@
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<typeof uploadSchema>
export const FormUpload = () => {
const { isUploadOpen, setUploadedFile } = useAdminContext()
const fetcher = useFetcher()
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)
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<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/*"
className="h-[42px] w-full cursor-pointer rounded-md border border-[#DFDFDF] p-2"
onChange={handleChange}
/>
<input
type="hidden"
id="input-file-upload-type"
value={isUploadOpen}
{...register('category')}
/>
<Button
disabled={fetcher.state !== 'idle'}
isLoading={fetcher.state !== 'idle'}
type="submit"
className="w-full rounded-md py-2"
>
Upload
</Button>
</fetcher.Form>
</RemixFormProvider>
)
}

View File

@ -60,9 +60,7 @@ export const ContentsPage = () => {
2: (_value: unknown, _type: unknown, data: TNewsResponse) => ( 2: (_value: unknown, _type: unknown, data: TNewsResponse) => (
<div> <div>
<div>{data.author.name}</div> <div>{data.author.name}</div>
<div className="text-sm text-[#7C7C7C]"> <div className="text-sm text-[#7C7C7C]">ID: {data.id.slice(0, 8)}</div>
ID: {data.author.id.slice(0, 8)}
</div>
</div> </div>
), ),
3: (value: string) => <span className="text-sm">{value}</span>, 3: (value: string) => <span className="text-sm">{value}</span>,

View File

@ -1,4 +1,3 @@
import { Field, Label, Select } from '@headlessui/react'
import { zodResolver } from '@hookform/resolvers/zod' import { zodResolver } from '@hookform/resolvers/zod'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { useFetcher, useNavigate } from 'react-router' import { useFetcher, useNavigate } from 'react-router'
@ -14,9 +13,6 @@ export const createSubscribePlanSchema = z.object({
id: z.string().optional(), id: z.string().optional(),
name: z.string().min(3, 'Nama minimal 3 karakter'), name: z.string().min(3, 'Nama minimal 3 karakter'),
code: z.string(), code: z.string(),
length: z.preprocess(Number, z.number().optional()),
price: z.preprocess(Number, z.number().optional()),
status: z.boolean().optional(),
}) })
export type TSubscribePlanSchema = z.infer<typeof createSubscribePlanSchema> export type TSubscribePlanSchema = z.infer<typeof createSubscribePlanSchema>
type TProperties = { type TProperties = {
@ -35,9 +31,6 @@ export const FormSubscribePlanPage = (properties: TProperties) => {
id: subscribePlanData?.id || undefined, id: subscribePlanData?.id || undefined,
code: subscribePlanData?.code || '', code: subscribePlanData?.code || '',
name: subscribePlanData?.name || '', name: subscribePlanData?.name || '',
length: subscribePlanData?.length || undefined,
price: subscribePlanData?.price || undefined,
status: subscribePlanData?.status || undefined,
}, },
}) })
const [error, setError] = useState<string>() const [error, setError] = useState<string>()
@ -106,41 +99,6 @@ export const FormSubscribePlanPage = (properties: TProperties) => {
Save Save
</Button> </Button>
</div> </div>
<div className="flex items-end justify-between gap-4">
<Input
id="length"
label="Length"
type="number"
placeholder="Masukkan Subscribe Plan Length (days)"
name="length"
className="border-0 bg-white shadow read-only:bg-gray-100 focus:ring-1 focus:ring-[#2E2F7C] focus:outline-none"
labelClassName="text-sm font-medium text-[#363636]"
containerClassName="flex-1"
/>
<Input
id="price"
label="Price"
placeholder="Masukkan Price"
type="number"
name="price"
className="border-0 bg-white shadow read-only:bg-gray-100 focus:ring-1 focus:ring-[#2E2F7C] focus:outline-none"
labelClassName="text-sm font-medium text-[#363636]"
containerClassName="flex-1"
/>
<Field className={'flex-1'}>
<Label className="mb-2 block text-sm font-medium">Status</Label>
<Select
name="status"
id="status"
className="w-full rounded-lg bg-white p-2 shadow focus:ring-1 focus:ring-[#2E2F7C] focus:outline-none"
>
<option disabled>Pilih Status</option>
<option value={1}>Aktif</option>
<option value={0}>Nonaktif</option>
</Select>
</Field>
</div>
</fetcher.Form> </fetcher.Form>
</RemixFormProvider> </RemixFormProvider>
</div> </div>

View File

@ -29,7 +29,7 @@ export const action = async ({ request }: Route.ActionArgs) => {
return data({ success: false, errors, defaultValues }, { status: 400 }) return data({ success: false, errors, defaultValues }, { status: 400 })
} }
const { data: subscribePlanData } = await createSubscribePlanRequest({ const { data: tagsData } = await createSubscribePlanRequest({
accessToken: staffToken, accessToken: staffToken,
payload, payload,
}) })
@ -37,7 +37,7 @@ export const action = async ({ request }: Route.ActionArgs) => {
return data( return data(
{ {
success: true, success: true,
subscribePlanData, tagsData,
}, },
{ {
status: 200, status: 200,

View File

@ -5,10 +5,8 @@ import { XiorError } from 'xior'
import { updateSubscribePlanRequest } from '~/apis/admin/update-subscribe-plan' import { updateSubscribePlanRequest } from '~/apis/admin/update-subscribe-plan'
import { handleCookie } from '~/libs/cookies' import { handleCookie } from '~/libs/cookies'
import { import type { TSubscribePlanSchema } from '~/pages/form-subscriptions-plan'
createSubscribePlanSchema, import { createTagSchema } from '~/pages/form-tag'
type TSubscribePlanSchema,
} from '~/pages/form-subscriptions-plan'
import type { Route } from './+types/actions.register' import type { Route } from './+types/actions.register'
@ -21,7 +19,7 @@ export const action = async ({ request }: Route.ActionArgs) => {
receivedValues: defaultValues, receivedValues: defaultValues,
} = await getValidatedFormData<TSubscribePlanSchema>( } = await getValidatedFormData<TSubscribePlanSchema>(
request, request,
zodResolver(createSubscribePlanSchema), zodResolver(createTagSchema),
false, false,
) )
@ -29,7 +27,7 @@ export const action = async ({ request }: Route.ActionArgs) => {
return data({ success: false, errors, defaultValues }, { status: 400 }) return data({ success: false, errors, defaultValues }, { status: 400 })
} }
const { data: subscribePlanData } = await updateSubscribePlanRequest({ const { data: tagData } = await updateSubscribePlanRequest({
accessToken: staffToken, accessToken: staffToken,
payload, payload,
}) })
@ -37,7 +35,7 @@ export const action = async ({ request }: Route.ActionArgs) => {
return data( return data(
{ {
success: true, success: true,
subscribePlanData, tagData,
}, },
{ {
status: 200, status: 200,

View File

@ -4,7 +4,7 @@ import { getValidatedFormData } from 'remix-hook-form'
import { XiorError } from 'xior' import { XiorError } from 'xior'
import { uploadFileRequest } from '~/apis/admin/upload-file' import { uploadFileRequest } from '~/apis/admin/upload-file'
import { uploadSchema, type TUploadSchema } from '~/layouts/admin/dialog-upload' import { uploadSchema, type TUploadSchema } from '~/layouts/admin/form-upload'
import { handleCookie } from '~/libs/cookies' import { handleCookie } from '~/libs/cookies'
import type { Route } from './+types/actions.register' import type { Route } from './+types/actions.register'