fix: loading state and tuncation

This commit is contained in:
Ephraim Atta-Duncan
2025-08-20 15:48:45 +00:00
parent c2dc6d6b26
commit 466b00ec84
2 changed files with 36 additions and 23 deletions

View File

@ -3,7 +3,7 @@ import { useEffect, useMemo, useState, useTransition } from 'react';
import { msg } from '@lingui/core/macro'; import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react'; import { useLingui } from '@lingui/react';
import { ChevronDownIcon, ChevronUpIcon, ChevronsUpDown, Loader } from 'lucide-react'; import { ChevronDownIcon, ChevronUpIcon, ChevronsUpDown, Loader } from 'lucide-react';
import { Link } from 'react-router'; import { Link, useSearchParams } from 'react-router';
import { useDebouncedValue } from '@documenso/lib/client-only/hooks/use-debounced-value'; import { useDebouncedValue } from '@documenso/lib/client-only/hooks/use-debounced-value';
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params'; import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
@ -44,6 +44,7 @@ export const AdminOrganisationOverviewTable = ({
const { _, i18n } = useLingui(); const { _, i18n } = useLingui();
const [isPending, startTransition] = useTransition(); const [isPending, startTransition] = useTransition();
const [searchParams] = useSearchParams();
const updateSearchParams = useUpdateSearchParams(); const updateSearchParams = useUpdateSearchParams();
const [searchString, setSearchString] = useState(''); const [searchString, setSearchString] = useState('');
const debouncedSearchString = useDebouncedValue(searchString, 1000); const debouncedSearchString = useDebouncedValue(searchString, 1000);
@ -74,7 +75,7 @@ export const AdminOrganisationOverviewTable = ({
<div> <div>
<Link <Link
className="text-primary underline" className="text-primary underline"
to={`/admin/organisation-insights/${row.original.id}`} to={`/admin/organisation-insights/${row.original.id}?dateRange=${searchParams.get('dateRange') || 'last30days'}`}
> >
{row.getValue('name')} {row.getValue('name')}
</Link> </Link>

View File

@ -3,6 +3,7 @@ import { useTransition } from 'react';
import { msg } from '@lingui/core/macro'; import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react'; import { useLingui } from '@lingui/react';
import { Archive, Building2, Loader, TrendingUp, Users } from 'lucide-react'; import { Archive, Building2, Loader, TrendingUp, Users } from 'lucide-react';
import { useNavigation } from 'react-router';
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params'; import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
import type { OrganisationDetailedInsights } from '@documenso/lib/server-only/admin/get-organisation-detailed-insights'; import type { OrganisationDetailedInsights } from '@documenso/lib/server-only/admin/get-organisation-detailed-insights';
@ -33,8 +34,11 @@ export const OrganisationInsightsTable = ({
}: OrganisationInsightsTableProps) => { }: OrganisationInsightsTableProps) => {
const { _, i18n } = useLingui(); const { _, i18n } = useLingui();
const [isPending, startTransition] = useTransition(); const [isPending, startTransition] = useTransition();
const navigation = useNavigation();
const updateSearchParams = useUpdateSearchParams(); const updateSearchParams = useUpdateSearchParams();
const isLoading = isPending || navigation.state === 'loading';
const onPaginationChange = (newPage: number, newPerPage: number) => { const onPaginationChange = (newPage: number, newPerPage: number) => {
startTransition(() => { startTransition(() => {
updateSearchParams({ updateSearchParams({
@ -121,8 +125,12 @@ export const OrganisationInsightsTable = ({
{ {
header: () => <span className="whitespace-nowrap">{_(msg`Title`)}</span>, header: () => <span className="whitespace-nowrap">{_(msg`Title`)}</span>,
accessorKey: 'title', accessorKey: 'title',
cell: ({ row }) => <span className="block max-w-full truncate">{row.getValue('title')}</span>, cell: ({ row }) => (
size: 240, <span className="block max-w-[200px] truncate" title={row.getValue('title') as string}>
{row.getValue('title')}
</span>
),
size: 200,
}, },
{ {
header: () => <span className="whitespace-nowrap">{_(msg`Status`)}</span>, header: () => <span className="whitespace-nowrap">{_(msg`Status`)}</span>,
@ -130,21 +138,23 @@ export const OrganisationInsightsTable = ({
cell: ({ row }) => ( cell: ({ row }) => (
<DocumentStatus status={row.getValue('status') as ExtendedDocumentStatus} /> <DocumentStatus status={row.getValue('status') as ExtendedDocumentStatus} />
), ),
size: 140, size: 120,
}, },
{ {
header: () => <span className="whitespace-nowrap">{_(msg`Team`)}</span>, header: () => <span className="whitespace-nowrap">{_(msg`Team`)}</span>,
accessorKey: 'teamName', accessorKey: 'teamName',
cell: ({ row }) => ( cell: ({ row }) => (
<span className="block max-w-full truncate">{row.getValue('teamName')}</span> <span className="block max-w-[150px] truncate" title={row.getValue('teamName') as string}>
{row.getValue('teamName')}
</span>
), ),
size: 180, size: 150,
}, },
{ {
header: () => <span className="whitespace-nowrap">{_(msg`Created`)}</span>, header: () => <span className="whitespace-nowrap">{_(msg`Created`)}</span>,
accessorKey: 'createdAt', accessorKey: 'createdAt',
cell: ({ row }) => i18n.date(new Date(row.getValue('createdAt'))), cell: ({ row }) => i18n.date(new Date(row.getValue('createdAt'))),
size: 160, size: 140,
}, },
{ {
header: () => <span className="whitespace-nowrap">{_(msg`Completed`)}</span>, header: () => <span className="whitespace-nowrap">{_(msg`Completed`)}</span>,
@ -154,7 +164,7 @@ export const OrganisationInsightsTable = ({
return completedAt ? i18n.date(new Date(completedAt)) : '-'; return completedAt ? i18n.date(new Date(completedAt)) : '-';
}, },
size: 160, size: 140,
}, },
] satisfies DataTableColumnDef<(typeof insights.documents)[number]>[]; ] satisfies DataTableColumnDef<(typeof insights.documents)[number]>[];
@ -231,21 +241,21 @@ export const OrganisationInsightsTable = ({
<Button <Button
variant={view === 'teams' ? 'default' : 'outline'} variant={view === 'teams' ? 'default' : 'outline'}
onClick={() => handleViewChange('teams')} onClick={() => handleViewChange('teams')}
disabled={isPending} disabled={isLoading}
> >
{_(msg`Teams`)} {_(msg`Teams`)}
</Button> </Button>
<Button <Button
variant={view === 'users' ? 'default' : 'outline'} variant={view === 'users' ? 'default' : 'outline'}
onClick={() => handleViewChange('users')} onClick={() => handleViewChange('users')}
disabled={isPending} disabled={isLoading}
> >
{_(msg`Users`)} {_(msg`Users`)}
</Button> </Button>
<Button <Button
variant={view === 'documents' ? 'default' : 'outline'} variant={view === 'documents' ? 'default' : 'outline'}
onClick={() => handleViewChange('documents')} onClick={() => handleViewChange('documents')}
disabled={isPending} disabled={isLoading}
> >
{_(msg`Documents`)} {_(msg`Documents`)}
</Button> </Button>
@ -253,18 +263,20 @@ export const OrganisationInsightsTable = ({
<DateRangeFilter currentRange={dateRange} /> <DateRangeFilter currentRange={dateRange} />
</div> </div>
<DataTable<unknown, unknown> <div className={view === 'documents' ? 'overflow-hidden' : undefined}>
columns={getCurrentColumns()} <DataTable<unknown, unknown>
data={getCurrentData()} columns={getCurrentColumns()}
perPage={perPage} data={getCurrentData()}
currentPage={page} perPage={perPage}
totalPages={insights.totalPages} currentPage={page}
onPaginationChange={onPaginationChange} totalPages={insights.totalPages}
> onPaginationChange={onPaginationChange}
{(table) => <DataTablePagination additionalInformation="VisibleCount" table={table} />} >
</DataTable> {(table) => <DataTablePagination additionalInformation="VisibleCount" table={table} />}
</DataTable>
</div>
{isPending && ( {isLoading && (
<div className="absolute inset-0 flex items-center justify-center bg-white/50"> <div className="absolute inset-0 flex items-center justify-center bg-white/50">
<Loader className="h-8 w-8 animate-spin text-gray-500" /> <Loader className="h-8 w-8 animate-spin text-gray-500" />
</div> </div>