From 2c703de8e5ac2e8f92c0ba144668ba10e80e360f Mon Sep 17 00:00:00 2001 From: Ardeman Date: Thu, 13 Mar 2025 09:05:29 +0800 Subject: [PATCH] feat: implement delete tag functionality with confirmation dialog --- app/apis/admin/delete-tags.ts | 7 +- app/pages/dashboard-tags/dialog-delete.tsx | 85 +++++++++++++++++++++ app/pages/dashboard-tags/index.tsx | 46 ++++++++--- app/routes/actions.admin.tags.delete.$id.ts | 48 ++++++++++++ 4 files changed, 171 insertions(+), 15 deletions(-) create mode 100644 app/pages/dashboard-tags/dialog-delete.tsx create mode 100644 app/routes/actions.admin.tags.delete.$id.ts diff --git a/app/apis/admin/delete-tags.ts b/app/apis/admin/delete-tags.ts index 51223dc..151554f 100644 --- a/app/apis/admin/delete-tags.ts +++ b/app/apis/admin/delete-tags.ts @@ -9,15 +9,12 @@ const deleteTagsResponseSchema = z.object({ }), }) -type TTagsId = Pick type TParameters = { - payload: TTagsId + id: TTagSchema['id'] } & THttpServer -export type TDeleteTagsResponse = z.infer export const deleteTagsRequest = async (parameters: TParameters) => { - const { payload, ...restParameters } = parameters - const { id } = payload + const { id, ...restParameters } = parameters try { const { data } = await HttpServer(restParameters).delete( `/api/tag/${id}/delete`, diff --git a/app/pages/dashboard-tags/dialog-delete.tsx b/app/pages/dashboard-tags/dialog-delete.tsx new file mode 100644 index 0000000..67aa496 --- /dev/null +++ b/app/pages/dashboard-tags/dialog-delete.tsx @@ -0,0 +1,85 @@ +import { + Description, + Dialog, + DialogBackdrop, + DialogPanel, + DialogTitle, +} from '@headlessui/react' +import { useEffect, type Dispatch, type SetStateAction } from 'react' +import toast from 'react-hot-toast' +import { useFetcher } from 'react-router' + +import type { TTagResponse } from '~/apis/common/get-tags' +import { Button } from '~/components/ui/button' + +type TProperties = { + selectedItem?: TTagResponse + setSelectedItem: Dispatch> +} + +export const DialogDelete = (properties: TProperties) => { + const { selectedItem, setSelectedItem } = properties || {} + const fetcher = useFetcher() + + useEffect(() => { + if (fetcher.data?.success === false) { + toast.error(fetcher.data?.message) + return + } + + if (fetcher.data?.success === true) { + setSelectedItem(undefined) + toast.success('Tag berhasil dihapus!') + return + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [fetcher.data]) + + return ( + { + if (fetcher.state === 'idle') { + setSelectedItem(undefined) + } + }} + className="relative z-50" + transition + > + +
+ + + Anda akan menghapus tag berikut? + + +

{selectedItem?.name}

+
+
+ + + +
+
+
+
+ ) +} diff --git a/app/pages/dashboard-tags/index.tsx b/app/pages/dashboard-tags/index.tsx index 82e5785..9ea3b9a 100644 --- a/app/pages/dashboard-tags/index.tsx +++ b/app/pages/dashboard-tags/index.tsx @@ -1,16 +1,26 @@ +import { + PencilSquareIcon, + PlusIcon, + TrashIcon, +} from '@heroicons/react/20/solid' import DT, { type Config, type ConfigColumns } from 'datatables.net-dt' import DataTable, { type DataTableSlots } from 'datatables.net-react' +import { useState } from 'react' import { Link, useRouteLoaderData } from 'react-router' +import type { TTagResponse } from '~/apis/common/get-tags' import { Button } from '~/components/ui/button' import { UiTable } from '~/components/ui/table' import { TitleDashboard } from '~/components/ui/title-dashboard' import type { loader } from '~/routes/_admin.lg-admin._dashboard' +import { DialogDelete } from './dialog-delete' + export const TagsPage = () => { const loaderData = useRouteLoaderData( 'routes/_admin.lg-admin._dashboard', ) + const [selectedTag, setSelectedTag] = useState() const { tagsData: dataTable } = loaderData || {} DataTable.use(DT) @@ -40,15 +50,26 @@ export const TagsPage = () => { }, ] const dataSlot: DataTableSlots = { - 3: (value: string) => ( - + 3: (value: string, _type: unknown, data: TTagResponse) => ( +
+ + +
), } const dataOptions: Config = { @@ -69,7 +90,7 @@ export const TagsPage = () => { size="lg" className="text-md h-[42px] px-4" > - Buat Tag + Buat Tag @@ -80,6 +101,11 @@ export const TagsPage = () => { slots={dataSlot} title="Daftar Tags" /> + + ) } diff --git a/app/routes/actions.admin.tags.delete.$id.ts b/app/routes/actions.admin.tags.delete.$id.ts new file mode 100644 index 0000000..e113cdc --- /dev/null +++ b/app/routes/actions.admin.tags.delete.$id.ts @@ -0,0 +1,48 @@ +import { data } from 'react-router' +import { XiorError } from 'xior' + +import { deleteTagsRequest } from '~/apis/admin/delete-tags' +import { handleCookie } from '~/libs/cookies' + +import type { Route } from './+types/actions.admin.advertisements.create' + +export const action = async ({ request, params }: Route.ActionArgs) => { + const { staffToken: accessToken } = await handleCookie(request) + const { id } = params + try { + const { data: tagData } = await deleteTagsRequest({ + accessToken, + id, + }) + + 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 }, + ) + } +}