feat: use data-table on template pages

This commit is contained in:
Ephraim Atta-Duncan
2025-06-05 10:53:53 +00:00
parent 9ccd8e0397
commit 9739a0ca96
10 changed files with 390 additions and 301 deletions

View File

@ -246,17 +246,15 @@ export default function DocumentsPage() {
</div>
<div className="mt-8">
<div>
<DocumentsDataTable
data={data}
isLoading={isLoading}
isLoadingError={isLoadingError}
onMoveDocument={(documentId) => {
setDocumentToMove(documentId);
setIsMovingDocument(true);
}}
/>
</div>
<DocumentsDataTable
data={data}
isLoading={isLoading}
isLoadingError={isLoadingError}
onMoveDocument={(documentId) => {
setDocumentToMove(documentId);
setIsMovingDocument(true);
}}
/>
</div>
{documentToMove && (

View File

@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import { Trans } from '@lingui/react/macro';
import { Bird, FolderIcon, HomeIcon, Loader2, PinIcon } from 'lucide-react';
import { Bird, FolderIcon, HomeIcon, Loader2 } from 'lucide-react';
import { useNavigate, useParams, useSearchParams } from 'react-router';
import { FolderType } from '@documenso/lib/types/folder-type';
@ -11,18 +11,13 @@ import { trpc } from '@documenso/trpc/react';
import type { TFolderWithSubfolders } from '@documenso/trpc/server/folder-router/schema';
import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/avatar';
import { Button } from '@documenso/ui/primitives/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@documenso/ui/primitives/dropdown-menu';
import { FolderDeleteDialog } from '~/components/dialogs/folder-delete-dialog';
import { FolderMoveDialog } from '~/components/dialogs/folder-move-dialog';
import { FolderSettingsDialog } from '~/components/dialogs/folder-settings-dialog';
import { TemplateCreateDialog } from '~/components/dialogs/template-create-dialog';
import { TemplateFolderCreateDialog } from '~/components/dialogs/template-folder-create-dialog';
import { TemplateFolderDeleteDialog } from '~/components/dialogs/template-folder-delete-dialog';
import { TemplateFolderMoveDialog } from '~/components/dialogs/template-folder-move-dialog';
import { TemplateFolderSettingsDialog } from '~/components/dialogs/template-folder-settings-dialog';
import { FolderCard } from '~/components/general/folder/folder-card';
import { TemplatesTable } from '~/components/tables/templates-table';
import { useOptionalCurrentTeam } from '~/providers/team';
import { appMetaTags } from '~/utils/meta';
@ -36,8 +31,18 @@ export default function TemplatesPage() {
const { folderId } = useParams();
const navigate = useNavigate();
const [isMovingFolder, setIsMovingFolder] = useState(false);
const [folderToMove, setFolderToMove] = useState<TFolderWithSubfolders | null>(null);
const [isDeletingFolder, setIsDeletingFolder] = useState(false);
const [folderToDelete, setFolderToDelete] = useState<TFolderWithSubfolders | null>(null);
const [isSettingsFolderOpen, setIsSettingsFolderOpen] = useState(false);
const [folderToSettings, setFolderToSettings] = useState<TFolderWithSubfolders | null>(null);
const team = useOptionalCurrentTeam();
const { mutateAsync: pinFolder } = trpc.folder.pinFolder.useMutation();
const { mutateAsync: unpinFolder } = trpc.folder.unpinFolder.useMutation();
const page = Number(searchParams.get('page')) || 1;
const perPage = Number(searchParams.get('perPage')) || 10;
@ -59,22 +64,12 @@ export default function TemplatesPage() {
type: FolderType.TEMPLATE,
});
const { mutateAsync: pinFolder } = trpc.folder.pinFolder.useMutation();
const { mutateAsync: unpinFolder } = trpc.folder.unpinFolder.useMutation();
const [folderToMove, setFolderToMove] = useState<TFolderWithSubfolders | null>(null);
const [isMovingFolder, setIsMovingFolder] = useState(false);
const [folderToSettings, setFolderToSettings] = useState<TFolderWithSubfolders | null>(null);
const [isSettingsFolderOpen, setIsSettingsFolderOpen] = useState(false);
const [folderToDelete, setFolderToDelete] = useState<TFolderWithSubfolders | null>(null);
const [isDeletingFolder, setIsDeletingFolder] = useState(false);
useEffect(() => {
void refetch();
void refetchFolders();
}, [team?.url]);
const navigateToFolder = (folderId?: string) => {
const navigateToFolder = (folderId?: string | null) => {
const templatesPath = formatTemplatesPath(team?.url);
if (folderId) {
@ -84,6 +79,33 @@ export default function TemplatesPage() {
}
};
const handleNavigate = (folderId: string) => {
navigateToFolder(folderId);
};
const handleMove = (folder: TFolderWithSubfolders) => {
setFolderToMove(folder);
setIsMovingFolder(true);
};
const handlePin = (folderId: string) => {
void pinFolder({ folderId });
};
const handleUnpin = (folderId: string) => {
void unpinFolder({ folderId });
};
const handleSettings = (folder: TFolderWithSubfolders) => {
setFolderToSettings(folder);
setIsSettingsFolderOpen(true);
};
const handleDelete = (folder: TFolderWithSubfolders) => {
setFolderToDelete(folder);
setIsDeletingFolder(true);
};
return (
<div className="mx-auto max-w-screen-xl px-4 md:px-8">
<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
@ -126,159 +148,44 @@ export default function TemplatesPage() {
</div>
) : (
<>
{foldersData?.folders.some((folder) => folder.pinned) && (
{foldersData?.folders && foldersData.folders.some((folder) => folder.pinned) && (
<div className="mt-6">
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
{foldersData?.folders
{foldersData.folders
.filter((folder) => folder.pinned)
.map((folder) => (
<div
<FolderCard
key={folder.id}
className="border-border hover:border-muted-foreground/40 group relative flex flex-col rounded-lg border p-4 transition-all hover:shadow-sm"
>
<div className="flex items-start justify-between">
<button
className="flex items-center space-x-2 text-left"
onClick={() => navigateToFolder(folder.id)}
>
<FolderIcon className="text-documenso h-6 w-6" />
<div>
<div className="flex items-center gap-2">
<h3 className="font-medium">{folder.name}</h3>
<PinIcon className="text-documenso h-3 w-3" />
</div>
<div className="mt-1 flex space-x-2 text-xs text-gray-500">
<span>{folder._count.templates || 0} templates</span>
<span></span>
<span>{folder._count.subfolders} folders</span>
</div>
</div>
</button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="sm"
className="opacity-0 group-hover:opacity-100"
>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
onClick={() => {
setFolderToMove(folder);
setIsMovingFolder(true);
}}
>
Move
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
void unpinFolder({ folderId: folder.id });
}}
>
Unpin
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
setFolderToSettings(folder);
setIsSettingsFolderOpen(true);
}}
>
Settings
</DropdownMenuItem>
<DropdownMenuItem
className="text-red-500"
onClick={() => {
setFolderToDelete(folder);
setIsDeletingFolder(true);
}}
>
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
folder={folder}
onNavigate={handleNavigate}
onMove={handleMove}
onPin={handlePin}
onUnpin={handleUnpin}
onSettings={handleSettings}
onDelete={handleDelete}
/>
))}
</div>
</div>
)}
<div className="mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
{foldersData?.folders
.filter((folder) => !folder.pinned)
.map((folder) => (
<div
key={folder.id}
className="border-border hover:border-muted-foreground/40 group relative flex flex-col rounded-lg border p-4 transition-all hover:shadow-sm"
>
<div className="flex items-start justify-between">
<button
className="flex items-center space-x-2 text-left"
onClick={() => navigateToFolder(folder.id)}
>
<FolderIcon className="text-documenso h-6 w-6" />
<div>
<h3 className="font-medium">{folder.name}</h3>
<div className="mt-1 flex space-x-2 text-xs text-gray-500">
<span>{folder._count.templates || 0} templates</span>
<span></span>
<span>{folder._count.subfolders} folders</span>
</div>
</div>
</button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="sm"
className="opacity-0 group-hover:opacity-100"
>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
onClick={() => {
setFolderToMove(folder);
setIsMovingFolder(true);
}}
>
Move
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
void pinFolder({ folderId: folder.id });
}}
>
Pin
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
setFolderToSettings(folder);
setIsSettingsFolderOpen(true);
}}
>
Settings
</DropdownMenuItem>
<DropdownMenuItem
className="text-red-500"
onClick={() => {
setFolderToDelete(folder);
setIsDeletingFolder(true);
}}
>
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
))}
<div className="mt-6">
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4">
{foldersData?.folders
.filter((folder) => !folder.pinned)
.map((folder) => (
<FolderCard
key={folder.id}
folder={folder}
onNavigate={handleNavigate}
onMove={handleMove}
onPin={handlePin}
onUnpin={handleUnpin}
onSettings={handleSettings}
onDelete={handleDelete}
/>
))}
</div>
</div>
</>
)}
@ -328,7 +235,7 @@ export default function TemplatesPage() {
</div>
</div>
<FolderMoveDialog
<TemplateFolderMoveDialog
foldersData={foldersData?.folders}
folder={folderToMove}
isOpen={isMovingFolder}
@ -341,7 +248,7 @@ export default function TemplatesPage() {
}}
/>
<FolderSettingsDialog
<TemplateFolderSettingsDialog
folder={folderToSettings}
isOpen={isSettingsFolderOpen}
onOpenChange={(open) => {
@ -353,7 +260,7 @@ export default function TemplatesPage() {
}}
/>
<FolderDeleteDialog
<TemplateFolderDeleteDialog
folder={folderToDelete}
isOpen={isDeletingFolder}
onOpenChange={(open) => {