chore: minor changes

This commit is contained in:
Ephraim Atta-Duncan
2025-06-05 12:07:19 +00:00
parent 2c064d5aff
commit de45a63c97
3 changed files with 176 additions and 125 deletions

View File

@ -1,70 +0,0 @@
import { useMemo } from 'react';
import { Trans } from '@lingui/react/macro';
import { useLocation, useNavigate, useSearchParams } from 'react-router';
import type { PeriodSelectorValue } from '@documenso/lib/server-only/document/find-documents';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@documenso/ui/primitives/select';
const isPeriodSelectorValue = (value: unknown): value is PeriodSelectorValue => {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
return ['', '7d', '14d', '30d'].includes(value as string);
};
export const PeriodSelector = () => {
const { pathname } = useLocation();
const [searchParams] = useSearchParams();
const navigate = useNavigate();
const period = useMemo(() => {
const p = searchParams?.get('period') ?? 'all';
return isPeriodSelectorValue(p) ? p : 'all';
}, [searchParams]);
const onPeriodChange = (newPeriod: string) => {
if (!pathname) {
return;
}
const params = new URLSearchParams(searchParams?.toString());
params.set('period', newPeriod);
if (newPeriod === '' || newPeriod === 'all') {
params.delete('period');
}
void navigate(`${pathname}?${params.toString()}`, { preventScrollReset: true });
};
return (
<Select defaultValue={period} onValueChange={onPeriodChange}>
<SelectTrigger className="text-muted-foreground max-w-[200px]">
<SelectValue />
</SelectTrigger>
<SelectContent position="popper">
<SelectItem value="all">
<Trans>All Time</Trans>
</SelectItem>
<SelectItem value="7d">
<Trans>Last 7 days</Trans>
</SelectItem>
<SelectItem value="14d">
<Trans>Last 14 days</Trans>
</SelectItem>
<SelectItem value="30d">
<Trans>Last 30 days</Trans>
</SelectItem>
</SelectContent>
</Select>
);
};

View File

