import { useMemo, useTransition } from 'react'; import { msg } from '@lingui/core/macro'; import { useLingui } from '@lingui/react'; import { Trans } from '@lingui/react/macro'; import { DocumentStatus as DocumentStatusEnum } from '@prisma/client'; import { RecipientRole, SigningStatus } from '@prisma/client'; import { CheckCircleIcon, DownloadIcon, EyeIcon, Loader, PencilIcon } from 'lucide-react'; import { DateTime } from 'luxon'; import { Link, useSearchParams } from 'react-router'; import { match } from 'ts-pattern'; import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params'; import { useSession } from '@documenso/lib/client-only/providers/session'; import { isDocumentCompleted } from '@documenso/lib/utils/document'; import { trpc } from '@documenso/trpc/react'; import type { TFindInboxResponse } from '@documenso/trpc/server/document-router/find-inbox.types'; import { Button } from '@documenso/ui/primitives/button'; import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table'; import { DataTable } from '@documenso/ui/primitives/data-table'; import { DataTablePagination } from '@documenso/ui/primitives/data-table-pagination'; import { Skeleton } from '@documenso/ui/primitives/skeleton'; import { TableCell } from '@documenso/ui/primitives/table'; import { useToast } from '@documenso/ui/primitives/use-toast'; import { DocumentStatus } from '~/components/general/document/document-status'; import { useOptionalCurrentTeam } from '~/providers/team'; import { EnvelopeDownloadDialog } from '../dialogs/envelope-download-dialog'; import { StackAvatarsWithTooltip } from '../general/stack-avatars-with-tooltip'; export type DocumentsTableProps = { data?: TFindInboxResponse; isLoading?: boolean; isLoadingError?: boolean; }; type DocumentsTableRow = TFindInboxResponse['data'][number]; export const InboxTable = () => { const { _, i18n } = useLingui(); const team = useOptionalCurrentTeam(); const [isPending, startTransition] = useTransition(); const [searchParams] = useSearchParams(); const updateSearchParams = useUpdateSearchParams(); const page = searchParams?.get?.('page') ? Number(searchParams.get('page')) : undefined; const perPage = searchParams?.get?.('perPage') ? Number(searchParams.get('perPage')) : undefined; const { data, isLoading, isLoadingError } = trpc.document.inbox.find.useQuery({ page: page || 1, perPage: perPage || 10, }); const columns = useMemo(() => { return [ { header: _(msg`Created`), accessorKey: 'createdAt', cell: ({ row }) => i18n.date(row.original.createdAt, { ...DateTime.DATETIME_SHORT, hourCycle: 'h12' }), }, { header: _(msg`Title`), cell: ({ row }) => ( {row.original.title} ), }, { id: 'sender', header: _(msg`Sender`), cell: ({ row }) => row.original.user.name ?? row.original.user.email, }, { header: _(msg`Recipient`), accessorKey: 'recipient', cell: ({ row }) => ( ), }, { header: _(msg`Status`), accessorKey: 'status', cell: ({ row }) => , size: 140, }, { header: _(msg`Actions`), cell: ({ row }) => , }, ] satisfies DataTableColumnDef[]; }, [team]); const onPaginationChange = (page: number, perPage: number) => { startTransition(() => { updateSearchParams({ page, perPage, }); }); }; const results = data ?? { data: [], perPage: 10, currentPage: 1, totalPages: 1, }; return (

Documents that require your attention will appear here

} skeleton={{ enable: isLoading || false, rows: 5, component: ( <>
), }} > {(table) => results.totalPages > 1 && ( ) } {isPending && (
)} ); }; export type InboxTableActionButtonProps = { row: TFindInboxResponse['data'][number]; }; export const InboxTableActionButton = ({ row }: InboxTableActionButtonProps) => { const { user } = useSession(); const { toast } = useToast(); const { _ } = useLingui(); const recipient = row.recipients.find((recipient) => recipient.email === user.email); const isPending = row.status === DocumentStatusEnum.PENDING; const isComplete = isDocumentCompleted(row.status); const isSigned = recipient?.signingStatus === SigningStatus.SIGNED; const role = recipient?.role; if (!recipient) { return null; } // TODO: Consider if want to keep this logic for hiding viewing for CC'ers if (recipient?.role === RecipientRole.CC && isComplete === false) { return null; } return match({ isPending, isComplete, isSigned, internalVersion: row.internalVersion, }) .with({ isPending: true, isSigned: false }, () => ( )) .with({ isPending: true, isSigned: true }, () => ( )) .with({ isComplete: true }, () => ( Download } /> )) .otherwise(() =>
); };