feat: implement toast notifications for delete actions in dashboard pages

This commit is contained in:
Ardeman 2025-03-12 21:19:41 +08:00
parent 36b22d3f4a
commit 7ab83a4f66
7 changed files with 114 additions and 19 deletions

View File

@ -11,6 +11,8 @@ const buttonVariants = cva(
variant: {
newsPrimary:
'bg-[#2E2F7C] text-white text-lg hover:bg-[#4C5CA0] hover:shadow transition active:bg-[#6970B4]',
newsDanger:
'bg-red-500 text-white text-lg hover:bg-red-600 hover:shadow transition active:bg-red-700',
newsPrimaryOutline:
'border-[3px] bg-[#2E2F7C] border-white text-white text-lg hover:bg-[#4C5CA0] hover:shadow-lg active:shadow-2xl transition active:bg-[#6970B4]',
newsSecondary:
@ -21,7 +23,7 @@ const buttonVariants = cva(
size: {
default: 'h-[50px] w-[150px]',
block: 'h-[50px] w-full',
icon: 'h-9 w-9',
icon: 'h-9 w-9 rounded-full',
sm: 'h-8 rounded-md px-3 text-xs',
lg: 'h-10 rounded-md px-8',
fit: 'w-fit',

View File

@ -1,7 +1,18 @@
import {
Description,
Dialog,
DialogBackdrop,
DialogPanel,
DialogTitle,
} from '@headlessui/react'
import { PencilSquareIcon, TrashIcon } from '@heroicons/react/20/solid'
import type { ConfigColumns } from 'datatables.net-dt'
import type { DataTableSlots } from 'datatables.net-react'
import { Link, useRouteLoaderData } from 'react-router'
import { useEffect, useState } from 'react'
import toast from 'react-hot-toast'
import { Link, useFetcher, useRouteLoaderData } from 'react-router'
import type { TAdResponse } from '~/apis/common/get-ads'
import { Button } from '~/components/ui/button'
import { UiTable } from '~/components/ui/table'
import { TitleDashboard } from '~/components/ui/title-dashboard'
@ -12,6 +23,22 @@ export const AdvertisementsPage = () => {
'routes/_admin.lg-admin._dashboard.advertisements._index',
)
const { adsData: dataTable } = loaderData || {}
const [selectedId, setSelectedId] = useState<TAdResponse>()
const fetcher = useFetcher()
useEffect(() => {
if (fetcher.data?.success === false) {
toast.error(fetcher.data?.message)
return
}
if (fetcher.data?.success === true) {
setSelectedId(undefined)
toast.success('Banner iklan berhasil dihapus!')
return
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fetcher.data])
const dataColumns: ConfigColumns[] = [
{
@ -42,15 +69,25 @@ export const AdvertisementsPage = () => {
/>
)
},
3: (value: string) => (
3: (value: string, _type: unknown, data: TAdResponse) => (
<div className="flex space-x-2">
<Button
as={Link}
to={`/lg-admin/advertisements/update/${value}`}
className="text-md rounded-md"
size="sm"
as="a"
href={`/lg-admin/advertisements/update/${value}`}
className=""
size="icon"
>
Update Banner Iklan
<PencilSquareIcon className="h-4 w-4" />
</Button>
<Button
type="button"
size="icon"
variant="newsDanger"
onClick={() => setSelectedId(data)}
>
<TrashIcon className="h-4 w-4" />
</Button>
</div>
),
}
@ -76,6 +113,60 @@ export const AdvertisementsPage = () => {
slots={dataSlot}
title="Daftar Banner Iklan"
/>
<Dialog
open={!!selectedId}
onClose={() => setSelectedId(undefined)}
className="relative z-50"
transition
>
<DialogBackdrop
className="fixed inset-0 bg-black/50 duration-300 ease-out data-[closed]:opacity-0"
transition
/>
<div className="fixed inset-0 flex w-screen justify-center overflow-y-auto p-0 max-sm:bg-white sm:items-center sm:p-4">
<DialogPanel
transition
className="max-w-lg space-y-6 rounded-lg bg-white p-8 duration-300 ease-out data-[closed]:scale-95 data-[closed]:opacity-0 sm:shadow-lg"
>
<DialogTitle className="relative flex justify-start text-xl font-bold">
Anda akan menghapus banner berikut?
</DialogTitle>
<Description className="space-y-1 text-center text-[#565658]">
<img
src={selectedId?.image_url}
alt={selectedId?.image_url}
className="aspect-[150/1] h-[50px] rounded object-contain"
/>
<Button
as={Link}
to={selectedId?.url || ''}
variant="link"
size="fit"
>
{selectedId?.url}
</Button>
</Description>
<div className="flex justify-end">
<fetcher.Form
method="POST"
action={`/actions/admin/advertisements/delete/${selectedId?.id}`}
className="grid"
>
<Button
type="submit"
variant="newsDanger"
className="text-md h-[42px] rounded-md"
disabled={fetcher.state !== 'idle'}
isLoading={fetcher.state !== 'idle'}
>
Hapus
</Button>
</fetcher.Form>
</div>
</DialogPanel>
</div>
</Dialog>
</div>
)
}

View File

@ -56,8 +56,8 @@ export const CategoriesPage = () => {
),
3: (value: string) => (
<Button
as={Link}
to={`/lg-admin/categories/update/${value}`}
as="a"
href={`/lg-admin/categories/update/${value}`}
className="text-md rounded-md"
size="sm"
>

View File

@ -83,8 +83,8 @@ export const ContentsPage = () => {
),
7: (value: string) => (
<Button
as={Link}
to={`/lg-admin/contents/update/${encodeURIComponent(value)}`}
as="a"
href={`/lg-admin/contents/update/${encodeURIComponent(value)}`}
className="text-md rounded-md"
size="sm"
>

View File

@ -67,8 +67,8 @@ export const SubscribePlanPage = () => {
),
6: (value: string) => (
<Button
as={Link}
to={`/lg-admin/subscribe-plan/update/${value}`}
as="a"
href={`/lg-admin/subscribe-plan/update/${value}`}
className="text-md rounded-md"
size="sm"
>

View File

@ -42,8 +42,8 @@ export const TagsPage = () => {
const dataSlot: DataTableSlots = {
3: (value: string) => (
<Button
as={Link}
to={`/lg-admin/tags/update/${value}`}
as="a"
href={`/lg-admin/tags/update/${value}`}
className="text-md rounded-md"
size="sm"
>

View File

@ -42,11 +42,13 @@ export const FormTagPage = (properties: TProperties) => {
useEffect(() => {
if (fetcher.data?.success === false) {
toast.error(fetcher.data?.message)
return
}
if (fetcher.data?.success === true) {
toast.success(`Tag berhasil ${tagData ? 'diupdate' : 'dibuat'}!`)
navigate('/lg-admin/tags')
return
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fetcher.data])