From d885b3cf269a196b7733de0634ebce0eb1e4d246 Mon Sep 17 00:00:00 2001 From: Ardeman Date: Fri, 7 Mar 2025 14:40:04 +0800 Subject: [PATCH 1/4] feat: implement update news functionality with API integration and form handling --- app/apis/admin/create-news.ts | 5 +- app/apis/admin/update-news.ts | 36 ++++++++++++ app/pages/contents-form/index.tsx | 4 +- app/pages/dashboard-contents/index.tsx | 2 +- app/routes/actions.admin.contents.update.ts | 64 +++++++++++++++++++++ 5 files changed, 105 insertions(+), 6 deletions(-) create mode 100644 app/apis/admin/update-news.ts create mode 100644 app/routes/actions.admin.contents.update.ts diff --git a/app/apis/admin/create-news.ts b/app/apis/admin/create-news.ts index 6b93099..0f1ab84 100644 --- a/app/apis/admin/create-news.ts +++ b/app/apis/admin/create-news.ts @@ -21,12 +21,9 @@ export const createNewsRequest = async (parameters: TParameter) => { const transformedPayload = { ...restPayload, categories: categories.map((category) => category?.id), - tags: tags?.map((tag) => tag?.id), + tags: tags?.map((tag) => tag?.id) || [], live_at: new Date(payload?.live_at).toISOString(), } - if (transformedPayload.tags?.length === 0) { - delete transformedPayload.tags - } const { data } = await HttpServer({ accessToken }).post( '/api/news/create', transformedPayload, diff --git a/app/apis/admin/update-news.ts b/app/apis/admin/update-news.ts new file mode 100644 index 0000000..109b187 --- /dev/null +++ b/app/apis/admin/update-news.ts @@ -0,0 +1,36 @@ +import { z } from 'zod' + +import { HttpServer } from '~/libs/http-server' +import type { TContentSchema } from '~/pages/contents-form' + +const newsResponseSchema = z.object({ + data: z.object({ + Message: z.string(), + }), +}) + +type TParameter = { + accessToken: string + payload: TContentSchema +} + +export const updateNewsRequest = async (parameters: TParameter) => { + const { accessToken, payload } = parameters + try { + const { categories, tags, id, ...restPayload } = payload + const transformedPayload = { + ...restPayload, + categories: categories.map((category) => category?.id), + tags: tags?.map((tag) => tag?.id) || [], + live_at: new Date(payload?.live_at).toISOString(), + } + const { data } = await HttpServer({ accessToken }).put( + `/api/news/${id}/update`, + transformedPayload, + ) + return newsResponseSchema.parse(data) + } catch (error) { + // eslint-disable-next-line unicorn/no-useless-promise-resolve-reject + return Promise.reject(error) + } +} diff --git a/app/pages/contents-form/index.tsx b/app/pages/contents-form/index.tsx index e94cc7d..fc0ce44 100644 --- a/app/pages/contents-form/index.tsx +++ b/app/pages/contents-form/index.tsx @@ -15,6 +15,7 @@ import { TitleDashboard } from '~/components/ui/title-dashboard' import type { loader } from '~/routes/_admin.lg-admin' export const contentSchema = z.object({ + id: z.string().optional(), categories: z .array( z @@ -74,6 +75,7 @@ export const ContentsFormPage = (properties: TProperties) => { fetcher, resolver: zodResolver(contentSchema), values: { + id: newsData?.id || '', categories: newsData?.categories || [], tags: newsData?.tags || [], title: newsData?.title || '', @@ -110,7 +112,7 @@ export const ContentsFormPage = (properties: TProperties) => { {error && ( diff --git a/app/pages/dashboard-contents/index.tsx b/app/pages/dashboard-contents/index.tsx index 93b55a3..8c65ccb 100644 --- a/app/pages/dashboard-contents/index.tsx +++ b/app/pages/dashboard-contents/index.tsx @@ -32,7 +32,7 @@ export const ContentsPage = () => { }, }, { - title: 'Tanggal Konten', + title: 'Tanggal Live', data: 'live_at', }, { diff --git a/app/routes/actions.admin.contents.update.ts b/app/routes/actions.admin.contents.update.ts new file mode 100644 index 0000000..b0b5fe4 --- /dev/null +++ b/app/routes/actions.admin.contents.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 { updateNewsRequest } from '~/apis/admin/update-news' +import { handleCookie } from '~/libs/cookies' +import { contentSchema, type TContentSchema } from '~/pages/contents-form' + +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(contentSchema), + false, + ) + + if (errors) { + return data({ success: false, errors, defaultValues }, { status: 400 }) + } + + const { data: newsData } = await updateNewsRequest({ + accessToken: staffToken, + payload, + }) + + return data( + { + success: true, + newsData, + }, + { + 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 aa660f4f5fd8cb909044b97c45b8b51d0381bb52 Mon Sep 17 00:00:00 2001 From: Ardeman Date: Fri, 7 Mar 2025 14:43:12 +0800 Subject: [PATCH 2/4] feat: sort news data by publication date and update column title for clarity --- app/pages/dashboard-contents/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/pages/dashboard-contents/index.tsx b/app/pages/dashboard-contents/index.tsx index 8c65ccb..16e1f52 100644 --- a/app/pages/dashboard-contents/index.tsx +++ b/app/pages/dashboard-contents/index.tsx @@ -18,7 +18,9 @@ export const ContentsPage = () => { const newsData = loaderData?.newsData DataTable.use(DT) - const dataTable = newsData + const dataTable = newsData?.sort( + (a, b) => new Date(b.live_at).getTime() - new Date(a.live_at).getTime(), + ) const dataColumns = [ { title: 'No', @@ -36,7 +38,7 @@ export const ContentsPage = () => { data: 'live_at', }, { - title: 'Nama Penulis', + title: 'Penulis', }, { title: 'Judul', data: 'title' }, { From 72e2dac328563cff62f100fe851f8c3b7237aabe Mon Sep 17 00:00:00 2001 From: Ardeman Date: Fri, 7 Mar 2025 14:58:35 +0800 Subject: [PATCH 3/4] feat: refactor category routes and components for improved organization and clarity --- app/pages/dashboard-categories/index.tsx | 36 +++++++++---------- ....lg-admin._dashboard.categories._index.tsx | 5 +-- ....lg-admin._dashboard.categories.create.tsx | 4 +++ ...min._dashboard.categories.update.$slug.tsx | 4 +++ ...in.lg-admin._dashboard.category.create.tsx | 4 --- ...admin._dashboard.category.update.$slug.tsx | 4 --- ....ts => actions.admin.categories.create.ts} | 0 7 files changed, 29 insertions(+), 28 deletions(-) create mode 100644 app/routes/_admin.lg-admin._dashboard.categories.create.tsx create mode 100644 app/routes/_admin.lg-admin._dashboard.categories.update.$slug.tsx delete mode 100644 app/routes/_admin.lg-admin._dashboard.category.create.tsx delete mode 100644 app/routes/_admin.lg-admin._dashboard.category.update.$slug.tsx rename app/routes/{actions.admin.category.create.ts => actions.admin.categories.create.ts} (100%) diff --git a/app/pages/dashboard-categories/index.tsx b/app/pages/dashboard-categories/index.tsx index e2c92a1..1f2f8cf 100644 --- a/app/pages/dashboard-categories/index.tsx +++ b/app/pages/dashboard-categories/index.tsx @@ -1,5 +1,5 @@ -import DataTable from 'datatables.net-dt' -import DT from 'datatables.net-react' +import DT from 'datatables.net-dt' +import DataTable from 'datatables.net-react' import { Link, useRouteLoaderData } from 'react-router' import { Button } from '~/components/ui/button' @@ -10,9 +10,10 @@ export const CategoriesPage = () => { const loaderData = useRouteLoaderData( 'routes/_admin.lg-admin._dashboard.categories._index', ) - const dataTable = loaderData?.dataCategories + const categoriesData = loaderData?.dataCategories DataTable.use(DT) + const dataTable = categoriesData const dataColumns = [ { title: 'No', @@ -38,7 +39,18 @@ export const CategoriesPage = () => { data: 'id', }, ] - + const dataSlot = { + 3: (value: string) => ( + + ), + } const dataOptions = { paging: true, searching: true, @@ -46,18 +58,6 @@ export const CategoriesPage = () => { info: true, } - const dataSlot = { - 3: (value: string) => ( - - ), - } return (
@@ -65,11 +65,11 @@ export const CategoriesPage = () => {
{/* TODO: Filter */}
diff --git a/app/routes/_admin.lg-admin._dashboard.categories._index.tsx b/app/routes/_admin.lg-admin._dashboard.categories._index.tsx index e2b970e..0a82712 100644 --- a/app/routes/_admin.lg-admin._dashboard.categories._index.tsx +++ b/app/routes/_admin.lg-admin._dashboard.categories._index.tsx @@ -11,5 +11,6 @@ export const loader = async ({ request }: Route.LoaderArgs) => { }) return { dataCategories } } -const DashboardCategoriesLayout = () => -export default DashboardCategoriesLayout + +const DashboardCategoriesIndexLayout = () => +export default DashboardCategoriesIndexLayout diff --git a/app/routes/_admin.lg-admin._dashboard.categories.create.tsx b/app/routes/_admin.lg-admin._dashboard.categories.create.tsx new file mode 100644 index 0000000..39b04d5 --- /dev/null +++ b/app/routes/_admin.lg-admin._dashboard.categories.create.tsx @@ -0,0 +1,4 @@ +import { CreateCategoryPage } from '~/pages/dashboard-category-create' + +const DashboardCategoriesCreateLayout = () => +export default DashboardCategoriesCreateLayout diff --git a/app/routes/_admin.lg-admin._dashboard.categories.update.$slug.tsx b/app/routes/_admin.lg-admin._dashboard.categories.update.$slug.tsx new file mode 100644 index 0000000..fd8f151 --- /dev/null +++ b/app/routes/_admin.lg-admin._dashboard.categories.update.$slug.tsx @@ -0,0 +1,4 @@ +import { UpdateCategoryPage } from '~/pages/dashboard-category-update' + +const DashboardCategoriesUpdateLayout = () => +export default DashboardCategoriesUpdateLayout diff --git a/app/routes/_admin.lg-admin._dashboard.category.create.tsx b/app/routes/_admin.lg-admin._dashboard.category.create.tsx deleted file mode 100644 index 5f475e0..0000000 --- a/app/routes/_admin.lg-admin._dashboard.category.create.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import { CreateCategoryPage } from '~/pages/dashboard-category-create' - -const DashboardCategoryLayout = () => -export default DashboardCategoryLayout diff --git a/app/routes/_admin.lg-admin._dashboard.category.update.$slug.tsx b/app/routes/_admin.lg-admin._dashboard.category.update.$slug.tsx deleted file mode 100644 index 2da33fd..0000000 --- a/app/routes/_admin.lg-admin._dashboard.category.update.$slug.tsx +++ /dev/null @@ -1,4 +0,0 @@ -import { UpdateCategoryPage } from '~/pages/dashboard-category-update' - -const DashboardCategoryUpdateLayout = () => -export default DashboardCategoryUpdateLayout diff --git a/app/routes/actions.admin.category.create.ts b/app/routes/actions.admin.categories.create.ts similarity index 100% rename from app/routes/actions.admin.category.create.ts rename to app/routes/actions.admin.categories.create.ts From 40a3f33ea3cfee3bd4993ce613118214b772736d Mon Sep 17 00:00:00 2001 From: Ardeman Date: Fri, 7 Mar 2025 15:04:22 +0800 Subject: [PATCH 4/4] feat: update category menu and dashboard titles for consistency and clarity --- app/layouts/admin/menu.ts | 14 ++++++-------- app/pages/dashboard-categories/index.tsx | 6 +++--- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/app/layouts/admin/menu.ts b/app/layouts/admin/menu.ts index 66f37bb..c0f5825 100644 --- a/app/layouts/admin/menu.ts +++ b/app/layouts/admin/menu.ts @@ -1,4 +1,5 @@ -import type { JSX, SVGProps } from 'react' +import { ClipboardDocumentCheckIcon } from '@heroicons/react/20/solid' +import type { SVGProps } from 'react' import { ChartIcon } from '~/components/icons/chart' import { ChatIcon } from '~/components/icons/chat' @@ -6,16 +7,13 @@ import { DocumentIcon } from '~/components/icons/document' import { MedicalNotesIcon } from '~/components/icons/medical-notes' import { ProfileIcon } from '~/components/icons/profile' import { SettingIcon } from '~/components/icons/setting' -import { WalletIcon } from '~/components/icons/wallet' type TMenu = { group: string items: { title: string url: string - icon: ( - properties: JSX.IntrinsicAttributes & SVGProps, - ) => JSX.Element + icon: React.ComponentType> }[] } @@ -54,9 +52,9 @@ export const MENU: TMenu[] = [ group: 'Others', items: [ { - title: 'Data Situs', - url: '/lg-admin/site-data', - icon: WalletIcon, + title: 'Kategori', + url: '/lg-admin/categories', + icon: ClipboardDocumentCheckIcon, }, { title: 'Pengaturan', diff --git a/app/pages/dashboard-categories/index.tsx b/app/pages/dashboard-categories/index.tsx index 1f2f8cf..f3ee82e 100644 --- a/app/pages/dashboard-categories/index.tsx +++ b/app/pages/dashboard-categories/index.tsx @@ -27,11 +27,11 @@ export const CategoriesPage = () => { }, }, { - title: 'Nama Kategori', + title: 'Nama', data: 'name', }, { - title: 'Code Katgeori', + title: 'Kode', data: 'code', }, { @@ -78,7 +78,7 @@ export const CategoriesPage = () => { columns={dataColumns} options={dataOptions} slots={dataSlot} - title="Daftar Katgeori" + title="Daftar Kategori" /> )