mirror of
https://github.com/documenso/documenso.git
synced 2025-11-24 21:51:40 +10:00
Merge branch 'feat/refresh' into feat/universal-upload
This commit is contained in:
@ -139,7 +139,7 @@ export const EditDocumentForm = ({
|
||||
duration: 5000,
|
||||
});
|
||||
|
||||
router.push('/dashboard');
|
||||
router.push('/documents');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
|
||||
56
apps/web/src/app/(dashboard)/documents/data-table-title.tsx
Normal file
56
apps/web/src/app/(dashboard)/documents/data-table-title.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { Document, Recipient, User } from '@documenso/prisma/client';
|
||||
|
||||
export type DataTableTitleProps = {
|
||||
row: Document & {
|
||||
User: Pick<User, 'id' | 'name' | 'email'>;
|
||||
Recipient: Recipient[];
|
||||
};
|
||||
};
|
||||
|
||||
export const DataTableTitle = ({ row }: DataTableTitleProps) => {
|
||||
const { data: session } = useSession();
|
||||
|
||||
if (!session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const recipient = row.Recipient.find((recipient) => recipient.email === session.user.email);
|
||||
|
||||
const isOwner = row.User.id === session.user.id;
|
||||
const isRecipient = !!recipient;
|
||||
|
||||
return match({
|
||||
isOwner,
|
||||
isRecipient,
|
||||
})
|
||||
.with({ isOwner: true }, () => (
|
||||
<Link
|
||||
href={`/documents/${row.id}`}
|
||||
title={row.title}
|
||||
className="block max-w-[10rem] truncate font-medium hover:underline md:max-w-[20rem]"
|
||||
>
|
||||
{row.title}
|
||||
</Link>
|
||||
))
|
||||
.with({ isRecipient: true }, () => (
|
||||
<Link
|
||||
href={`/sign/${recipient?.token}`}
|
||||
title={row.title}
|
||||
className="block max-w-[10rem] truncate font-medium hover:underline md:max-w-[20rem]"
|
||||
>
|
||||
{row.title}
|
||||
</Link>
|
||||
))
|
||||
.otherwise(() => (
|
||||
<span className="block max-w-[10rem] truncate font-medium hover:underline md:max-w-[20rem]">
|
||||
{row.title}
|
||||
</span>
|
||||
));
|
||||
};
|
||||
@ -2,9 +2,8 @@
|
||||
|
||||
import { useTransition } from 'react';
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
import { Loader } from 'lucide-react';
|
||||
import { useSession } from 'next-auth/react';
|
||||
|
||||
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
|
||||
import { FindResultSet } from '@documenso/lib/types/find-result-set';
|
||||
@ -18,6 +17,7 @@ import { LocaleDate } from '~/components/formatter/locale-date';
|
||||
|
||||
import { DataTableActionButton } from './data-table-action-button';
|
||||
import { DataTableActionDropdown } from './data-table-action-dropdown';
|
||||
import { DataTableTitle } from './data-table-title';
|
||||
|
||||
export type DocumentsDataTableProps = {
|
||||
results: FindResultSet<
|
||||
@ -29,6 +29,7 @@ export type DocumentsDataTableProps = {
|
||||
};
|
||||
|
||||
export const DocumentsDataTable = ({ results }: DocumentsDataTableProps) => {
|
||||
const { data: session } = useSession();
|
||||
const [isPending, startTransition] = useTransition();
|
||||
|
||||
const updateSearchParams = useUpdateSearchParams();
|
||||
@ -42,25 +43,22 @@ export const DocumentsDataTable = ({ results }: DocumentsDataTableProps) => {
|
||||
});
|
||||
};
|
||||
|
||||
if (!session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<DataTable
|
||||
columns={[
|
||||
{
|
||||
header: 'ID',
|
||||
accessorKey: 'id',
|
||||
header: 'Created',
|
||||
accessorKey: 'created',
|
||||
cell: ({ row }) => <LocaleDate date={row.getValue('created')} />,
|
||||
},
|
||||
{
|
||||
header: 'Title',
|
||||
cell: ({ row }) => (
|
||||
<Link
|
||||
href={`/documents/${row.original.id}`}
|
||||
title={row.original.title}
|
||||
className="block max-w-[10rem] truncate font-medium hover:underline md:max-w-[20rem]"
|
||||
>
|
||||
{row.original.title}
|
||||
</Link>
|
||||
),
|
||||
cell: ({ row }) => <DataTableTitle row={row.original} />,
|
||||
},
|
||||
{
|
||||
header: 'Recipient',
|
||||
@ -74,11 +72,6 @@ export const DocumentsDataTable = ({ results }: DocumentsDataTableProps) => {
|
||||
accessorKey: 'status',
|
||||
cell: ({ row }) => <DocumentStatus status={row.getValue('status')} />,
|
||||
},
|
||||
{
|
||||
header: 'Created',
|
||||
accessorKey: 'created',
|
||||
cell: ({ row }) => <LocaleDate date={row.getValue('created')} />,
|
||||
},
|
||||
{
|
||||
header: 'Actions',
|
||||
cell: ({ row }) => (
|
||||
@ -95,7 +88,7 @@ export const DocumentsDataTable = ({ results }: DocumentsDataTableProps) => {
|
||||
totalPages={results.totalPages}
|
||||
onPaginationChange={onPaginationChange}
|
||||
>
|
||||
{(table) => <DataTablePagination table={table} />}
|
||||
{(table) => <DataTablePagination additionalInformation="VisibleCount" table={table} />}
|
||||
</DataTable>
|
||||
|
||||
{isPending && (
|
||||
|
||||
@ -11,8 +11,8 @@ import { PeriodSelector } from '~/components/(dashboard)/period-selector/period-
|
||||
import { PeriodSelectorValue } from '~/components/(dashboard)/period-selector/types';
|
||||
import { DocumentStatus } from '~/components/formatter/document-status';
|
||||
|
||||
import { UploadDocument } from '../dashboard/upload-document';
|
||||
import { DocumentsDataTable } from './data-table';
|
||||
import { UploadDocument } from './upload-document';
|
||||
|
||||
export type DocumentsPageProps = {
|
||||
searchParams?: {
|
||||
@ -81,6 +81,7 @@ export default async function DocumentsPage({ searchParams = {} }: DocumentsPage
|
||||
{value !== ExtendedDocumentStatus.ALL && (
|
||||
<span className="ml-1 hidden opacity-50 md:inline-block">
|
||||
{Math.min(stats[value], 99)}
|
||||
{stats[value] > 99 && '+'}
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
|
||||
58
apps/web/src/app/(dashboard)/documents/upload-document.tsx
Normal file
58
apps/web/src/app/(dashboard)/documents/upload-document.tsx
Normal file
@ -0,0 +1,58 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import { Loader } from 'lucide-react';
|
||||
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { DocumentDropzone } from '@documenso/ui/primitives/document-dropzone';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
import { useCreateDocument } from '~/api/document/create/fetcher';
|
||||
|
||||
export type UploadDocumentProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const UploadDocument = ({ className }: UploadDocumentProps) => {
|
||||
const { toast } = useToast();
|
||||
const router = useRouter();
|
||||
|
||||
const { isLoading, mutateAsync: createDocument } = useCreateDocument();
|
||||
|
||||
const onFileDrop = async (file: File) => {
|
||||
try {
|
||||
const { id } = await createDocument({
|
||||
file: file,
|
||||
});
|
||||
|
||||
toast({
|
||||
title: 'Document uploaded',
|
||||
description: 'Your document has been uploaded successfully.',
|
||||
duration: 5000,
|
||||
});
|
||||
|
||||
router.push(`/documents/${id}`);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'An error occurred while uploading your document.',
|
||||
variant: 'destructive',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn('relative', className)}>
|
||||
<DocumentDropzone className="min-h-[40vh]" onDrop={onFileDrop} />
|
||||
|
||||
{isLoading && (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-white/50">
|
||||
<Loader className="h-12 w-12 animate-spin text-slate-500" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user