mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
Better DataTable loader (#34)
This commit is contained in:
parent
3aec71ea16
commit
9662d16669
4 changed files with 188 additions and 173 deletions
|
|
@ -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<DeveloperUsage>[] = [
|
||||
{
|
||||
header: 'Developer',
|
||||
cell: ({ row }) => (
|
||||
<Button
|
||||
variant="link"
|
||||
onClick={() =>
|
||||
onFilter({
|
||||
type: 'userId',
|
||||
value: row.original.userId,
|
||||
label: row.original.user.name,
|
||||
})
|
||||
}
|
||||
className="px-0"
|
||||
>
|
||||
{row.original.user.name}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
{
|
||||
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<DeveloperUsage>[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
header: 'Developer',
|
||||
cell: ({ row }) => (
|
||||
<Button
|
||||
variant="link"
|
||||
onClick={() =>
|
||||
onFilter({
|
||||
type: 'userId',
|
||||
value: row.original.userId,
|
||||
label: row.original.user.name,
|
||||
})
|
||||
}
|
||||
className="px-0"
|
||||
>
|
||||
{row.original.user.name}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
{
|
||||
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 <Loader />;
|
||||
}
|
||||
const columns = useMemo(
|
||||
() =>
|
||||
isPending
|
||||
? cols.map((col) => ({
|
||||
...col,
|
||||
cell: () => <Skeleton className="h-9 w-full" />,
|
||||
}))
|
||||
: cols,
|
||||
[isPending, cols],
|
||||
);
|
||||
|
||||
return <DataTable columns={columns} data={data} />;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
import { LoaderCircle } from 'lucide-react';
|
||||
|
||||
import { Skeleton } from '@/components/ui';
|
||||
|
||||
export const Loader = () => (
|
||||
<div className="flex flex-col bg-card border shadow rounded">
|
||||
<div className="border-b p-2">
|
||||
<Skeleton className="h-6 w-full" />
|
||||
</div>
|
||||
<div className="flex justify-center p-2">
|
||||
<LoaderCircle className="size-5 animate-spin opacity-25" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -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<ModelUsage>[] = [
|
||||
{
|
||||
header: 'Model',
|
||||
cell: ({ row: { original: model } }) => (
|
||||
<Button
|
||||
variant="link"
|
||||
onClick={() =>
|
||||
onFilter({
|
||||
type: 'model',
|
||||
value: model.model,
|
||||
label: model.model,
|
||||
})
|
||||
}
|
||||
className="px-0"
|
||||
>
|
||||
{model.model}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
{
|
||||
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<ModelUsage>[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
header: 'Model',
|
||||
cell: ({ row: { original: model } }) => (
|
||||
<Button
|
||||
variant="link"
|
||||
onClick={() =>
|
||||
onFilter({
|
||||
type: 'model',
|
||||
value: model.model,
|
||||
label: model.model,
|
||||
})
|
||||
}
|
||||
className="px-0"
|
||||
>
|
||||
{model.model}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
{
|
||||
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 <Loader />;
|
||||
}
|
||||
const columns = useMemo(
|
||||
() =>
|
||||
isPending
|
||||
? cols.map((col) => ({
|
||||
...col,
|
||||
cell: () => <Skeleton className="h-9 w-full" />,
|
||||
}))
|
||||
: cols,
|
||||
[isPending, cols],
|
||||
);
|
||||
|
||||
return <DataTable columns={columns} data={data} />;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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<Task>[] = [
|
||||
{
|
||||
header: 'Task ID',
|
||||
cell: ({ row: { original: task } }) => (
|
||||
<Button
|
||||
variant="link"
|
||||
onClick={() => onTaskSelected(task)}
|
||||
className="px-0"
|
||||
>
|
||||
{task.taskId.slice(0, 8)}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: 'Developer',
|
||||
cell: ({ row }) => (
|
||||
<Button
|
||||
variant="link"
|
||||
onClick={() =>
|
||||
onFilter({
|
||||
type: 'userId',
|
||||
value: row.original.userId,
|
||||
label: row.original.user.name,
|
||||
})
|
||||
}
|
||||
className="px-0"
|
||||
>
|
||||
{row.original.user.name}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: 'Model',
|
||||
cell: ({ row }) => (
|
||||
<Button
|
||||
variant="link"
|
||||
onClick={() =>
|
||||
onFilter({
|
||||
type: 'model',
|
||||
value: row.original.model,
|
||||
label: row.original.model,
|
||||
})
|
||||
}
|
||||
className="px-0"
|
||||
>
|
||||
{row.original.model}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
{
|
||||
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 }) => <Status completed={row.original.completed} />,
|
||||
},
|
||||
];
|
||||
const cols: ColumnDef<Task>[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
header: 'Task ID',
|
||||
cell: ({ row: { original: task } }) => (
|
||||
<Button
|
||||
variant="link"
|
||||
onClick={() => onTaskSelected(task)}
|
||||
className="px-0"
|
||||
>
|
||||
{task.taskId.slice(0, 8)}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: 'Developer',
|
||||
cell: ({ row }) => (
|
||||
<Button
|
||||
variant="link"
|
||||
onClick={() =>
|
||||
onFilter({
|
||||
type: 'userId',
|
||||
value: row.original.userId,
|
||||
label: row.original.user.name,
|
||||
})
|
||||
}
|
||||
className="px-0"
|
||||
>
|
||||
{row.original.user.name}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: 'Model',
|
||||
cell: ({ row }) => (
|
||||
<Button
|
||||
variant="link"
|
||||
onClick={() =>
|
||||
onFilter({
|
||||
type: 'model',
|
||||
value: row.original.model,
|
||||
label: row.original.model,
|
||||
})
|
||||
}
|
||||
className="px-0"
|
||||
>
|
||||
{row.original.model}
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
{
|
||||
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 }) => <Status completed={row.original.completed} />,
|
||||
},
|
||||
],
|
||||
[onFilter, onTaskSelected],
|
||||
);
|
||||
|
||||
if (isPending) {
|
||||
return <Loader />;
|
||||
}
|
||||
const columns = useMemo(
|
||||
() =>
|
||||
isPending
|
||||
? cols.map((col) => ({
|
||||
...col,
|
||||
cell: () => <Skeleton className="h-9 w-full" />,
|
||||
}))
|
||||
: cols,
|
||||
[isPending, cols],
|
||||
);
|
||||
|
||||
return <DataTable columns={columns} data={tasks} />;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue