diff --git a/app/apis/common/get-tags.ts b/app/apis/common/get-tags.ts new file mode 100644 index 0000000..bf6c36b --- /dev/null +++ b/app/apis/common/get-tags.ts @@ -0,0 +1,25 @@ +import { z } from 'zod' + +import { HttpServer, type THttpServer } from '~/libs/http-server' + +const tagSchema = z.object({ + data: z.array( + z.object({ + id: z.string(), + code: z.string(), + name: z.string(), + }), + ), +}) + +export type TTagSchema = z.infer + +export const getTags = async (parameters?: THttpServer) => { + try { + const { data } = await HttpServer(parameters).get(`/api/tag`) + return tagSchema.parse(data) + } catch (error) { + // eslint-disable-next-line unicorn/no-useless-promise-resolve-reject + return Promise.reject(error) + } +} diff --git a/app/components/ui/combobox.tsx b/app/components/ui/combobox.tsx index 1e8c9a2..d4e662c 100644 --- a/app/components/ui/combobox.tsx +++ b/app/components/ui/combobox.tsx @@ -35,13 +35,26 @@ type TInputProperties = ComponentProps< rules?: RegisterOptions placeholder?: string options?: TComboboxOption[] + labelClassName?: string + containerClassName?: string } export const Combobox = >( properties: TInputProperties, ) => { - const { id, label, name, rules, disabled, placeholder, options, ...rest } = - properties + const { + id, + label, + name, + rules, + disabled, + placeholder, + options, + className, + labelClassName, + containerClassName, + ...rest + } = properties const { control, formState: { errors }, @@ -58,11 +71,11 @@ export const Combobox = >( return ( - { const editor = useEditor({ extensions: [StarterKit], + immediatelyRender: false, content: '

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quis lobortis nisl cursus bibendum sit nulla accumsan sodales ornare. At urna viverra non suspendisse neque, lorem. Pretium condimentum pellentesque gravida id etiam sit sed arcu euismod. Rhoncus proin orci duis scelerisque molestie cursus tincidunt aliquam.

', }) diff --git a/app/components/ui/title-dashboard.tsx b/app/components/ui/title-dashboard.tsx index 9a0c16d..8d1ef35 100644 --- a/app/components/ui/title-dashboard.tsx +++ b/app/components/ui/title-dashboard.tsx @@ -4,7 +4,7 @@ type TitleDashboardProperties = { export const TitleDashboard = (properties: TitleDashboardProperties) => { const { title } = properties return ( -
+

{title}

diff --git a/app/pages/contents-create/index.tsx b/app/pages/contents-create/index.tsx index d6a684d..7471ada 100644 --- a/app/pages/contents-create/index.tsx +++ b/app/pages/contents-create/index.tsx @@ -1,45 +1,156 @@ -import { Field, Input, Label, Select } from '@headlessui/react' +import { DevTool } from '@hookform/devtools' +import { zodResolver } from '@hookform/resolvers/zod' +import { useEffect, useState } from 'react' +import { useFetcher, useRouteLoaderData } from 'react-router' +import { RemixFormProvider, useRemixForm } from 'remix-hook-form' +import { z } from 'zod' -import { SearchIcon } from '~/components/icons/search' +import { Button } from '~/components/ui/button' +import { Combobox } from '~/components/ui/combobox' +import { Input } from '~/components/ui/input' import DefaultTextEditor from '~/components/ui/text-editor' import { TitleDashboard } from '~/components/ui/title-dashboard' +import type { loader } from '~/routes/_admin.lg-admin' + +export const contentSchema = z.object({ + categories: z + .array( + z + .object({ + id: z.string(), + code: z.string(), + name: z.string(), + }) + .optional() + .nullable(), + ) + .refine((data) => !!data, { + message: 'Please select a category', + }), + tags: z.array( + z + .object({ + id: z.string(), + code: z.string(), + name: z.string(), + }) + .optional() + .nullable(), + ), + title: z.string().min(1, { + message: 'Title is required', + }), + content: z.string().min(1, { + message: 'Content is required', + }), + featured_image: z.string().optional(), + is_premium: z.boolean().optional(), + live_at: z.string().min(1, { + message: 'Tanggal live is required', + }), +}) + +export type TContentSchema = z.infer export const CreateContentsPage = () => { + const fetcher = useFetcher() + const loaderData = useRouteLoaderData('routes/_admin.lg-admin') + const categories = loaderData?.categoriesData + const tags = loaderData?.tagsData + const [error, setError] = useState() + const [disabled, setDisabled] = useState(false) + + const formMethods = useRemixForm({ + mode: 'onSubmit', + fetcher, + resolver: zodResolver(contentSchema), + }) + + const { handleSubmit, control, watch } = formMethods + const watchCategories = watch('categories') + const watchTags = watch('tags') + + useEffect(() => { + if (!fetcher.data?.success) { + setError(fetcher.data?.message) + setDisabled(false) + return + } + + setDisabled(true) + setError(undefined) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [fetcher]) + return (
-
-
- - -
- -
- -
-
-
-
+ + + {error && ( +
{error}
+ )} +
+ category?.name).join(', ') + : 'Pilih Kategori' + } + options={categories} + className="border-0 bg-white shadow focus:ring-1 focus:ring-[#2E2F7C] focus:outline-none" + labelClassName="text-sm font-medium text-[#363636]" + containerClassName="flex-1" + /> + tag?.name).join(', ') + : 'Pilih Tags' + } + options={tags} + className="border-0 bg-white shadow focus:ring-1 focus:ring-[#2E2F7C] focus:outline-none" + labelClassName="text-sm font-medium text-[#363636]" + containerClassName="flex-1" + /> + + +
-
- - - - -
-
+
+ +
+ + -
- -
+
) } diff --git a/app/pages/dashboard-contents/index.tsx b/app/pages/dashboard-contents/index.tsx index b6df7b2..7ba58fe 100644 --- a/app/pages/dashboard-contents/index.tsx +++ b/app/pages/dashboard-contents/index.tsx @@ -91,7 +91,7 @@ export const ContentsPage = () => {