From 3a15c0d945c5864682a8027e7594e20b1466fea8 Mon Sep 17 00:00:00 2001 From: Ardeman Date: Sat, 8 Mar 2025 00:45:05 +0800 Subject: [PATCH 1/5] feat: enhance category schema with sequence and description fields, and update dashboard display --- app/apis/common/get-categories.ts | 2 ++ app/pages/dashboard-categories/index.tsx | 28 ++++++++++++++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/app/apis/common/get-categories.ts b/app/apis/common/get-categories.ts index c074cee..778e58d 100644 --- a/app/apis/common/get-categories.ts +++ b/app/apis/common/get-categories.ts @@ -6,6 +6,8 @@ export const categoryResponseSchema = z.object({ id: z.string(), name: z.string(), code: z.string(), + sequence: z.number().nullable(), + description: z.string().nullable(), }) const categoriesResponseSchema = z.object({ data: z.array(categoryResponseSchema), diff --git a/app/pages/dashboard-categories/index.tsx b/app/pages/dashboard-categories/index.tsx index f3ee82e..163e64c 100644 --- a/app/pages/dashboard-categories/index.tsx +++ b/app/pages/dashboard-categories/index.tsx @@ -2,6 +2,7 @@ import DT from 'datatables.net-dt' import DataTable from 'datatables.net-react' import { Link, useRouteLoaderData } from 'react-router' +import type { TCategoryResponse } from '~/apis/common/get-categories' import { Button } from '~/components/ui/button' import { UiTable } from '~/components/ui/table' import { TitleDashboard } from '~/components/ui/title-dashboard' @@ -13,26 +14,33 @@ export const CategoriesPage = () => { const categoriesData = loaderData?.dataCategories DataTable.use(DT) - const dataTable = categoriesData + const dataTable = categoriesData?.sort((a, b) => { + if (a.sequence === null) return 1 + if (b.sequence === null) return -1 + return a.sequence - b.sequence + }) const dataColumns = [ { title: 'No', render: ( data: unknown, type: unknown, - row: unknown, + row: TCategoryResponse, meta: { row: number }, ) => { - return meta.row + 1 + return `
${meta.row + 1}
${ + row.sequence === null + ? '' + : `
Urutan: ${row.sequence}
` + }` }, }, { - title: 'Nama', - data: 'name', + title: 'Kategori', }, { - title: 'Kode', - data: 'code', + title: 'Deskripsi', + data: 'description', }, { title: 'Action', @@ -40,6 +48,12 @@ export const CategoriesPage = () => { }, ] const dataSlot = { + 1: (_value: unknown, _type: unknown, data: TCategoryResponse) => ( +
+
{data.name}
+
Kode: {data.code}
+
+ ), 3: (value: string) => ( +
+ + +
diff --git a/app/pages/form-contents/index.tsx b/app/pages/form-contents/index.tsx index 893644b..d255481 100644 --- a/app/pages/form-contents/index.tsx +++ b/app/pages/form-contents/index.tsx @@ -75,7 +75,7 @@ export const FormContentsPage = (properties: TProperties) => { fetcher, resolver: zodResolver(contentSchema), values: { - id: newsData?.id || '', + id: newsData?.id || undefined, categories: newsData?.categories || [], tags: newsData?.tags || [], title: newsData?.title || '', diff --git a/app/routes/actions.admin.categories.update.ts b/app/routes/actions.admin.categories.update.ts new file mode 100644 index 0000000..dab861f --- /dev/null +++ b/app/routes/actions.admin.categories.update.ts @@ -0,0 +1,67 @@ +import { zodResolver } from '@hookform/resolvers/zod' +import { data } from 'react-router' +import { getValidatedFormData } from 'remix-hook-form' +import { XiorError } from 'xior' + +import { updateCategoryRequest } from '~/apis/admin/update-category' +import { handleCookie } from '~/libs/cookies' +import { + createCategorySchema, + type TCategorySchema, +} from '~/pages/form-category' + +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(createCategorySchema), + false, + ) + + if (errors) { + return data({ success: false, errors, defaultValues }, { status: 400 }) + } + + const { data: categoryData } = await updateCategoryRequest({ + accessToken: staffToken, + payload, + }) + + return data( + { + success: true, + categoryData, + }, + { + 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 }, + ) + } +} From 87156e93ee06924eaf21ecd77cbf9a409a94e813 Mon Sep 17 00:00:00 2001 From: Ardeman Date: Sat, 8 Mar 2025 02:14:36 +0800 Subject: [PATCH 5/5] feat: sort categories by sequence and update category description handling in news pages --- app/layouts/news/header-menu.tsx | 6 +++++- app/pages/news-categories/index.tsx | 13 ++++++------- ....category.$name.tsx => _news.category.$code.tsx} | 0 3 files changed, 11 insertions(+), 8 deletions(-) rename app/routes/{_news.category.$name.tsx => _news.category.$code.tsx} (100%) diff --git a/app/layouts/news/header-menu.tsx b/app/layouts/news/header-menu.tsx index 8bfab06..8b8cdab 100644 --- a/app/layouts/news/header-menu.tsx +++ b/app/layouts/news/header-menu.tsx @@ -7,7 +7,11 @@ import { HeaderSearch } from './header-search' export const HeaderMenu = () => { const loaderData = useRouteLoaderData('routes/_news') - const menu = loaderData?.categoriesData + const menu = loaderData?.categoriesData?.sort((a, b) => { + if (a.sequence === null) return 1 + if (b.sequence === null) return -1 + return a.sequence - b.sequence + }) return ( <> diff --git a/app/pages/news-categories/index.tsx b/app/pages/news-categories/index.tsx index fffb282..40fc18a 100644 --- a/app/pages/news-categories/index.tsx +++ b/app/pages/news-categories/index.tsx @@ -1,18 +1,17 @@ -import { useLocation, useRouteLoaderData } from 'react-router' +import { useParams, useRouteLoaderData } from 'react-router' import { Card } from '~/components/ui/card' import { CategorySection } from '~/components/ui/category-section' -import { DUMMY_DESCRIPTION } from '~/data/contents' import type { loader } from '~/routes/_news' import { BERITA } from './data' export const NewsCategoriesPage = () => { - const { pathname } = useLocation() - const code = pathname.split('/')[2] + const parameters = useParams() const loaderData = useRouteLoaderData('routes/_news') - const { name } = - loaderData?.categoriesData.find((item) => item.code === code) || {} + const { name, description } = + loaderData?.categoriesData.find((item) => item.code === parameters.code) || + {} const { items } = BERITA return ( @@ -20,7 +19,7 @@ export const NewsCategoriesPage = () => { diff --git a/app/routes/_news.category.$name.tsx b/app/routes/_news.category.$code.tsx similarity index 100% rename from app/routes/_news.category.$name.tsx rename to app/routes/_news.category.$code.tsx