From fc67298a85e84cca6cca17d7a68d6184e2dbef19 Mon Sep 17 00:00:00 2001 From: "fredy.siswanto" Date: Sat, 8 Mar 2025 06:18:13 +0700 Subject: [PATCH] feat: migrate tag creation and update functionality to new form structure --- app/apis/admin/create-tags.ts | 2 +- app/apis/admin/update-tag.ts | 30 +++++++++ .../index.tsx | 43 +++++++++---- ..._admin.lg-admin._dashboard.tags.create.tsx | 4 +- ...in.lg-admin._dashboard.tags.update.$id.tsx | 20 ++++++ app/routes/actions.admin.tags.create.ts | 7 +- app/routes/actions.admin.tags.update.ts | 64 +++++++++++++++++++ 7 files changed, 150 insertions(+), 20 deletions(-) create mode 100644 app/apis/admin/update-tag.ts rename app/pages/{dashboard-tags-create => form-tag}/index.tsx (62%) create mode 100644 app/routes/_admin.lg-admin._dashboard.tags.update.$id.tsx create mode 100644 app/routes/actions.admin.tags.update.ts diff --git a/app/apis/admin/create-tags.ts b/app/apis/admin/create-tags.ts index e6bb3f7..2922887 100644 --- a/app/apis/admin/create-tags.ts +++ b/app/apis/admin/create-tags.ts @@ -1,7 +1,7 @@ import { z } from 'zod' import { HttpServer } from '~/libs/http-server' -import type { TTagSchema } from '~/pages/dashboard-tags-create' +import type { TTagSchema } from '~/pages/form-tag' const tagsResponseSchema = z.object({ data: z.object({ diff --git a/app/apis/admin/update-tag.ts b/app/apis/admin/update-tag.ts new file mode 100644 index 0000000..71c0064 --- /dev/null +++ b/app/apis/admin/update-tag.ts @@ -0,0 +1,30 @@ +import { z } from 'zod' + +import { HttpServer } from '~/libs/http-server' +import type { TTagSchema } from '~/pages/form-tag' + +const tagResponseSchema = z.object({ + data: z.object({ + Message: z.string(), + }), +}) + +type TParameters = { + accessToken: string + payload: TTagSchema +} + +export const updateTagRequest = async (parameters: TParameters) => { + const { accessToken, payload } = parameters + try { + const { id, ...restPayload } = payload + const { data } = await HttpServer({ accessToken }).put( + `/api/tag/${id}/update`, + restPayload, + ) + return tagResponseSchema.parse(data) + } catch (error) { + // eslint-disable-next-line unicorn/no-useless-promise-resolve-reject + return Promise.reject(error) + } +} diff --git a/app/pages/dashboard-tags-create/index.tsx b/app/pages/form-tag/index.tsx similarity index 62% rename from app/pages/dashboard-tags-create/index.tsx rename to app/pages/form-tag/index.tsx index fc2760f..35256ae 100644 --- a/app/pages/dashboard-tags-create/index.tsx +++ b/app/pages/form-tag/index.tsx @@ -4,28 +4,41 @@ import { useFetcher, useNavigate } from 'react-router' import { RemixFormProvider, useRemixForm } from 'remix-hook-form' import { z } from 'zod' +import type { TTagResponse } from '~/apis/common/get-tags' import { Button } from '~/components/ui/button' import { Input } from '~/components/ui/input' import { TitleDashboard } from '~/components/ui/title-dashboard' +import { urlFriendlyCode } from '~/utils/formatter' -export const createTagsSchema = z.object({ - code: z.string().min(3, 'Kode minimal 3 karakter'), +export const createTagSchema = z.object({ + id: z.string().optional(), name: z.string().min(3, 'Nama minimal 3 karakter'), + code: z.string(), }) -export type TTagSchema = z.infer +export type TTagSchema = z.infer +type TProperties = { + tagData?: TTagResponse +} -export const CreateTagsPage = () => { +export const FormTagPage = (properties: TProperties) => { + const { tagData } = properties || {} const fetcher = useFetcher() const navigate = useNavigate() const formMethods = useRemixForm({ mode: 'onSubmit', fetcher, - resolver: zodResolver(createTagsSchema), + resolver: zodResolver(createTagSchema), + values: { + id: tagData?.id || undefined, + code: tagData?.code || '', + name: tagData?.name || '', + }, }) const [error, setError] = useState() const [disabled, setDisabled] = useState(false) - const { handleSubmit } = formMethods + const { handleSubmit, watch, setValue } = formMethods + const watchName = watch('name') useEffect(() => { if (!fetcher.data?.success) { @@ -39,15 +52,20 @@ export const CreateTagsPage = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [fetcher]) + useEffect(() => { + setValue('code', urlFriendlyCode(watchName)) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [watchName]) + return (
- +
{error && ( @@ -56,19 +74,20 @@ export const CreateTagsPage = () => {
diff --git a/app/routes/_admin.lg-admin._dashboard.tags.create.tsx b/app/routes/_admin.lg-admin._dashboard.tags.create.tsx index 8e3448a..e74b2b4 100644 --- a/app/routes/_admin.lg-admin._dashboard.tags.create.tsx +++ b/app/routes/_admin.lg-admin._dashboard.tags.create.tsx @@ -1,4 +1,4 @@ -import { CreateTagsPage } from '~/pages/dashboard-tags-create' +import { FormTagPage } from '~/pages/form-tag' -const DashboardTagsCreateLayout = () => +const DashboardTagsCreateLayout = () => export default DashboardTagsCreateLayout diff --git a/app/routes/_admin.lg-admin._dashboard.tags.update.$id.tsx b/app/routes/_admin.lg-admin._dashboard.tags.update.$id.tsx new file mode 100644 index 0000000..cb98cdb --- /dev/null +++ b/app/routes/_admin.lg-admin._dashboard.tags.update.$id.tsx @@ -0,0 +1,20 @@ +import { getTags } from '~/apis/common/get-tags' +import { handleCookie } from '~/libs/cookies' +import { FormTagPage } from '~/pages/form-tag' + +import type { Route } from './+types/_admin.lg-admin._dashboard.tags.update.$id' + +export const loader = async ({ request, params }: Route.LoaderArgs) => { + const { staffToken } = await handleCookie(request) + const { data: tagsData } = await getTags({ + accessToken: staffToken, + }) + const tagData = tagsData.find((tag) => tag.id === params.id) + return { tagData } +} + +const DashboardTagUpdateLayout = ({ loaderData }: Route.ComponentProps) => { + const tagData = loaderData.tagData + return +} +export default DashboardTagUpdateLayout diff --git a/app/routes/actions.admin.tags.create.ts b/app/routes/actions.admin.tags.create.ts index d6ad20f..8dcf3c0 100644 --- a/app/routes/actions.admin.tags.create.ts +++ b/app/routes/actions.admin.tags.create.ts @@ -5,10 +5,7 @@ import { XiorError } from 'xior' import { createTagsRequest } from '~/apis/admin/create-tags' import { handleCookie } from '~/libs/cookies' -import { - createTagsSchema, - type TTagSchema, -} from '~/pages/dashboard-tags-create' +import { createTagSchema, type TTagSchema } from '~/pages/form-tag' import type { Route } from './+types/actions.register' @@ -21,7 +18,7 @@ export const action = async ({ request }: Route.ActionArgs) => { receivedValues: defaultValues, } = await getValidatedFormData( request, - zodResolver(createTagsSchema), + zodResolver(createTagSchema), false, ) diff --git a/app/routes/actions.admin.tags.update.ts b/app/routes/actions.admin.tags.update.ts new file mode 100644 index 0000000..ca3cfd3 --- /dev/null +++ b/app/routes/actions.admin.tags.update.ts @@ -0,0 +1,64 @@ +import { zodResolver } from '@hookform/resolvers/zod' +import { data } from 'react-router' +import { getValidatedFormData } from 'remix-hook-form' +import { XiorError } from 'xior' + +import { updateTagRequest } from '~/apis/admin/update-tag' +import { handleCookie } from '~/libs/cookies' +import { createTagSchema, type TTagSchema } from '~/pages/form-tag' + +import type { Route } from './+types/actions.register' + +export const action = async ({ request }: Route.ActionArgs) => { + const { staffToken } = await handleCookie(request) + try { + const { + errors, + data: payload, + receivedValues: defaultValues, + } = await getValidatedFormData( + request, + zodResolver(createTagSchema), + false, + ) + + if (errors) { + return data({ success: false, errors, defaultValues }, { status: 400 }) + } + + const { data: tagData } = await updateTagRequest({ + accessToken: staffToken, + payload, + }) + + return data( + { + success: true, + tagData, + }, + { + status: 200, + statusText: 'OK', + }, + ) + } catch (error) { + if (error instanceof XiorError) { + return data( + { + success: false, + message: error?.response?.data?.error?.message || error.message, + }, + { + status: error?.response?.status || 500, + }, + ) + } + return data( + { + success: false, + message: 'Internal server error', + }, + { status: 500 }, + ) + } +}