diff --git a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/components/table/logs-table.tsx b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/components/table/logs-table.tsx index a8f8a7672b..a1ade1bd2c 100644 --- a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/components/table/logs-table.tsx +++ b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/components/table/logs-table.tsx @@ -1,6 +1,7 @@ "use client"; import { VirtualTable } from "@/components/virtual-table/index"; import type { Column } from "@/components/virtual-table/types"; +import { formatNumberFull } from "@/lib/fmt"; import { shortenId } from "@/lib/shorten-id"; import { trpc } from "@/lib/trpc/client"; import { cn } from "@/lib/utils"; @@ -449,9 +450,10 @@ export const KeyDetailsLogsTable = ({ keyspaceId, keyId, selectedLog, onLogSelec hide: isLoading, countInfoText: (
- Showing {historicalLogs.length} + Showing{" "} + {formatNumberFull(historicalLogs.length)} of - {totalCount} + {formatNumberFull(totalCount)} requests
), diff --git a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/table/keys-list.tsx b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/table/keys-list.tsx index 5bdfa1f9ed..ac96d9ada6 100644 --- a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/table/keys-list.tsx +++ b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/table/keys-list.tsx @@ -1,6 +1,7 @@ "use client"; import { VirtualTable } from "@/components/virtual-table/index"; import type { Column } from "@/components/virtual-table/types"; +import { formatNumberFull } from "@/lib/fmt"; import { shortenId } from "@/lib/shorten-id"; import type { KeyDetails } from "@/lib/trpc/routers/api/keys/query-api-keys/schema"; import { BookBookmark, Dots, Focus, Key } from "@unkey/icons"; @@ -25,10 +26,20 @@ import { StatusDisplay } from "./components/status-cell"; import { useKeysListQuery } from "./hooks/use-keys-list-query"; import { getRowClassName } from "./utils/get-row-class"; +const SKELETON_COLUMN_KEYS = [ + "select", + "key", + "value", + "usage", + "last_used", + "status", + "action", +] as const; + const KeysTableActionPopover = dynamic( () => import("./components/actions/keys-table-action.popover.constants").then( - (mod) => mod.KeysTableActions, + (mod) => mod.KeysTableActions ), { ssr: false, @@ -37,13 +48,16 @@ const KeysTableActionPopover = dynamic( type="button" className={cn( "group-data-[state=open]:bg-gray-6 group-hover:bg-gray-6 group size-5 p-0 rounded m-0 items-center flex justify-center", - "border border-gray-6 group-hover:border-gray-8 ring-2 ring-transparent focus-visible:ring-gray-7 focus-visible:border-gray-7", + "border border-gray-6 group-hover:border-gray-8 ring-2 ring-transparent focus-visible:ring-gray-7 focus-visible:border-gray-7" )} > - + ), - }, + } ); export const KeysList = ({ @@ -53,9 +67,10 @@ export const KeysList = ({ keyspaceId: string; apiId: string; }) => { - const { keys, isLoading, isLoadingMore, loadMore, totalCount, hasMore } = useKeysListQuery({ - keyAuthId: keyspaceId, - }); + const { keys, isLoading, isLoadingMore, loadMore, totalCount, hasMore } = + useKeysListQuery({ + keyAuthId: keyspaceId, + }); const [selectedKey, setSelectedKey] = useState(null); const [navigatingKeyId, setNavigatingKeyId] = useState(null); // Add state for selected keys @@ -98,13 +113,17 @@ export const KeysList = ({ className={cn( "size-5 rounded flex items-center justify-center cursor-pointer", identity ? "bg-successA-3" : "bg-grayA-3", - isSelected && "bg-brand-5", + isSelected && "bg-brand-5" )} onMouseEnter={() => setHoveredKeyId(key.id)} onMouseLeave={() => setHoveredKeyId(null)} > {isNavigating ? ( -
+
) : ( @@ -155,17 +174,24 @@ export const KeysList = ({ rel="noopener noreferrer" aria-disabled={isNavigating} > - {identity} + + {identity} + ) : ( - {identity} + + {identity} + )} } asChild > {React.cloneElement(iconContainer, { - className: cn(iconContainer.props.className, "cursor-pointer"), + className: cn( + iconContainer.props.className, + "cursor-pointer" + ), })} ) : ( @@ -205,7 +231,11 @@ export const KeysList = ({ header: "Value", width: "15%", render: (key) => ( - + ), }, { @@ -266,7 +296,7 @@ export const KeysList = ({ selectedKeys, toggleSelection, hoveredKeyId, - ], + ] ); const getSelectedKeysState = useCallback(() => { @@ -291,17 +321,15 @@ export const KeysList = ({ // Early exit if we already found a mix if (!allEnabled && !allDisabled) { - break; + return "mixed"; } } if (allEnabled) { return "all-enabled"; } - if (allDisabled) { - return "all-disabled"; - } - return "mixed"; + + return "all-disabled"; }, [selectedKeys, keys]); return ( @@ -330,9 +358,12 @@ export const KeysList = ({ ), countInfoText: (
- Showing {keys.length} + Showing{" "} + + {formatNumberFull(keys.length)} + of - {totalCount} + {formatNumberFull(totalCount)} keys
), @@ -343,8 +374,8 @@ export const KeysList = ({ No API Keys Found - There are no API keys associated with this service yet. Create your first API key to - get started. + There are no API keys associated with this service yet. Create + your first API key to get started. @@ -384,9 +415,11 @@ export const KeysList = ({ {column.key === "last_used" && } {column.key === "status" && } {column.key === "action" && } - {!["select", "key", "value", "usage", "last_used", "status", "action"].includes( - column.key, - ) &&
} + {!SKELETON_COLUMN_KEYS.includes( + column.key as (typeof SKELETON_COLUMN_KEYS)[number] + ) && ( +
+ )} )) } diff --git a/apps/dashboard/app/(app)/apis/_components/api-list-client.tsx b/apps/dashboard/app/(app)/apis/_components/api-list-client.tsx index 2256810f9d..111056236a 100644 --- a/apps/dashboard/app/(app)/apis/_components/api-list-client.tsx +++ b/apps/dashboard/app/(app)/apis/_components/api-list-client.tsx @@ -1,6 +1,7 @@ "use client"; import { EmptyComponentSpacer } from "@/components/empty-component-spacer"; +import { formatNumberFull } from "@/lib/fmt"; import { trpc } from "@/lib/trpc/client"; import { BookBookmark } from "@unkey/icons"; import { Button, Empty } from "@unkey/ui"; @@ -81,7 +82,8 @@ export const ApiListClient = () => {
- Showing {apiList.length} of {apisData?.pages[0]?.total || 0} APIs + Showing {formatNumberFull(apiList.length)} of{" "} + {formatNumberFull(apisData?.pages[0]?.total || 0)} APIs
{!isSearching && hasNextPage && ( diff --git a/apps/dashboard/app/(app)/apis/_components/api-list-grid.tsx b/apps/dashboard/app/(app)/apis/_components/api-list-grid.tsx index bc77345e80..1db12e3eac 100644 --- a/apps/dashboard/app/(app)/apis/_components/api-list-grid.tsx +++ b/apps/dashboard/app/(app)/apis/_components/api-list-grid.tsx @@ -1,4 +1,5 @@ import { EmptyComponentSpacer } from "@/components/empty-component-spacer"; +import { formatNumberFull } from "@/lib/fmt"; import type { ApiOverview } from "@/lib/trpc/routers/api/overview/query-overview/schemas"; import { ChevronDown } from "@unkey/icons"; import { Button, Empty } from "@unkey/ui"; @@ -43,7 +44,7 @@ export const ApiListGrid = ({
- Showing {apiList.length} of {total} APIs + Showing {formatNumberFull(apiList.length)} of {formatNumberFull(total)} APIs
{!isSearching && hasMore && ( diff --git a/apps/dashboard/app/(app)/authorization/permissions/components/table/permissions-list.tsx b/apps/dashboard/app/(app)/authorization/permissions/components/table/permissions-list.tsx index 29c47a5175..8e3fcb8660 100644 --- a/apps/dashboard/app/(app)/authorization/permissions/components/table/permissions-list.tsx +++ b/apps/dashboard/app/(app)/authorization/permissions/components/table/permissions-list.tsx @@ -1,6 +1,7 @@ "use client"; import { VirtualTable } from "@/components/virtual-table/index"; import type { Column } from "@/components/virtual-table/types"; +import { formatNumberFull } from "@/lib/fmt"; import type { Permission } from "@/lib/trpc/routers/authorization/permissions/query"; import { BookBookmark, Page2 } from "@unkey/icons"; import { Button, Checkbox, Empty } from "@unkey/ui"; @@ -182,9 +183,10 @@ export const PermissionsList = () => { ), countInfoText: (
- Showing {permissions.length} + Showing{" "} + {formatNumberFull(permissions.length)} of - {totalCount} + {formatNumberFull(totalCount)} permissions
), diff --git a/apps/dashboard/app/(app)/authorization/roles/components/table/roles-list.tsx b/apps/dashboard/app/(app)/authorization/roles/components/table/roles-list.tsx index fb4056b34c..d64360a3e0 100644 --- a/apps/dashboard/app/(app)/authorization/roles/components/table/roles-list.tsx +++ b/apps/dashboard/app/(app)/authorization/roles/components/table/roles-list.tsx @@ -1,6 +1,7 @@ "use client"; import { VirtualTable } from "@/components/virtual-table/index"; import type { Column } from "@/components/virtual-table/types"; +import { formatNumberFull } from "@/lib/fmt"; import type { RoleBasic } from "@/lib/trpc/routers/authorization/roles/query"; import { BookBookmark, Tag } from "@unkey/icons"; import { Button, Checkbox, Empty } from "@unkey/ui"; @@ -166,9 +167,10 @@ export const RolesList = () => { ), countInfoText: (
- Showing {roles.length} + Showing{" "} + {formatNumberFull(roles.length)} of - {totalCount} + {formatNumberFull(totalCount)} roles
), diff --git a/apps/dashboard/app/(app)/logs/components/table/logs-table.tsx b/apps/dashboard/app/(app)/logs/components/table/logs-table.tsx index 3c89367479..897576b783 100644 --- a/apps/dashboard/app/(app)/logs/components/table/logs-table.tsx +++ b/apps/dashboard/app/(app)/logs/components/table/logs-table.tsx @@ -2,6 +2,7 @@ import { VirtualTable } from "@/components/virtual-table/index"; import type { Column } from "@/components/virtual-table/types"; +import { formatNumberFull } from "@/lib/fmt"; import { cn } from "@/lib/utils"; import type { Log } from "@unkey/clickhouse/src/logs"; import { BookBookmark, TriangleWarning2 } from "@unkey/icons"; @@ -250,9 +251,10 @@ export const LogsTable = () => { hasMore, countInfoText: (
- Showing {historicalLogs.length} + Showing{" "} + {formatNumberFull(historicalLogs.length)} of - {total} + {formatNumberFull(total)} requests
), diff --git a/apps/dashboard/app/(app)/projects/[projectId]/deployments/components/table/deployments-list.tsx b/apps/dashboard/app/(app)/projects/[projectId]/deployments/components/table/deployments-list.tsx index baeb499a5d..afe222b257 100644 --- a/apps/dashboard/app/(app)/projects/[projectId]/deployments/components/table/deployments-list.tsx +++ b/apps/dashboard/app/(app)/projects/[projectId]/deployments/components/table/deployments-list.tsx @@ -2,7 +2,7 @@ import { VirtualTable } from "@/components/virtual-table/index"; import type { Column } from "@/components/virtual-table/types"; import { useIsMobile } from "@/hooks/use-mobile"; -import type { Deployment, Environment } from "@/lib/collections"; + import { shortenId } from "@/lib/shorten-id"; import { BookBookmark, Cloud, CodeBranch, Cube } from "@unkey/icons"; import { Button, Empty, TimestampInfo } from "@unkey/ui"; @@ -307,7 +307,7 @@ export const DeploymentsList = () => { onRowClick={setSelectedDeployment} selectedItem={selectedDeployment} keyExtractor={(deployment) => deployment.id} - rowClassName={(deployment) => getRowClassName(deployment, selectedDeployment?.deployment.id)} + emptyState={
diff --git a/apps/dashboard/app/(app)/projects/_components/list/index.tsx b/apps/dashboard/app/(app)/projects/_components/list/index.tsx index b4ee4ef028..b0d569ece7 100644 --- a/apps/dashboard/app/(app)/projects/_components/list/index.tsx +++ b/apps/dashboard/app/(app)/projects/_components/list/index.tsx @@ -1,5 +1,4 @@ -import { collection } from "@/lib/collections"; -import { ilike, useLiveQuery } from "@tanstack/react-db"; + import { BookBookmark, Dots } from "@unkey/icons"; import { Button, Empty } from "@unkey/ui"; import { useProjectsFilters } from "../hooks/use-projects-filters"; @@ -102,6 +101,6 @@ export const ProjectsList = () => { /> ))}
-
+ ); }; diff --git a/apps/dashboard/app/(app)/ratelimits/[namespaceId]/_overview/components/table/logs-table.tsx b/apps/dashboard/app/(app)/ratelimits/[namespaceId]/_overview/components/table/logs-table.tsx index 0f22c0c059..1accb4aeac 100644 --- a/apps/dashboard/app/(app)/ratelimits/[namespaceId]/_overview/components/table/logs-table.tsx +++ b/apps/dashboard/app/(app)/ratelimits/[namespaceId]/_overview/components/table/logs-table.tsx @@ -3,7 +3,7 @@ import { useSort } from "@/components/logs/hooks/use-sort"; import { VirtualTable } from "@/components/virtual-table/index"; import type { Column } from "@/components/virtual-table/types"; -import { formatNumber } from "@/lib/fmt"; +import { formatNumber, formatNumberFull } from "@/lib/fmt"; import { cn } from "@/lib/utils"; import type { RatelimitOverviewLog } from "@unkey/clickhouse/src/ratelimits"; import { Ban, BookBookmark } from "@unkey/icons"; @@ -218,8 +218,9 @@ export const RatelimitOverviewLogsTable = ({ hide: isLoading, countInfoText: (
- Showing {historicalLogs.length} - of {totalCount} + Showing{" "} + {formatNumberFull(historicalLogs.length)} + of {formatNumberFull(totalCount)} rate limit identifiers
), diff --git a/apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/table/logs-table.tsx b/apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/table/logs-table.tsx index 9e84754a6c..2e4684c039 100644 --- a/apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/table/logs-table.tsx +++ b/apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/components/table/logs-table.tsx @@ -3,6 +3,7 @@ import { safeParseJson } from "@/app/(app)/logs/utils"; import { VirtualTable } from "@/components/virtual-table/index"; import type { Column } from "@/components/virtual-table/types"; +import { formatNumberFull } from "@/lib/fmt"; import { cn } from "@/lib/utils"; import type { RatelimitLog } from "@unkey/clickhouse/src/ratelimits"; import { BookBookmark } from "@unkey/icons"; @@ -220,9 +221,10 @@ export const RatelimitLogsTable = () => { hide: isLoading, countInfoText: (
- Showing {historicalLogs.length} + Showing{" "} + {formatNumberFull(historicalLogs.length)} of - {totalCount} + {formatNumberFull(totalCount)} ratelimit requests
), diff --git a/apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx b/apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx index e2c51eed01..985448e544 100644 --- a/apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx +++ b/apps/dashboard/app/(app)/settings/root-keys/components/table/root-keys-list.tsx @@ -2,6 +2,7 @@ import { HiddenValueCell } from "@/app/(app)/apis/[apiId]/keys/[keyAuthId]/_components/components/table/components/hidden-value"; import { VirtualTable } from "@/components/virtual-table/index"; import type { Column } from "@/components/virtual-table/types"; +import { formatNumberFull } from "@/lib/fmt"; import type { RootKey } from "@/lib/trpc/routers/settings/root-keys/query"; import { cn } from "@/lib/utils"; import { BookBookmark, Dots, Key2 } from "@unkey/icons"; @@ -86,9 +87,10 @@ export const RootKeysList = () => { hasMore, countInfoText: (
- Showing {rootKeys.length} + Showing{" "} + {formatNumberFull(rootKeys.length)} of - {totalCount} + {formatNumberFull(totalCount)} root keys
), @@ -142,7 +144,13 @@ export const RootKeysList = () => { // Memoize the renderSkeletonRow function to prevent unnecessary re-renders const renderSkeletonRow = useCallback( - ({ columns, rowHeight }: { columns: Column[]; rowHeight: number }) => + ({ + columns, + rowHeight, + }: { + columns: Column[]; + rowHeight: number; + }) => columns.map((column) => (
@@ -119,10 +122,12 @@ export const LoadMoreFooter = ({
Viewing - {totalVisible} + {formatNumberFull(totalVisible)} of - {totalCount} + + {formatNumberFull(totalCount)} + {itemLabel}
)} diff --git a/apps/dashboard/lib/fmt.ts b/apps/dashboard/lib/fmt.ts index e159f17b6d..4dd53e4ac8 100644 --- a/apps/dashboard/lib/fmt.ts +++ b/apps/dashboard/lib/fmt.ts @@ -1,3 +1,10 @@ +const compactFormatter = new Intl.NumberFormat("en", { notation: "compact" }); +const fullFormatter = new Intl.NumberFormat("en-US"); + export function formatNumber(n: number): string { - return Intl.NumberFormat("en", { notation: "compact" }).format(n); + return compactFormatter.format(n); +} + +export function formatNumberFull(n: number): string { + return fullFormatter.format(n); }