diff --git a/src/app/api/sessions/active/route.ts b/src/app/api/sessions/active/route.ts index 49ec8713..50ec1ad4 100644 --- a/src/app/api/sessions/active/route.ts +++ b/src/app/api/sessions/active/route.ts @@ -1,6 +1,6 @@ import { NextResponse } from "next/server" import { requireAuthApi } from "@/components/auth/require-auth" -import { IJsonApiResponse, parseJsonApiQuery } from "@/lib/json-api" +import { getJsonApiSkip, getJsonApiSort, getJsonApiTake, IJsonApiResponse, parseJsonApiQuery } from "@/lib/json-api" import { prisma } from "@/lib/prisma" export async function GET(request: Request) { @@ -9,25 +9,31 @@ export async function GET(request: Request) { const { searchParams } = new URL(request.url) const query = parseJsonApiQuery(searchParams) - console.log(query) const activeSessions = await prisma.session.findMany({ where: { userId: session.user.id, }, + skip: getJsonApiSkip(query), + take: getJsonApiTake(query), + orderBy: getJsonApiSort(query), + }) + + const total = await prisma.session.count({ + where: { + userId: session.user.id, + }, }) const response: IJsonApiResponse<(typeof activeSessions)[number]> = { data: activeSessions, meta: { total: activeSessions.length, - page: 1, - perPage: 10, - totalPages: 1, + page: query.page, + perPage: query.perPage, + totalPages: Math.ceil(total / query.perPage), }, } - NextResponse.next - return NextResponse.json(response) } diff --git a/src/components/profile/sessions/sessions-table.tsx b/src/components/profile/sessions/sessions-table.tsx index 17afc71b..915ce317 100644 --- a/src/components/profile/sessions/sessions-table.tsx +++ b/src/components/profile/sessions/sessions-table.tsx @@ -40,6 +40,7 @@ export default function SessionsTable() { `/api/sessions/active?${jsonApiQuery({ page: currentPage, perPage: itemsPerPage, + sort: ["-lastUsedAt"], })}` ), () => { @@ -91,15 +92,15 @@ export default function SessionsTable() {
{sessions ? rows : skelRows} - {sessions && (sessions.meta.totalPages > 1 || itemsPerPageInitial !== itemsPerPage) && ( - - )} + 1 || itemsPerPageInitial !== itemsPerPage)} + currentNumberOfItems={sessions?.data?.length ?? 0} + currentPage={sessions?.meta.page} + totalPages={sessions?.meta.totalPages} + setCurrentPage={setCurrentPage} + itemsPerPage={sessions?.meta.perPage} + setItemsPerPage={setItemsPerPage} + /> Are you absolutely sure? diff --git a/src/components/ui/pagination.tsx b/src/components/ui/pagination.tsx index 89e25294..579780c3 100644 --- a/src/components/ui/pagination.tsx +++ b/src/components/ui/pagination.tsx @@ -1,89 +1,105 @@ +"use client" + import { Dispatch, SetStateAction } from "react" +import { logger } from "@/lib/logger" +import { cn } from "@/lib/utils" import { Button } from "./button" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select" import { Icons } from "../icons" interface PaginationProps { - currentPage: number - setCurrentPage: Dispatch> - totalPages: number + currentNumberOfItems?: number + currentPage?: number + setCurrentPage?: Dispatch> + totalPages?: number itemsPerPage?: number setItemsPerPage?: Dispatch> + show?: boolean } export default function Pagination({ + currentNumberOfItems, currentPage, setCurrentPage, totalPages, itemsPerPage, setItemsPerPage, + show = true, }: PaginationProps) { + //? If there are no items, and we're not on the first page, go to the first page + if (currentNumberOfItems === 0 && (currentPage ?? 1) > 1) { + logger.debug("Pagination: No items, going to first page") + setCurrentPage?.(1) + } + return ( -
-
- {itemsPerPage && setItemsPerPage && ( +
+
+
+ {itemsPerPage && setItemsPerPage && ( +
+

Rows per page

+ +
+ )} +
+ Page {currentPage} of {totalPages} +
-

Rows per page

- + Go to previous page + + + +
- )} -
- Page {currentPage} of {totalPages} -
-
- - - -
diff --git a/src/lib/json-api.ts b/src/lib/json-api.ts index 06b29349..ba18025a 100644 --- a/src/lib/json-api.ts +++ b/src/lib/json-api.ts @@ -76,7 +76,17 @@ export const jsonApiQuery = (query: IJsonApiQuery) => { return searchParams } -export const parseJsonApiQuery = (query: URLSearchParams | string): IJsonApiQuery => { +export const jsonApiDefaults = { + page: 1, + perPage: 10, +} + +export type IJsonApiQueryWithDefaults = IJsonApiQuery & typeof jsonApiDefaults + +export const parseJsonApiQuery = ( + query: URLSearchParams | string, + defaults = jsonApiDefaults +): IJsonApiQueryWithDefaults => { const searchParams = new URLSearchParams(query) const page = searchParams.get("page") @@ -87,8 +97,8 @@ export const parseJsonApiQuery = (query: URLSearchParams | string): IJsonApiQuer const fields = searchParams.get("fields") return { - page: page ? parseInt(page) : undefined, - perPage: perPage ? parseInt(perPage) : undefined, + page: page ? parseInt(page) : defaults.page, + perPage: perPage ? parseInt(perPage) : defaults.perPage, sort: sort ? sort.split(",") : undefined, filter: filter ? filter.split(",").map((filter) => { @@ -100,3 +110,21 @@ export const parseJsonApiQuery = (query: URLSearchParams | string): IJsonApiQuer fields: fields ? fields.split(",") : undefined, } } + +export const getJsonApiSkip = ({ page, perPage }: { page: number; perPage: number }) => { + return perPage * (page - 1) +} + +export const getJsonApiTake = ({ perPage }: { perPage: number }) => { + return perPage +} + +export const getJsonApiSort = ({ sort }: { sort?: string[] }) => { + if (!sort) return undefined + + return sort.map((sort) => { + const direction = sort[0] === "-" ? "desc" : "asc" + const field = sort.replace(/^-/, "") + return { [field]: direction } + }) +}