diff --git a/src/app/(private)/admin-registration-applications/[id]/page.tsx b/src/app/(private)/admin-registration-applications/[id]/page.tsx index 6f5eb78a..704e2d18 100644 --- a/src/app/(private)/admin-registration-applications/[id]/page.tsx +++ b/src/app/(private)/admin-registration-applications/[id]/page.tsx @@ -64,7 +64,7 @@ const Page = ({ params }: { params: { slug: string; id: string } }) => { } fetchDetails() - }, [params.id]) + }, [params.id, t, toast]) return (
diff --git a/src/app/(private)/role-listing/page.tsx b/src/app/(private)/role-listing/page.tsx new file mode 100644 index 00000000..e2779fcf --- /dev/null +++ b/src/app/(private)/role-listing/page.tsx @@ -0,0 +1,93 @@ +'use client' + +import { useEffect, useMemo, useState } from 'react' +import PrivateRoute from '@/app/hocs/isAuth' +import { useTranslation } from 'react-i18next' +import { useToast } from '@/components/ui/use-toast' +import { useSearchParams } from 'next/navigation' +import { Permission } from '@/constants/permissions' +import { postRoleListing } from '@/modules/roleListing/service' +import { searchParamsSchema } from '@/modules/roleListing/constants/searchParamsSchema' +import { RoleListing } from '@/modules/roleListing/constants/types' +import { useDataTable } from '@/app/hocs/useDataTable' +import { DataTable, DataTableToolbar } from '@/components/dataTable' +import { columns } from '@/modules/roleListing/components/columns' +import filterFields from '@/modules/roleListing/constants/filterFields' + +const Page = () => { + const searchParams = useSearchParams() + const search = searchParamsSchema.parse( + Object.fromEntries(searchParams.entries()), + ) + + const { t } = useTranslation() + const { toast } = useToast() + const [data, setData] = useState({ + content: [], + totalPageCount: 0, + }) + const [isLoading, setIsLoading] = useState(false) + const searchParamsString = useMemo(() => JSON.stringify(search), [search]) + + useEffect(() => { + setIsLoading(true) + postRoleListing({ + page: search.page, + per_page: search.per_page, + sort: search.sort, + status: search.status, + name: search.name, + createdAt: search.createdAt, + updatedAt: search.updatedAt, + }) + .then((responseData) => { + setData(responseData.data.response) + }) + .catch(() => { + toast({ + title: t('error'), + description: t('defaultError'), + variant: 'destructive', + }) + }) + .finally(() => setIsLoading(false)) + }, [ + searchParamsString, + search.createdAt, + search.name, + search.page, + search.per_page, + search.sort, + search.status, + search.updatedAt, + t, + toast, + ]) + + const { table } = useDataTable({ + data: data.content, + columns, + pageCount: data.totalPageCount, + filterFields, + }) + + return ( + +
+ +
+

{t('roleListing')}

+ +
+
+
+
+ ) +} + +export default Page diff --git a/src/app/(public)/login/page.tsx b/src/app/(public)/login/page.tsx index ddad3f6a..4d64e0bc 100644 --- a/src/app/(public)/login/page.tsx +++ b/src/app/(public)/login/page.tsx @@ -93,7 +93,7 @@ const Page = () => { if (tokenInfo) { router.push('/dashboard') } - }, [tokenInfo]) + }, [tokenInfo, router]) return tokenInfo ? ( <> diff --git a/src/components/dataTable/dataTableSearchField.tsx b/src/components/dataTable/dataTableSearchField.tsx index 4753d138..a699e2ad 100644 --- a/src/components/dataTable/dataTableSearchField.tsx +++ b/src/components/dataTable/dataTableSearchField.tsx @@ -21,6 +21,7 @@ const DataTableSearchField = ({ table, }: DataTableSearchFieldProps) => { const schema = getValidationSchema(field.value) + const handleInputChange = (event: React.ChangeEvent) => { const inputValue = event.target.value const validation = schema.safeParse(inputValue) diff --git a/src/components/dataTable/dataTableSort.tsx b/src/components/dataTable/dataTableSort.tsx index 3536f4d6..693b688c 100644 --- a/src/components/dataTable/dataTableSort.tsx +++ b/src/components/dataTable/dataTableSort.tsx @@ -5,11 +5,10 @@ import { TooltipProvider, TooltipTrigger, } from '@/components/ui/tooltip' -import i18next from 'i18next' import i18n from 'i18next' import { BiSort, BiSortDown, BiSortUp } from 'react-icons/bi' -const DataTableSort = ({ column }: { column: any }) => { +const DataTableSort = ({ column, label }: { column: any, label: string }) => { return ( @@ -18,7 +17,7 @@ const DataTableSort = ({ column }: { column: any }) => { onClick={() => column.toggleSorting()} > - {i18next.t('createdAt')} + {label} diff --git a/src/components/ui/filterInput.tsx b/src/components/ui/filterInput.tsx index 789ac08f..755cdbbe 100644 --- a/src/components/ui/filterInput.tsx +++ b/src/components/ui/filterInput.tsx @@ -8,7 +8,15 @@ import { z } from 'zod' import { toast } from '@/components/ui/use-toast' import { cn } from '@/lib/utils' -const FilterInput = ({ param, min = 2, max = 100 }: { param: string, min?: number, max?: number }) => { +const FilterInput = ({ + param, + min = 2, + max = 100, +}: { + param: string + min?: number + max?: number +}) => { const searchParams = useSearchParams() const router = useRouter() const pathname = usePathname() @@ -16,7 +24,10 @@ const FilterInput = ({ param, min = 2, max = 100 }: { param: string, min?: numbe const [search, setSearch] = useState(initialValue || '') const debouncedSearch = useDebounce(search, 500) - const schema = z.string().min(min, { message: i18next.t('minLength', { field: min }) }).max(max, { message: i18next.t('maxLength', { field: max }) }) + const schema = z + .string() + .min(min, { message: i18next.t('minLength', { field: min }) }) + .max(max, { message: i18next.t('maxLength', { field: max }) }) useEffect(() => { const newSearchParams = new URLSearchParams() @@ -24,19 +35,29 @@ const FilterInput = ({ param, min = 2, max = 100 }: { param: string, min?: numbe const validation = schema.safeParse(debouncedSearch) if (validation.success) { newSearchParams.set(param, debouncedSearch) - router.replace(`${pathname}?${newSearchParams.toString()}`, { scroll: false }) + router.replace(`${pathname}?${newSearchParams.toString()}`, { + scroll: false, + }) } else { - toast({ title: validation.error.errors[0].message, variant: 'destructive' }) + toast({ + title: validation.error.errors[0].message, + variant: 'destructive', + }) } } else if (!debouncedSearch && initialValue) { newSearchParams.delete(param) - router.replace(`${pathname}?${newSearchParams.toString()}`, { scroll: false }) + router.replace(`${pathname}?${newSearchParams.toString()}`, { + scroll: false, + }) } - }, [debouncedSearch]) + }, [debouncedSearch, initialValue, param, pathname, router, schema]) - const handleInputChange = useCallback((e: React.ChangeEvent) => { - setSearch(e.currentTarget.value) - }, []) + const handleInputChange = useCallback( + (e: React.ChangeEvent) => { + setSearch(e.currentTarget.value) + }, + [], + ) return (
@@ -45,7 +66,9 @@ const FilterInput = ({ param, min = 2, max = 100 }: { param: string, min?: numbe placeholder="" value={search} onChange={handleInputChange} - className={cn('block focus-visible:ring-0 focus-visible:ring-offset-0 p-3 w-full text-sm text-gray-900 bg-transparent rounded-lg border-[2px] border-gray-200 appearance-none dark:text-white dark:border-gray-600 dark:focus:border-blue-500 focus:outline-none focus:ring-0 focus:border-blue-600 peer')} + className={cn( + 'block focus-visible:ring-0 focus-visible:ring-offset-0 p-3 w-full text-sm text-gray-900 bg-transparent rounded-lg border-[2px] border-gray-200 appearance-none dark:text-white dark:border-gray-600 dark:focus:border-blue-500 focus:outline-none focus:ring-0 focus:border-blue-600 peer', + )} />