diff --git a/components/dashboard/src/Pagination/Pagination.tsx b/components/dashboard/src/Pagination/Pagination.tsx
new file mode 100644
index 00000000000000..ed0659f1a9abb6
--- /dev/null
+++ b/components/dashboard/src/Pagination/Pagination.tsx
@@ -0,0 +1,70 @@
+/**
+ * Copyright (c) 2022 Gitpod GmbH. All rights reserved.
+ * Licensed under the GNU Affero General Public License (AGPL).
+ * See License-AGPL.txt in the project root for license information.
+ */
+
+import { getPaginationNumbers } from "./getPagination";
+import Arrow from "../components/Arrow";
+
+interface PaginationProps {
+ totalResults: number;
+ totalNumberOfPages: number;
+ currentPage: number;
+ setCurrentPage: any;
+}
+
+function Pagination({ totalNumberOfPages, currentPage, setCurrentPage }: PaginationProps) {
+ const calculatedPagination = getPaginationNumbers(totalNumberOfPages, currentPage);
+
+ const nextPage = () => {
+ if (currentPage !== totalNumberOfPages) setCurrentPage(currentPage + 1);
+ };
+ const prevPage = () => {
+ if (currentPage !== 1) setCurrentPage(currentPage - 1);
+ };
+ const getClassnames = (pageNumber: string | number) => {
+ if (pageNumber === currentPage) {
+ return "text-gray-500 w-8 text-center rounded-md hover:bg-gray-50 bg-gray-100 disabled pointer-events-none";
+ }
+ if (pageNumber === "...") {
+ return "text-gray-500 w-8 text-center rounded-md hover:bg-gray-50 disabled pointer-events-none";
+ }
+ return "text-gray-500 w-8 text-center rounded-md hover:bg-gray-50 cursor-pointer";
+ };
+
+ return (
+
+ );
+}
+
+export default Pagination;
diff --git a/components/dashboard/src/Pagination/getPagination.spec.ts b/components/dashboard/src/Pagination/getPagination.spec.ts
new file mode 100644
index 00000000000000..cf63c6506c9eeb
--- /dev/null
+++ b/components/dashboard/src/Pagination/getPagination.spec.ts
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) 2022 Gitpod GmbH. All rights reserved.
+ * Licensed under the GNU Affero General Public License (AGPL).
+ * See License-AGPL.txt in the project root for license information.
+ */
+
+import { getPaginationNumbers } from "./getPagination";
+
+test("getPagination", () => {
+ const totalNumberOfPages = 15;
+ const currentPage = 1;
+
+ expect(getPaginationNumbers(totalNumberOfPages, currentPage)).toStrictEqual([1, 2, 3, "...", 15]);
+
+ expect(getPaginationNumbers(37, 4)).toStrictEqual([1, "...", 3, 4, 5, "...", 37]);
+
+ expect(getPaginationNumbers(28, 7)).toStrictEqual([1, "...", 6, 7, 8, "...", 28]);
+
+ expect(getPaginationNumbers(5, 1)).toStrictEqual([1, 2, 3, 4, 5]);
+});
diff --git a/components/dashboard/src/Pagination/getPagination.ts b/components/dashboard/src/Pagination/getPagination.ts
new file mode 100644
index 00000000000000..6834c00cc02a36
--- /dev/null
+++ b/components/dashboard/src/Pagination/getPagination.ts
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2022 Gitpod GmbH. All rights reserved.
+ * Licensed under the GNU Affero General Public License (AGPL).
+ * See License-AGPL.txt in the project root for license information.
+ */
+
+export function getPaginationNumbers(totalNumberOfPages: number, currentPage: number) {
+ const adjacentToCurrentPage = 1; // This is the number(s) we see next to the currentPage
+ const totalNumbersShownInPagination = 6;
+ let calculatedPagination: number[] = [];
+
+ const pageNumbersAsArray = (startRange: number, endRange: number) => {
+ return [...Array(endRange + 1).keys()].slice(startRange);
+ };
+
+ const minimumAmountInBetweenToShowEllipsis = 2;
+ // Without ellipsis aka normal case
+ if (totalNumberOfPages <= totalNumbersShownInPagination) {
+ return (calculatedPagination = pageNumbersAsArray(1, totalNumberOfPages));
+ }
+
+ // Otherwise, we show the ellipses
+ const toTheRightOfCurrent = Math.min(currentPage + adjacentToCurrentPage, totalNumberOfPages);
+ const toTheLeftOfCurrent = Math.max(currentPage - adjacentToCurrentPage, 1);
+
+ const showRightEllipsis = toTheRightOfCurrent < totalNumberOfPages - minimumAmountInBetweenToShowEllipsis; // e.g. "1 2 3 ... 7"
+ const showLeftEllipsis = toTheLeftOfCurrent > minimumAmountInBetweenToShowEllipsis; // e.g. 1 ... 5 6 7"
+
+ if (showRightEllipsis && !showLeftEllipsis) {
+ let leftSideNumbers = 3;
+ let leftPageNumbersAsArray = pageNumbersAsArray(1, leftSideNumbers);
+ return [...leftPageNumbersAsArray, "...", totalNumberOfPages];
+ }
+
+ if (showLeftEllipsis && !showRightEllipsis) {
+ let rightSideNumbers = 3;
+ let rightPageNumbersAsArray = pageNumbersAsArray(totalNumberOfPages - rightSideNumbers, totalNumberOfPages);
+ return [1, "...", ...rightPageNumbersAsArray];
+ }
+
+ if (showRightEllipsis && showLeftEllipsis) {
+ let middleNumbers = pageNumbersAsArray(toTheLeftOfCurrent, toTheRightOfCurrent);
+ return [1, "...", ...middleNumbers, "...", totalNumberOfPages];
+ }
+
+ return calculatedPagination;
+}
diff --git a/components/dashboard/src/components/Pagination.tsx b/components/dashboard/src/components/Pagination.tsx
deleted file mode 100644
index 69177626ab9850..00000000000000
--- a/components/dashboard/src/components/Pagination.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
- * Copyright (c) 2022 Gitpod GmbH. All rights reserved.
- * Licensed under the GNU Affero General Public License (AGPL).
- * See License-AGPL.txt in the project root for license information.
- */
-
-import Arrow from "./Arrow";
-
-function Pagination(props: { numberOfPages: number; currentPage: number; setCurrentPage: any }) {
- const { numberOfPages, currentPage, setCurrentPage } = props;
- const availablePageNumbers = [...Array(numberOfPages + 1).keys()].slice(1);
-
- const nextPage = () => {
- if (currentPage !== numberOfPages) setCurrentPage(currentPage + 1);
- };
- const prevPage = () => {
- if (currentPage !== 1) setCurrentPage(currentPage - 1);
- };
-
- return (
-
- );
-}
-
-export default Pagination;
diff --git a/components/dashboard/src/teams/TeamUsage.tsx b/components/dashboard/src/teams/TeamUsage.tsx
index 66913ea289a09c..a12b094edc0a98 100644
--- a/components/dashboard/src/teams/TeamUsage.tsx
+++ b/components/dashboard/src/teams/TeamUsage.tsx
@@ -17,7 +17,7 @@ import {
import { AttributionId } from "@gitpod/gitpod-protocol/lib/attribution";
import { Item, ItemField, ItemsList } from "../components/ItemsList";
import moment from "moment";
-import Pagination from "../components/Pagination";
+import Pagination from "../Pagination/Pagination";
import Header from "../components/Header";
import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
import { FeatureFlagContext } from "../contexts/FeatureFlagContext";
@@ -32,7 +32,7 @@ function TeamUsage() {
const team = getCurrentTeam(location, teams);
const [billedUsage, setBilledUsage] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
- const [resultsPerPage] = useState(10);
+ const [resultsPerPage] = useState(15);
const [errorMessage, setErrorMessage] = useState("");
const today = new Date();
const startOfCurrentMonth = new Date(today.getFullYear(), today.getMonth(), 1);
@@ -126,7 +126,7 @@ function TeamUsage() {
const lastResultOnCurrentPage = currentPage * resultsPerPage;
const firstResultOnCurrentPage = lastResultOnCurrentPage - resultsPerPage;
- const numberOfPages = Math.ceil(billedUsage.length / resultsPerPage);
+ const totalNumberOfPages = Math.ceil(billedUsage.length / resultsPerPage);
const currentPaginatedResults = billedUsage.slice(firstResultOnCurrentPage, lastResultOnCurrentPage);
return (
@@ -236,9 +236,10 @@ function TeamUsage() {
{billedUsage.length > resultsPerPage && (
)}