diff --git a/src/app/(main)/admin/teams/AdminTeamsDataTable.tsx b/src/app/(main)/admin/teams/AdminTeamsDataTable.tsx new file mode 100644 index 00000000..53e18248 --- /dev/null +++ b/src/app/(main)/admin/teams/AdminTeamsDataTable.tsx @@ -0,0 +1,19 @@ +import { DataGrid } from '@/components/common/DataGrid'; +import { useTeamsQuery } from '@/components/hooks'; +import { AdminTeamsTable } from './AdminTeamsTable'; +import { ReactNode } from 'react'; + +export function AdminTeamsDataTable({ + showActions, +}: { + showActions?: boolean; + children?: ReactNode; +}) { + const queryResult = useTeamsQuery(); + + return ( + + {({ data }) => } + + ); +} diff --git a/src/app/(main)/admin/teams/AdminTeamsPage.tsx b/src/app/(main)/admin/teams/AdminTeamsPage.tsx new file mode 100644 index 00000000..37f43c94 --- /dev/null +++ b/src/app/(main)/admin/teams/AdminTeamsPage.tsx @@ -0,0 +1,16 @@ +'use client'; +import { AdminTeamsDataTable } from './AdminTeamsDataTable'; +import { Column } from '@umami/react-zen'; +import { SectionHeader } from '@/components/common/SectionHeader'; +import { useMessages } from '@/components/hooks'; + +export function AdminTeamsPage() { + const { formatMessage, labels } = useMessages(); + + return ( + + + + + ); +} diff --git a/src/app/(main)/admin/teams/AdminTeamsTable.tsx b/src/app/(main)/admin/teams/AdminTeamsTable.tsx new file mode 100644 index 00000000..6582cf38 --- /dev/null +++ b/src/app/(main)/admin/teams/AdminTeamsTable.tsx @@ -0,0 +1,78 @@ +import { useState } from 'react'; +import { Row, Text, Icon, DataTable, DataColumn, MenuItem, Modal } from '@umami/react-zen'; +import Link from 'next/link'; +import { Trash } from '@/components/icons'; +import { useMessages } from '@/components/hooks'; +import { Edit } from '@/components/icons'; +import { MenuButton } from '@/components/input/MenuButton'; +import { DateDistance } from '@/components/common/DateDistance'; + +export function AdminTeamsTable({ + data = [], + showActions = true, +}: { + data: any[]; + showActions?: boolean; +}) { + const { formatMessage, labels } = useMessages(); + const [deleteUser, setDeleteUser] = useState(null); + + return ( + <> + + + {(row: any) => {row.name}} + + + {(row: any) => row?._count?.teamUser} + + + {(row: any) => row?._count?.website} + + + {(row: any) => ( + + {row?.teamUser?.[0]?.user?.username} + + )} + + + {(row: any) => } + + {showActions && ( + + {(row: any) => { + const { id } = row; + + return ( + + + + + + + {formatMessage(labels.edit)} + + + setDeleteUser(row)} + data-test="link-button-delete" + > + + + + + {formatMessage(labels.delete)} + + + + ); + }} + + )} + + + + ); +} diff --git a/src/app/(main)/admin/teams/[teamId]/AdminTeamPage.tsx b/src/app/(main)/admin/teams/[teamId]/AdminTeamPage.tsx new file mode 100644 index 00000000..3edd1ed1 --- /dev/null +++ b/src/app/(main)/admin/teams/[teamId]/AdminTeamPage.tsx @@ -0,0 +1,10 @@ +'use client'; +import { TeamDetails } from '@/app/(main)/teams/[teamId]/settings/team/TeamDetails'; + +export function AdminTeamPage({ teamId }: { teamId: string }) { + return ( + <> + + + ); +} diff --git a/src/app/(main)/admin/teams/[teamId]/page.tsx b/src/app/(main)/admin/teams/[teamId]/page.tsx new file mode 100644 index 00000000..0dc9d538 --- /dev/null +++ b/src/app/(main)/admin/teams/[teamId]/page.tsx @@ -0,0 +1,17 @@ +import { AdminTeamPage } from './AdminTeamPage'; +import { TeamProvider } from '@/app/(main)/teams/[teamId]/TeamProvider'; +import { Metadata } from 'next'; + +export default async function ({ params }: { params: Promise<{ teamId: string }> }) { + const { teamId } = await params; + + return ( + + + + ); +} + +export const metadata: Metadata = { + title: 'Team', +}; diff --git a/src/app/(main)/admin/teams/page.tsx b/src/app/(main)/admin/teams/page.tsx new file mode 100644 index 00000000..fb0fd342 --- /dev/null +++ b/src/app/(main)/admin/teams/page.tsx @@ -0,0 +1,9 @@ +import { Metadata } from 'next'; +import { AdminTeamsPage } from './AdminTeamsPage'; + +export default function () { + return ; +} +export const metadata: Metadata = { + title: 'Teams', +}; diff --git a/src/app/(main)/admin/users/UsersDataTable.tsx b/src/app/(main)/admin/users/UsersDataTable.tsx index d0cc5ec8..ae72cfed 100644 --- a/src/app/(main)/admin/users/UsersDataTable.tsx +++ b/src/app/(main)/admin/users/UsersDataTable.tsx @@ -7,7 +7,7 @@ export function UsersDataTable({ showActions }: { showActions?: boolean; childre const queryResult = useUsersQuery(); return ( - + {({ data }) => } ); diff --git a/src/app/(main)/admin/users/UsersSettingsPage.tsx b/src/app/(main)/admin/users/UsersPage.tsx similarity index 93% rename from src/app/(main)/admin/users/UsersSettingsPage.tsx rename to src/app/(main)/admin/users/UsersPage.tsx index b2e53dac..5c8a1ae3 100644 --- a/src/app/(main)/admin/users/UsersSettingsPage.tsx +++ b/src/app/(main)/admin/users/UsersPage.tsx @@ -5,7 +5,7 @@ import { SectionHeader } from '@/components/common/SectionHeader'; import { useMessages } from '@/components/hooks'; import { UserAddButton } from './UserAddButton'; -export function UsersSettingsPage() { +export function UsersPage() { const { formatMessage, labels } = useMessages(); const handleSave = () => {}; diff --git a/src/app/(main)/admin/users/UsersTable.tsx b/src/app/(main)/admin/users/UsersTable.tsx index ffa0ec33..a78e66f2 100644 --- a/src/app/(main)/admin/users/UsersTable.tsx +++ b/src/app/(main)/admin/users/UsersTable.tsx @@ -1,22 +1,13 @@ import { useState } from 'react'; -import { - Row, - Text, - Icon, - DataTable, - DataColumn, - MenuItem, - MenuSeparator, - Modal, -} from '@umami/react-zen'; +import { Row, Text, Icon, DataTable, DataColumn, MenuItem, Modal } from '@umami/react-zen'; import Link from 'next/link'; -import { formatDistance } from 'date-fns'; import { ROLES } from '@/lib/constants'; import { Trash } from '@/components/icons'; -import { useMessages, useLocale } from '@/components/hooks'; +import { useMessages } from '@/components/hooks'; import { Edit } from '@/components/icons'; import { MenuButton } from '@/components/input/MenuButton'; import { UserDeleteForm } from './UserDeleteForm'; +import { DateDistance } from '@/components/common/DateDistance'; export function UsersTable({ data = [], @@ -26,7 +17,6 @@ export function UsersTable({ showActions?: boolean; }) { const { formatMessage, labels } = useMessages(); - const { dateLocale } = useLocale(); const [deleteUser, setDeleteUser] = useState(null); return ( @@ -42,17 +32,12 @@ export function UsersTable({ ) } - - {(row: any) => - formatDistance(new Date(row.createdAt), new Date(), { - addSuffix: true, - locale: dateLocale, - }) - } - - + {(row: any) => row._count.websiteUser} + + {(row: any) => } + {showActions && ( {(row: any) => { @@ -68,7 +53,6 @@ export function UsersTable({ {formatMessage(labels.edit)} - setDeleteUser(row)} diff --git a/src/app/(main)/admin/users/[userId]/UserWebsites.tsx b/src/app/(main)/admin/users/[userId]/UserWebsites.tsx index 63a06867..c3e4960e 100644 --- a/src/app/(main)/admin/users/[userId]/UserWebsites.tsx +++ b/src/app/(main)/admin/users/[userId]/UserWebsites.tsx @@ -1,12 +1,12 @@ import { DataGrid } from '@/components/common/DataGrid'; -import { useWebsitesQuery } from '@/components/hooks'; +import { useUserWebsitesQuery } from '@/components/hooks'; import { WebsitesTable } from '@/app/(main)/settings/websites/WebsitesTable'; export function UserWebsites({ userId }) { - const queryResult = useWebsitesQuery({ userId }); + const queryResult = useUserWebsitesQuery({ userId }); return ( - + {({ data }) => ( )} diff --git a/src/app/(main)/admin/users/[userId]/page.tsx b/src/app/(main)/admin/users/[userId]/page.tsx index 47bd3531..5d0eea99 100644 --- a/src/app/(main)/admin/users/[userId]/page.tsx +++ b/src/app/(main)/admin/users/[userId]/page.tsx @@ -8,5 +8,5 @@ export default async function ({ params }: { params: Promise<{ userId: string }> } export const metadata: Metadata = { - title: 'User Settings', + title: 'Users', }; diff --git a/src/app/(main)/admin/users/page.tsx b/src/app/(main)/admin/users/page.tsx index 311e4cb2..7cfd4887 100644 --- a/src/app/(main)/admin/users/page.tsx +++ b/src/app/(main)/admin/users/page.tsx @@ -1,8 +1,8 @@ import { Metadata } from 'next'; -import { UsersSettingsPage } from './UsersSettingsPage'; +import { UsersPage } from './UsersPage'; export default function () { - return ; + return ; } export const metadata: Metadata = { title: 'Users', diff --git a/src/app/(main)/admin/websites/AdminWebsitesDataTable.tsx b/src/app/(main)/admin/websites/AdminWebsitesDataTable.tsx new file mode 100644 index 00000000..21059924 --- /dev/null +++ b/src/app/(main)/admin/websites/AdminWebsitesDataTable.tsx @@ -0,0 +1,13 @@ +import { DataGrid } from '@/components/common/DataGrid'; +import { useWebsitesQuery } from '@/components/hooks'; +import { AdminWebsitesTable } from './AdminWebsitesTable'; + +export function AdminWebsitesDataTable() { + const query = useWebsitesQuery(); + + return ( + + {props => } + + ); +} diff --git a/src/app/(main)/admin/websites/AdminWebsitesPage.tsx b/src/app/(main)/admin/websites/AdminWebsitesPage.tsx new file mode 100644 index 00000000..07a73a40 --- /dev/null +++ b/src/app/(main)/admin/websites/AdminWebsitesPage.tsx @@ -0,0 +1,16 @@ +'use client'; +import { AdminWebsitesDataTable } from './AdminWebsitesDataTable'; +import { Column } from '@umami/react-zen'; +import { SectionHeader } from '@/components/common/SectionHeader'; +import { useMessages } from '@/components/hooks'; + +export function AdminWebsitesPage() { + const { formatMessage, labels } = useMessages(); + + return ( + + + + + ); +} diff --git a/src/app/(main)/admin/websites/AdminWebsitesTable.tsx b/src/app/(main)/admin/websites/AdminWebsitesTable.tsx new file mode 100644 index 00000000..99105bc3 --- /dev/null +++ b/src/app/(main)/admin/websites/AdminWebsitesTable.tsx @@ -0,0 +1,85 @@ +import { useState } from 'react'; +import Link from 'next/link'; +import { Row, Text, Icon, DataTable, DataColumn, MenuItem, Modal } from '@umami/react-zen'; +import { Trash, Users } from '@/components/icons'; +import { useMessages } from '@/components/hooks'; +import { Edit } from '@/components/icons'; +import { MenuButton } from '@/components/input/MenuButton'; +import { DateDistance } from '@/components/common/DateDistance'; + +export function AdminWebsitesTable({ data = [] }: { data: any[] }) { + const { formatMessage, labels } = useMessages(); + const [deleteUser, setDeleteUser] = useState(null); + + return ( + <> + + + {(row: any) => ( + + {row.name} + + )} + + + {(row: any) => {row.domain}} + + + {(row: any) => { + if (row?.team) { + return ( + + + + + + {row?.team?.name} + + + ); + } + return ( + + {row?.user?.username} + + ); + }} + + + {(row: any) => } + + + {(row: any) => { + const { id } = row; + + return ( + + + + + + + {formatMessage(labels.edit)} + + + setDeleteUser(row)} + data-test="link-button-delete" + > + + + + + {formatMessage(labels.delete)} + + + + ); + }} + + + + + ); +} diff --git a/src/app/(main)/admin/websites/page.tsx b/src/app/(main)/admin/websites/page.tsx new file mode 100644 index 00000000..85eb029a --- /dev/null +++ b/src/app/(main)/admin/websites/page.tsx @@ -0,0 +1,9 @@ +import { Metadata } from 'next'; +import { AdminWebsitesPage } from './AdminWebsitesPage'; + +export default function () { + return ; +} +export const metadata: Metadata = { + title: 'Websites', +}; diff --git a/src/app/(main)/settings/teams/TeamsDataTable.tsx b/src/app/(main)/settings/teams/TeamsDataTable.tsx index 309faca6..093917a0 100644 --- a/src/app/(main)/settings/teams/TeamsDataTable.tsx +++ b/src/app/(main)/settings/teams/TeamsDataTable.tsx @@ -1,6 +1,6 @@ import { DataGrid } from '@/components/common/DataGrid'; import { TeamsTable } from '@/app/(main)/settings/teams/TeamsTable'; -import { useLoginQuery, useTeamsQuery } from '@/components/hooks'; +import { useLoginQuery, useUserTeamsQuery } from '@/components/hooks'; import { ReactNode } from 'react'; export function TeamsDataTable({ @@ -13,10 +13,10 @@ export function TeamsDataTable({ children?: ReactNode; }) { const { user } = useLoginQuery(); - const queryResult = useTeamsQuery(user.id); + const queryResult = useUserTeamsQuery(user.id); return ( - children}> + children}> {({ data }) => { return ; }} diff --git a/src/app/(main)/settings/websites/WebsitesDataTable.tsx b/src/app/(main)/settings/websites/WebsitesDataTable.tsx index 042c0923..5015264a 100644 --- a/src/app/(main)/settings/websites/WebsitesDataTable.tsx +++ b/src/app/(main)/settings/websites/WebsitesDataTable.tsx @@ -1,6 +1,6 @@ import { WebsitesTable } from './WebsitesTable'; import { DataGrid } from '@/components/common/DataGrid'; -import { useWebsitesQuery } from '@/components/hooks'; +import { useUserWebsitesQuery } from '@/components/hooks'; export function WebsitesDataTable({ teamId, @@ -13,10 +13,10 @@ export function WebsitesDataTable({ allowView?: boolean; showActions?: boolean; }) { - const queryResult = useWebsitesQuery({ teamId }); + const queryResult = useUserWebsitesQuery({ teamId }); return ( - + {({ data }) => ( + data.filter(({ teamUser }) => teamUser.find( ({ role, userId }) => [ROLES.teamOwner, ROLES.teamManager].includes(role) && userId === user.id, @@ -31,7 +31,7 @@ export function WebsiteData({ websiteId, onSave }: { websiteId: string; onSave?: ) ).length > 0 || (teamId && - !!result?.data + !!data ?.find(({ id }) => id === teamId) ?.teamUser.find(({ role, userId }) => role === ROLES.teamOwner && userId === user.id)); diff --git a/src/app/(main)/settings/websites/[websiteId]/WebsiteTransferForm.tsx b/src/app/(main)/settings/websites/[websiteId]/WebsiteTransferForm.tsx index 610f3268..c101d4ba 100644 --- a/src/app/(main)/settings/websites/[websiteId]/WebsiteTransferForm.tsx +++ b/src/app/(main)/settings/websites/[websiteId]/WebsiteTransferForm.tsx @@ -10,7 +10,7 @@ import { ListItem, Text, } from '@umami/react-zen'; -import { useApi, useLoginQuery, useMessages, useTeamsQuery } from '@/components/hooks'; +import { useApi, useLoginQuery, useMessages, useUserTeamsQuery } from '@/components/hooks'; import { WebsiteContext } from '@/app/(main)/websites/[websiteId]/WebsiteProvider'; import { ROLES } from '@/lib/constants'; @@ -31,7 +31,7 @@ export function WebsiteTransferForm({ const { mutate, error } = useMutation({ mutationFn: (data: any) => post(`/websites/${websiteId}/transfer`, data), }); - const { result, query } = useTeamsQuery(user.id); + const { result, query } = useUserTeamsQuery(user.id); const isTeamWebsite = !!website?.teamId; const items = result.data.filter(({ teamUser }) => diff --git a/src/app/(main)/teams/[teamId]/settings/members/TeamMembersDataTable.tsx b/src/app/(main)/teams/[teamId]/settings/members/TeamMembersDataTable.tsx index 32dd6c4a..25ccbab3 100644 --- a/src/app/(main)/teams/[teamId]/settings/members/TeamMembersDataTable.tsx +++ b/src/app/(main)/teams/[teamId]/settings/members/TeamMembersDataTable.tsx @@ -12,7 +12,7 @@ export function TeamMembersDataTable({ const queryResult = useTeamMembersQuery(teamId); return ( - + {({ data }) => } ); diff --git a/src/app/(main)/teams/[teamId]/settings/team/TeamEditForm.tsx b/src/app/(main)/teams/[teamId]/settings/team/TeamEditForm.tsx index a902e159..8ea3ddb9 100644 --- a/src/app/(main)/teams/[teamId]/settings/team/TeamEditForm.tsx +++ b/src/app/(main)/teams/[teamId]/settings/team/TeamEditForm.tsx @@ -48,7 +48,7 @@ export function TeamEditForm({ teamId, allowEdit }: { teamId: string; allowEdit? rules={{ required: formatMessage(labels.required) }} > {allowEdit && } - {!allowEdit && team.name} + {!allowEdit && team?.name} {!cloudMode && allowEdit && ( diff --git a/src/app/(main)/teams/[teamId]/settings/websites/TeamWebsitesDataTable.tsx b/src/app/(main)/teams/[teamId]/settings/websites/TeamWebsitesDataTable.tsx index bc7623de..1cb65eac 100644 --- a/src/app/(main)/teams/[teamId]/settings/websites/TeamWebsitesDataTable.tsx +++ b/src/app/(main)/teams/[teamId]/settings/websites/TeamWebsitesDataTable.tsx @@ -12,7 +12,7 @@ export function TeamWebsitesDataTable({ const queryResult = useTeamWebsitesQuery(teamId); return ( - + {({ data }) => } ); diff --git a/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx b/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx index 70c0ca56..f73ec442 100644 --- a/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx +++ b/src/app/(main)/websites/[websiteId]/events/EventsDataTable.tsx @@ -37,7 +37,7 @@ export function EventsDataTable({ return ( + {({ data }) => { return ; }} diff --git a/src/app/api/admin/teams/route.ts b/src/app/api/admin/teams/route.ts new file mode 100644 index 00000000..6b162050 --- /dev/null +++ b/src/app/api/admin/teams/route.ts @@ -0,0 +1,54 @@ +import { z } from 'zod'; +import { parseRequest } from '@/lib/request'; +import { json, unauthorized } from '@/lib/response'; +import { pagingParams, searchParams } from '@/lib/schema'; +import { canViewAllTeams } from '@/lib/auth'; +import { getTeams } from '@/queries/prisma/team'; + +export async function GET(request: Request) { + const schema = z.object({ + ...pagingParams, + ...searchParams, + }); + + const { auth, query, error } = await parseRequest(request, schema); + + if (error) { + return error(); + } + + if (!(await canViewAllTeams(auth))) { + return unauthorized(); + } + + const teams = await getTeams( + { + include: { + _count: { + select: { + teamUser: true, + website: true, + }, + }, + teamUser: { + select: { + user: { + omit: { + password: true, + }, + }, + }, + where: { + role: 'team-owner', + }, + }, + }, + orderBy: { + createdAt: 'desc', + }, + }, + query, + ); + + return json(teams); +} diff --git a/src/app/api/admin/users/route.ts b/src/app/api/admin/users/route.ts index 2185e03e..3cf116f7 100644 --- a/src/app/api/admin/users/route.ts +++ b/src/app/api/admin/users/route.ts @@ -1,13 +1,14 @@ import { z } from 'zod'; import { parseRequest } from '@/lib/request'; import { json, unauthorized } from '@/lib/response'; -import { pagingParams } from '@/lib/schema'; +import { pagingParams, searchParams } from '@/lib/schema'; import { canViewUsers } from '@/lib/auth'; import { getUsers } from '@/queries/prisma/user'; export async function GET(request: Request) { const schema = z.object({ ...pagingParams, + ...searchParams, }); const { auth, query, error } = await parseRequest(request, schema); @@ -31,6 +32,9 @@ export async function GET(request: Request) { }, }, }, + orderBy: { + createdAt: 'desc', + }, }, query, ); diff --git a/src/app/api/admin/websites/route.ts b/src/app/api/admin/websites/route.ts index 3f35ea49..2b82fbe8 100644 --- a/src/app/api/admin/websites/route.ts +++ b/src/app/api/admin/websites/route.ts @@ -1,17 +1,15 @@ import { z } from 'zod'; import { parseRequest } from '@/lib/request'; import { json, unauthorized } from '@/lib/response'; -import { pagingParams } from '@/lib/schema'; +import { pagingParams, searchParams } from '@/lib/schema'; import { canViewAllWebsites } from '@/lib/auth'; import { getWebsites } from '@/queries/prisma/website'; import { ROLES } from '@/lib/constants'; export async function GET(request: Request) { const schema = z.object({ - userId: z.string().uuid(), - includeOwnedTeams: z.string().optional(), - includeAllTeams: z.string().optional(), ...pagingParams, + ...searchParams, }); const { auth, query, error } = await parseRequest(request, schema); @@ -24,46 +22,13 @@ export async function GET(request: Request) { return unauthorized(); } - const { userId, includeOwnedTeams, includeAllTeams } = query; - const websites = await getWebsites( { - where: { - OR: [ - ...(userId && [{ userId }]), - ...(userId && includeOwnedTeams - ? [ - { - team: { - deletedAt: null, - teamUser: { - some: { - role: ROLES.teamOwner, - userId, - }, - }, - }, - }, - ] - : []), - ...(userId && includeAllTeams - ? [ - { - team: { - deletedAt: null, - teamUser: { - some: { - userId, - }, - }, - }, - }, - ] - : []), - ], - }, include: { user: { + where: { + deletedAt: null, + }, select: { username: true, id: true, @@ -82,6 +47,9 @@ export async function GET(request: Request) { }, }, }, + orderBy: { + createdAt: 'desc', + }, }, query, ); diff --git a/src/components/common/DataGrid.tsx b/src/components/common/DataGrid.tsx index 22344875..82c2bfdb 100644 --- a/src/components/common/DataGrid.tsx +++ b/src/components/common/DataGrid.tsx @@ -1,13 +1,15 @@ -import { ReactNode, useState } from 'react'; +import { ReactNode, useState, useCallback } from 'react'; import { SearchField, Row, Column } from '@umami/react-zen'; +import { UseQueryResult } from '@tanstack/react-query'; import { useMessages, useNavigation } from '@/components/hooks'; import { Pager } from '@/components/common/Pager'; import { LoadingPanel } from '@/components/common/LoadingPanel'; +import { PageResult } from '@/lib/types'; const DEFAULT_SEARCH_DELAY = 600; export interface DataGridProps { - queryResult: any; + query: UseQueryResult, any>; searchDelay?: number; allowSearch?: boolean; allowPaging?: boolean; @@ -17,7 +19,7 @@ export interface DataGridProps { } export function DataGrid({ - queryResult, + query, searchDelay = 600, allowSearch, allowPaging = true, @@ -26,20 +28,23 @@ export function DataGrid({ children, }: DataGridProps) { const { formatMessage, labels } = useMessages(); - const { data, error, isLoading, isFetching, setParams } = queryResult; - const { router, updateParams } = useNavigation(); - const [search, setSearch] = useState(''); + const { data, error, isLoading, isFetching } = query; + const { router, updateParams, query: queryParams } = useNavigation(); + const [search, setSearch] = useState(queryParams?.saerch || data?.search || ''); - const handleSearch = (search: string) => { - setSearch(search); - setParams(params => ({ ...params, search })); - router.push(updateParams({ search })); + const handleSearch = (value: string) => { + if (value !== search) { + setSearch(value); + router.push(updateParams({ search: value, page: 1 })); + } }; - const handlePageChange = (page: number) => { - setParams(params => ({ ...params, page })); - router.push(updateParams({ page })); - }; + const handlePageChange = useCallback( + (page: number) => { + router.push(updateParams({ search, page })); + }, + [search], + ); return ( diff --git a/src/components/common/LinkButton.tsx b/src/components/common/LinkButton.tsx index ca1baccb..69a025d1 100644 --- a/src/components/common/LinkButton.tsx +++ b/src/components/common/LinkButton.tsx @@ -17,13 +17,20 @@ export function LinkButton({ scroll = true, target, children, + isDisabled, ...props }: LinkButtonProps) { const { dir } = useLocale(); return ( - diff --git a/src/components/common/Pager.tsx b/src/components/common/Pager.tsx index 3f1802dd..31ff8aa5 100644 --- a/src/components/common/Pager.tsx +++ b/src/components/common/Pager.tsx @@ -34,9 +34,14 @@ export function Pager({ page, pageSize, count, onPageChange }: PagerProps) { return ( - {formatMessage(labels.numberOfRecords, { x: count })} + {formatMessage(labels.numberOfRecords, { x: count.toLocaleString() })} - {formatMessage(labels.pageOf, { current: page, total: maxPage })} + + {formatMessage(labels.pageOf, { + current: page.toLocaleString(), + total: maxPage.toLocaleString(), + })} +