@ -1,17 +1,17 @@
import type { Prisma, User } from '@prisma/client';
import { DocumentVisibility, SigningStatus, TeamMemberRole } from '@prisma/client';
import { DateTime } from 'luxon';
import { match } from 'ts-pattern';
import type { PeriodSelectorValue } from '@documenso/lib/server-only/document/find-documents';
import { prisma } from '@documenso/prisma';
import { isExtendedDocumentStatus } from '@documenso/prisma/guards/is-extended-document-status';
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
import type { TimePeriod } from '@documenso/ui/primitives/data-table/utils/time-filters';
import { getDateRangeForPeriod } from '@documenso/ui/primitives/data-table/utils/time-filters';
export type GetStatsInput = {
user: User;
team?: Omit<GetTeamCountsOption, 'createdAt'>;
period?: PeriodSelectorValue;
period?: TimePeriod;
search?: string;
folderId?: string;
};
@ -26,61 +26,15 @@ export const getStats = async ({
let createdAt: Prisma.DocumentWhereInput['createdAt'];
if (period && period !== 'all-time') {
const now = DateTime.now();
let startDate: DateTime;
let endDate: DateTime;
switch (period) {
case 'today':
startDate = now.startOf('day');
endDate = now.endOf('day');
break;
case 'yesterday':
startDate = now.minus({ days: 1 }).startOf('day');
endDate = now.minus({ days: 1 }).endOf('day');
break;
case 'this-week':
startDate = now.startOf('week');
endDate = now.endOf('week');
break;
case 'last-week':
startDate = now.minus({ weeks: 1 }).startOf('week');
endDate = now.minus({ weeks: 1 }).endOf('week');
break;
case 'this-month':
startDate = now.startOf('month');
endDate = now.endOf('month');
break;
case 'last-month':
startDate = now.minus({ months: 1 }).startOf('month');
endDate = now.minus({ months: 1 }).endOf('month');
break;
case 'this-quarter':
startDate = now.startOf('quarter');
endDate = now.endOf('quarter');
break;
case 'last-quarter':
startDate = now.minus({ quarters: 1 }).startOf('quarter');
endDate = now.minus({ quarters: 1 }).endOf('quarter');
break;
case 'this-year':
startDate = now.startOf('year');
endDate = now.endOf('year');
break;
case 'last-year':
startDate = now.minus({ years: 1 }).startOf('year');
endDate = now.minus({ years: 1 }).endOf('year');
break;
default:
startDate = now.startOf('day');
endDate = now.endOf('day');
}
const dateRange = getDateRangeForPeriod(period);
if (dateRange) {
createdAt = {
gte: startDate.toJSDate(),
lte: endDate.toJSDate(),
gte: dateRange.start.toJSDate(),
lte: dateRange.end.toJSDate(),
};
}
}
const [ownerCounts, notSignedCounts, hasSignedCounts] = await (options.team
? getTeamCounts({

View File

@ -0,0 +1,167 @@
import React, { useMemo } from 'react';
import { Trans } from '@lingui/react/macro';
import type {
ColumnDef,
PaginationState,
Table as TTable,
Updater,
VisibilityState,
} from '@tanstack/react-table';
import { flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table';
import { Skeleton } from './skeleton';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from './table';
export type DataTableChildren<TData> = (_table: TTable<TData>) => React.ReactNode;
export type { ColumnDef as DataTableColumnDef } from '@tanstack/react-table';
export interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
columnVisibility?: VisibilityState;
data: TData[];
perPage?: number;
currentPage?: number;
totalPages?: number;
onPaginationChange?: (_page: number, _perPage: number) => void;
onClearFilters?: () => void;
hasFilters?: boolean;
children?: DataTableChildren<TData>;
skeleton?: {
enable: boolean;
rows: number;
component?: React.ReactNode;
};
error?: {
enable: boolean;
component?: React.ReactNode;
};
}
export function DataTable<TData, TValue>({
columns,
columnVisibility,
data,
error,
perPage,
currentPage,
totalPages,
skeleton,
hasFilters,
onClearFilters,
onPaginationChange,
children,
}: DataTableProps<TData, TValue>) {
const pagination = useMemo<PaginationState>(() => {
if (currentPage !== undefined && perPage !== undefined) {
return {
pageIndex: currentPage - 1,
pageSize: perPage,
};
}
return {
pageIndex: 0,
pageSize: 0,
};
}, [currentPage, perPage]);
const manualPagination = Boolean(currentPage !== undefined && totalPages !== undefined);
const onTablePaginationChange = (updater: Updater<PaginationState>) => {
if (typeof updater === 'function') {
const newState = updater(pagination);
onPaginationChange?.(newState.pageIndex + 1, newState.pageSize);
} else {
onPaginationChange?.(updater.pageIndex + 1, updater.pageSize);
}
};
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
state: {
pagination: manualPagination ? pagination : undefined,
columnVisibility,
},
manualPagination,
pageCount: totalPages,
onPaginationChange: onTablePaginationChange,
});
return (
<>
<div className="rounded-md border">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(header.column.columnDef.header, header.getContext())}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow key={row.id} data-state={row.getIsSelected() && 'selected'}>
{row.getVisibleCells().map((cell) => (
<TableCell
key={cell.id}
style={{
width: `${cell.column.getSize()}px`,
}}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : error?.enable ? (
<TableRow>
{error.component ?? (
<TableCell colSpan={columns.length} className="h-32 text-center">
<Trans>Something went wrong.</Trans>
</TableCell>
)}
</TableRow>
) : skeleton?.enable ? (
Array.from({ length: skeleton.rows }).map((_, i) => (
<TableRow key={`skeleton-row-${i}`}>{skeleton.component ?? <Skeleton />}</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-32 text-center">
<p>
<Trans>No results found</Trans>
</p>
{hasFilters && onClearFilters !== undefined && (
<button
onClick={() => onClearFilters()}
className="text-foreground mt-1 text-sm"
>
<Trans>Clear filters</Trans>
</button>
)}
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
{children && <div className="mt-8 w-full">{children(table)}</div>}
</>
);
}