From 9662d16669189995661bb5a6901965b710d2b8b6 Mon Sep 17 00:00:00 2001 From: Chris Estreich Date: Sun, 18 May 2025 15:28:02 -0700 Subject: [PATCH] Better DataTable loader (#34) --- src/app/(authenticated)/usage/Developers.tsx | 100 ++++++------ src/app/(authenticated)/usage/Loader.tsx | 14 -- src/app/(authenticated)/usage/Models.tsx | 92 ++++++----- src/app/(authenticated)/usage/Tasks.tsx | 155 ++++++++++--------- 4 files changed, 188 insertions(+), 173 deletions(-) delete mode 100644 src/app/(authenticated)/usage/Loader.tsx diff --git a/src/app/(authenticated)/usage/Developers.tsx b/src/app/(authenticated)/usage/Developers.tsx index c9ba07cbe1..5157c4f1f6 100644 --- a/src/app/(authenticated)/usage/Developers.tsx +++ b/src/app/(authenticated)/usage/Developers.tsx @@ -1,14 +1,14 @@ +import { useMemo } from 'react'; import { useQuery } from '@tanstack/react-query'; import type { ColumnDef } from '@tanstack/react-table'; import { useAuth } from '@clerk/nextjs'; import { type DeveloperUsage, getDeveloperUsage } from '@/actions/analytics'; import { formatCurrency, formatNumber } from '@/lib/formatters'; -import { Button } from '@/components/ui'; +import { Button, Skeleton } from '@/components/ui'; import { DataTable } from '@/components/layout/DataTable'; import type { Filter } from './types'; -import { Loader } from './Loader'; export const Developers = ({ onFilter, @@ -23,50 +23,60 @@ export const Developers = ({ enabled: !!orgId, }); - const columns: ColumnDef[] = [ - { - header: 'Developer', - cell: ({ row }) => ( - - ), - }, - { - accessorKey: 'user.email', - header: 'Email', - }, - { - accessorKey: 'tasksStarted', - header: 'Tasks Started', - }, - { - accessorKey: 'tasksCompleted', - header: 'Tasks Completed', - }, - { - header: 'Tokens', - cell: ({ row }) => formatNumber(row.original.tokens), - }, - { - header: 'Cost (USD)', - cell: ({ row }) => formatCurrency(row.original.cost), - }, - ]; + const cols: ColumnDef[] = useMemo( + () => [ + { + header: 'Developer', + cell: ({ row }) => ( + + ), + }, + { + accessorKey: 'user.email', + header: 'Email', + }, + { + accessorKey: 'tasksStarted', + header: 'Tasks Started', + }, + { + accessorKey: 'tasksCompleted', + header: 'Tasks Completed', + }, + { + header: 'Tokens', + cell: ({ row }) => formatNumber(row.original.tokens), + }, + { + header: 'Cost (USD)', + cell: ({ row }) => formatCurrency(row.original.cost), + }, + ], + [onFilter], + ); - if (isPending) { - return ; - } + const columns = useMemo( + () => + isPending + ? cols.map((col) => ({ + ...col, + cell: () => , + })) + : cols, + [isPending, cols], + ); return ; }; diff --git a/src/app/(authenticated)/usage/Loader.tsx b/src/app/(authenticated)/usage/Loader.tsx deleted file mode 100644 index 8e946ecee2..0000000000 --- a/src/app/(authenticated)/usage/Loader.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { LoaderCircle } from 'lucide-react'; - -import { Skeleton } from '@/components/ui'; - -export const Loader = () => ( -
-
- -
-
- -
-
-); diff --git a/src/app/(authenticated)/usage/Models.tsx b/src/app/(authenticated)/usage/Models.tsx index 30825e083b..2e53acd578 100644 --- a/src/app/(authenticated)/usage/Models.tsx +++ b/src/app/(authenticated)/usage/Models.tsx @@ -1,14 +1,14 @@ +import { useMemo } from 'react'; import { useQuery } from '@tanstack/react-query'; import type { ColumnDef } from '@tanstack/react-table'; import { useAuth } from '@clerk/nextjs'; import { type ModelUsage, getModelUsage } from '@/actions/analytics'; import { formatCurrency, formatNumber } from '@/lib/formatters'; -import { Button } from '@/components/ui'; +import { Button, Skeleton } from '@/components/ui'; import { DataTable } from '@/components/layout/DataTable'; import type { Filter } from './types'; -import { Loader } from './Loader'; export const Models = ({ onFilter, @@ -23,46 +23,56 @@ export const Models = ({ enabled: !!orgId, }); - const columns: ColumnDef[] = [ - { - header: 'Model', - cell: ({ row: { original: model } }) => ( - - ), - }, - { - accessorKey: 'provider', - header: 'Provider', - }, - { - accessorKey: 'tasks', - header: 'Tasks', - }, - { - header: 'Tokens', - cell: ({ row }) => formatNumber(row.original.tokens), - }, - { - header: 'Cost (USD)', - cell: ({ row }) => formatCurrency(row.original.cost), - }, - ]; + const cols: ColumnDef[] = useMemo( + () => [ + { + header: 'Model', + cell: ({ row: { original: model } }) => ( + + ), + }, + { + accessorKey: 'provider', + header: 'Provider', + }, + { + accessorKey: 'tasks', + header: 'Tasks', + }, + { + header: 'Tokens', + cell: ({ row }) => formatNumber(row.original.tokens), + }, + { + header: 'Cost (USD)', + cell: ({ row }) => formatCurrency(row.original.cost), + }, + ], + [onFilter], + ); - if (isPending) { - return ; - } + const columns = useMemo( + () => + isPending + ? cols.map((col) => ({ + ...col, + cell: () => , + })) + : cols, + [isPending, cols], + ); return ; }; diff --git a/src/app/(authenticated)/usage/Tasks.tsx b/src/app/(authenticated)/usage/Tasks.tsx index f79aa061eb..2619199e77 100644 --- a/src/app/(authenticated)/usage/Tasks.tsx +++ b/src/app/(authenticated)/usage/Tasks.tsx @@ -5,11 +5,10 @@ import { useQuery } from '@tanstack/react-query'; import { type Task, getTasks } from '@/actions/analytics'; import { formatNumber, formatCurrency } from '@/lib/formatters'; -import { Button } from '@/components/ui'; +import { Button, Skeleton } from '@/components/ui'; import { DataTable } from '@/components/layout/DataTable'; import type { Filter } from './types'; -import { Loader } from './Loader'; import { Status } from './Status'; export const Tasks = ({ @@ -23,7 +22,7 @@ export const Tasks = ({ }) => { const { orgId } = useAuth(); - const { data = [], isPending } = useQuery({ + const { data = Array(3).fill({}), isPending } = useQuery({ queryKey: ['getTasks', orgId], queryFn: () => getTasks({ orgId }), enabled: !!orgId, @@ -41,77 +40,87 @@ export const Tasks = ({ ); }, [filter, data]); - const columns: ColumnDef[] = [ - { - header: 'Task ID', - cell: ({ row: { original: task } }) => ( - - ), - }, - { - header: 'Developer', - cell: ({ row }) => ( - - ), - }, - { - header: 'Model', - cell: ({ row }) => ( - - ), - }, - { - header: 'Tokens', - cell: ({ row }) => formatNumber(row.original.tokens), - }, - { - header: 'Cost (USD)', - cell: ({ row }) => formatCurrency(row.original.cost), - }, - { - header: 'Date', - cell: ({ row }) => - new Date(1000 * row.original.timestamp).toLocaleString(), - }, - { - header: 'Status', - cell: ({ row }) => , - }, - ]; + const cols: ColumnDef[] = useMemo( + () => [ + { + header: 'Task ID', + cell: ({ row: { original: task } }) => ( + + ), + }, + { + header: 'Developer', + cell: ({ row }) => ( + + ), + }, + { + header: 'Model', + cell: ({ row }) => ( + + ), + }, + { + header: 'Tokens', + cell: ({ row }) => formatNumber(row.original.tokens), + }, + { + header: 'Cost (USD)', + cell: ({ row }) => formatCurrency(row.original.cost), + }, + { + header: 'Date', + cell: ({ row }) => + new Date(1000 * row.original.timestamp).toLocaleString(), + }, + { + header: 'Status', + cell: ({ row }) => , + }, + ], + [onFilter, onTaskSelected], + ); - if (isPending) { - return ; - } + const columns = useMemo( + () => + isPending + ? cols.map((col) => ({ + ...col, + cell: () => , + })) + : cols, + [isPending, cols], + ); return ; };