mirror of
https://github.com/documenso/documenso.git
synced 2025-11-17 10:11:35 +10:00
feat: add team templates (#912)
This commit is contained in:
@ -25,7 +25,7 @@ export type DocumentPageViewProps = {
|
||||
team?: Team;
|
||||
};
|
||||
|
||||
export default async function DocumentPageView({ params, team }: DocumentPageViewProps) {
|
||||
export const DocumentPageView = async ({ params, team }: DocumentPageViewProps) => {
|
||||
const { id } = params;
|
||||
|
||||
const documentId = Number(id);
|
||||
@ -128,4 +128,4 @@ export default async function DocumentPageView({ params, team }: DocumentPageVie
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import DocumentPageView from './document-page-view';
|
||||
import { DocumentPageView } from './document-page-view';
|
||||
|
||||
export type DocumentPageProps = {
|
||||
params: {
|
||||
|
||||
@ -33,10 +33,7 @@ export type DocumentsPageViewProps = {
|
||||
team?: Team & { teamEmail?: TeamEmail | null };
|
||||
};
|
||||
|
||||
export default async function DocumentsPageView({
|
||||
searchParams = {},
|
||||
team,
|
||||
}: DocumentsPageViewProps) {
|
||||
export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPageViewProps) => {
|
||||
const { user } = await getRequiredServerComponentSession();
|
||||
|
||||
const status = isExtendedDocumentStatus(searchParams.status) ? searchParams.status : 'ALL';
|
||||
@ -155,4 +152,4 @@ export default async function DocumentsPageView({
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
import type { DocumentsPageViewProps } from './documents-page-view';
|
||||
import DocumentsPageView from './documents-page-view';
|
||||
import { DocumentsPageView } from './documents-page-view';
|
||||
|
||||
export type DocumentsPageProps = {
|
||||
searchParams?: DocumentsPageViewProps['searchParams'];
|
||||
|
||||
@ -28,6 +28,7 @@ export type EditTemplateFormProps = {
|
||||
recipients: Recipient[];
|
||||
fields: Field[];
|
||||
documentData: DocumentData;
|
||||
templateRootPath: string;
|
||||
};
|
||||
|
||||
type EditTemplateStep = 'signers' | 'fields';
|
||||
@ -40,6 +41,7 @@ export const EditTemplateForm = ({
|
||||
fields,
|
||||
user: _user,
|
||||
documentData,
|
||||
templateRootPath,
|
||||
}: EditTemplateFormProps) => {
|
||||
const { toast } = useToast();
|
||||
const router = useRouter();
|
||||
@ -98,7 +100,7 @@ export const EditTemplateForm = ({
|
||||
duration: 5000,
|
||||
});
|
||||
|
||||
router.push('/templates');
|
||||
router.push(templateRootPath);
|
||||
} catch (err) {
|
||||
toast({
|
||||
title: 'Error',
|
||||
|
||||
@ -1,81 +1,10 @@
|
||||
import React from 'react';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { redirect } from 'next/navigation';
|
||||
import type { TemplatePageViewProps } from './template-page-view';
|
||||
import { TemplatePageView } from './template-page-view';
|
||||
|
||||
import { ChevronLeft } from 'lucide-react';
|
||||
type TemplatePageProps = Pick<TemplatePageViewProps, 'params'>;
|
||||
|
||||
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
||||
import { getFieldsForTemplate } from '@documenso/lib/server-only/field/get-fields-for-template';
|
||||
import { getRecipientsForTemplate } from '@documenso/lib/server-only/recipient/get-recipients-for-template';
|
||||
import { getTemplateById } from '@documenso/lib/server-only/template/get-template-by-id';
|
||||
|
||||
import { TemplateType } from '~/components/formatter/template-type';
|
||||
|
||||
import { EditTemplateForm } from './edit-template';
|
||||
|
||||
export type TemplatePageProps = {
|
||||
params: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
export default async function TemplatePage({ params }: TemplatePageProps) {
|
||||
const { id } = params;
|
||||
|
||||
const templateId = Number(id);
|
||||
|
||||
if (!templateId || Number.isNaN(templateId)) {
|
||||
redirect('/documents');
|
||||
}
|
||||
|
||||
const { user } = await getRequiredServerComponentSession();
|
||||
|
||||
const template = await getTemplateById({
|
||||
id: templateId,
|
||||
userId: user.id,
|
||||
}).catch(() => null);
|
||||
|
||||
if (!template || !template.templateDocumentData) {
|
||||
redirect('/documents');
|
||||
}
|
||||
|
||||
const { templateDocumentData } = template;
|
||||
|
||||
const [templateRecipients, templateFields] = await Promise.all([
|
||||
getRecipientsForTemplate({
|
||||
templateId,
|
||||
userId: user.id,
|
||||
}),
|
||||
getFieldsForTemplate({
|
||||
templateId,
|
||||
userId: user.id,
|
||||
}),
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-screen-xl px-4 md:px-8">
|
||||
<Link href="/templates" className="flex items-center text-[#7AC455] hover:opacity-80">
|
||||
<ChevronLeft className="mr-2 inline-block h-5 w-5" />
|
||||
Templates
|
||||
</Link>
|
||||
|
||||
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl" title={template.title}>
|
||||
{template.title}
|
||||
</h1>
|
||||
|
||||
<div className="mt-2.5 flex items-center gap-x-6">
|
||||
<TemplateType inheritColor type={template.type} className="text-muted-foreground" />
|
||||
</div>
|
||||
|
||||
<EditTemplateForm
|
||||
className="mt-8"
|
||||
template={template}
|
||||
user={user}
|
||||
recipients={templateRecipients}
|
||||
fields={templateFields}
|
||||
documentData={templateDocumentData}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
export default function TemplatePage({ params }: TemplatePageProps) {
|
||||
return <TemplatePageView params={params} />;
|
||||
}
|
||||
|
||||
@ -0,0 +1,86 @@
|
||||
import React from 'react';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
import { ChevronLeft } from 'lucide-react';
|
||||
|
||||
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
||||
import { getFieldsForTemplate } from '@documenso/lib/server-only/field/get-fields-for-template';
|
||||
import { getRecipientsForTemplate } from '@documenso/lib/server-only/recipient/get-recipients-for-template';
|
||||
import { getTemplateById } from '@documenso/lib/server-only/template/get-template-by-id';
|
||||
import { formatTemplatesPath } from '@documenso/lib/utils/teams';
|
||||
import type { Team } from '@documenso/prisma/client';
|
||||
|
||||
import { TemplateType } from '~/components/formatter/template-type';
|
||||
|
||||
import { EditTemplateForm } from './edit-template';
|
||||
|
||||
export type TemplatePageViewProps = {
|
||||
params: {
|
||||
id: string;
|
||||
};
|
||||
team?: Team;
|
||||
};
|
||||
|
||||
export const TemplatePageView = async ({ params, team }: TemplatePageViewProps) => {
|
||||
const { id } = params;
|
||||
|
||||
const templateId = Number(id);
|
||||
const templateRootPath = formatTemplatesPath(team?.url);
|
||||
|
||||
if (!templateId || Number.isNaN(templateId)) {
|
||||
redirect(templateRootPath);
|
||||
}
|
||||
|
||||
const { user } = await getRequiredServerComponentSession();
|
||||
|
||||
const template = await getTemplateById({
|
||||
id: templateId,
|
||||
userId: user.id,
|
||||
}).catch(() => null);
|
||||
|
||||
if (!template || !template.templateDocumentData) {
|
||||
redirect(templateRootPath);
|
||||
}
|
||||
|
||||
const { templateDocumentData } = template;
|
||||
|
||||
const [templateRecipients, templateFields] = await Promise.all([
|
||||
getRecipientsForTemplate({
|
||||
templateId,
|
||||
userId: user.id,
|
||||
}),
|
||||
getFieldsForTemplate({
|
||||
templateId,
|
||||
userId: user.id,
|
||||
}),
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-screen-xl px-4 md:px-8">
|
||||
<Link href="/templates" className="flex items-center text-[#7AC455] hover:opacity-80">
|
||||
<ChevronLeft className="mr-2 inline-block h-5 w-5" />
|
||||
Templates
|
||||
</Link>
|
||||
|
||||
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl" title={template.title}>
|
||||
{template.title}
|
||||
</h1>
|
||||
|
||||
<div className="mt-2.5 flex items-center gap-x-6">
|
||||
<TemplateType inheritColor type={template.type} className="text-muted-foreground" />
|
||||
</div>
|
||||
|
||||
<EditTemplateForm
|
||||
className="mt-8"
|
||||
template={template}
|
||||
user={user}
|
||||
recipients={templateRecipients}
|
||||
fields={templateFields}
|
||||
documentData={templateDocumentData}
|
||||
templateRootPath={templateRootPath}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -21,9 +21,15 @@ import { DuplicateTemplateDialog } from './duplicate-template-dialog';
|
||||
|
||||
export type DataTableActionDropdownProps = {
|
||||
row: Template;
|
||||
templateRootPath: string;
|
||||
teamId?: number;
|
||||
};
|
||||
|
||||
export const DataTableActionDropdown = ({ row }: DataTableActionDropdownProps) => {
|
||||
export const DataTableActionDropdown = ({
|
||||
row,
|
||||
templateRootPath,
|
||||
teamId,
|
||||
}: DataTableActionDropdownProps) => {
|
||||
const { data: session } = useSession();
|
||||
|
||||
const [isDeleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||
@ -34,6 +40,7 @@ export const DataTableActionDropdown = ({ row }: DataTableActionDropdownProps) =
|
||||
}
|
||||
|
||||
const isOwner = row.userId === session.user.id;
|
||||
const isTeamTemplate = row.teamId === teamId;
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
@ -44,20 +51,25 @@ export const DataTableActionDropdown = ({ row }: DataTableActionDropdownProps) =
|
||||
<DropdownMenuContent className="w-52" align="start" forceMount>
|
||||
<DropdownMenuLabel>Action</DropdownMenuLabel>
|
||||
|
||||
<DropdownMenuItem disabled={!isOwner} asChild>
|
||||
<Link href={`/templates/${row.id}`}>
|
||||
<DropdownMenuItem disabled={!isOwner && !isTeamTemplate} asChild>
|
||||
<Link href={`${templateRootPath}/${row.id}`}>
|
||||
<Edit className="mr-2 h-4 w-4" />
|
||||
Edit
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
|
||||
{/* <DropdownMenuItem disabled={!isOwner} onClick={async () => onDuplicateButtonClick(row.id)}> */}
|
||||
<DropdownMenuItem disabled={!isOwner} onClick={() => setDuplicateDialogOpen(true)}>
|
||||
<DropdownMenuItem
|
||||
disabled={!isOwner && !isTeamTemplate}
|
||||
onClick={() => setDuplicateDialogOpen(true)}
|
||||
>
|
||||
<Copy className="mr-2 h-4 w-4" />
|
||||
Duplicate
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem disabled={!isOwner} onClick={() => setDeleteDialogOpen(true)}>
|
||||
<DropdownMenuItem
|
||||
disabled={!isOwner && !isTeamTemplate}
|
||||
onClick={() => setDeleteDialogOpen(true)}
|
||||
>
|
||||
<Trash2 className="mr-2 h-4 w-4" />
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
@ -65,6 +77,7 @@ export const DataTableActionDropdown = ({ row }: DataTableActionDropdownProps) =
|
||||
|
||||
<DuplicateTemplateDialog
|
||||
id={row.id}
|
||||
teamId={teamId}
|
||||
open={isDuplicateDialogOpen}
|
||||
onOpenChange={setDuplicateDialogOpen}
|
||||
/>
|
||||
|
||||
@ -28,6 +28,9 @@ type TemplatesDataTableProps = {
|
||||
perPage: number;
|
||||
page: number;
|
||||
totalPages: number;
|
||||
documentRootPath: string;
|
||||
templateRootPath: string;
|
||||
teamId?: number;
|
||||
};
|
||||
|
||||
export const TemplatesDataTable = ({
|
||||
@ -35,6 +38,9 @@ export const TemplatesDataTable = ({
|
||||
perPage,
|
||||
page,
|
||||
totalPages,
|
||||
documentRootPath,
|
||||
templateRootPath,
|
||||
teamId,
|
||||
}: TemplatesDataTableProps) => {
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const updateSearchParams = useUpdateSearchParams();
|
||||
@ -70,7 +76,7 @@ export const TemplatesDataTable = ({
|
||||
duration: 5000,
|
||||
});
|
||||
|
||||
router.push(`/documents/${id}`);
|
||||
router.push(`${documentRootPath}/${id}`);
|
||||
} catch (err) {
|
||||
toast({
|
||||
title: 'Error',
|
||||
@ -131,7 +137,12 @@ export const TemplatesDataTable = ({
|
||||
{!isRowLoading && <Plus className="-ml-1 mr-2 h-4 w-4" />}
|
||||
Use Template
|
||||
</Button>
|
||||
<DataTableActionDropdown row={row.original} />
|
||||
|
||||
<DataTableActionDropdown
|
||||
row={row.original}
|
||||
teamId={teamId}
|
||||
templateRootPath={templateRootPath}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
@ -35,20 +35,15 @@ export const DeleteTemplateDialog = ({ id, open, onOpenChange }: DeleteTemplateD
|
||||
|
||||
onOpenChange(false);
|
||||
},
|
||||
});
|
||||
|
||||
const onDeleteTemplate = async () => {
|
||||
try {
|
||||
await deleteTemplate({ id });
|
||||
} catch {
|
||||
onError: () => {
|
||||
toast({
|
||||
title: 'Something went wrong',
|
||||
description: 'This template could not be deleted at this time. Please try again.',
|
||||
variant: 'destructive',
|
||||
duration: 7500,
|
||||
});
|
||||
}
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={(value) => !isLoading && onOpenChange(value)}>
|
||||
@ -63,20 +58,18 @@ export const DeleteTemplateDialog = ({ id, open, onOpenChange }: DeleteTemplateD
|
||||
</DialogHeader>
|
||||
|
||||
<DialogFooter>
|
||||
<div className="flex w-full flex-1 flex-nowrap gap-4">
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={() => onOpenChange(false)}
|
||||
className="flex-1"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
disabled={isLoading}
|
||||
onClick={() => onOpenChange(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
<Button type="button" loading={isLoading} onClick={onDeleteTemplate} className="flex-1">
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
<Button type="button" loading={isLoading} onClick={async () => deleteTemplate({ id })}>
|
||||
Delete
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@ -14,12 +14,14 @@ import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
type DuplicateTemplateDialogProps = {
|
||||
id: number;
|
||||
teamId?: number;
|
||||
open: boolean;
|
||||
onOpenChange: (_open: boolean) => void;
|
||||
};
|
||||
|
||||
export const DuplicateTemplateDialog = ({
|
||||
id,
|
||||
teamId,
|
||||
open,
|
||||
onOpenChange,
|
||||
}: DuplicateTemplateDialogProps) => {
|
||||
@ -40,22 +42,15 @@ export const DuplicateTemplateDialog = ({
|
||||
|
||||
onOpenChange(false);
|
||||
},
|
||||
onError: () => {
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'An error occurred while duplicating template.',
|
||||
variant: 'destructive',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const onDuplicate = async () => {
|
||||
try {
|
||||
await duplicateTemplate({
|
||||
templateId: id,
|
||||
});
|
||||
} catch (err) {
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'An error occurred while duplicating template.',
|
||||
variant: 'destructive',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={(value) => !isLoading && onOpenChange(value)}>
|
||||
<DialogContent>
|
||||
@ -66,20 +61,27 @@ export const DuplicateTemplateDialog = ({
|
||||
</DialogHeader>
|
||||
|
||||
<DialogFooter>
|
||||
<div className="flex w-full flex-1 flex-nowrap gap-4">
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={() => onOpenChange(false)}
|
||||
className="flex-1"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
disabled={isLoading}
|
||||
variant="secondary"
|
||||
onClick={() => onOpenChange(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
<Button type="button" loading={isLoading} onClick={onDuplicate} className="flex-1">
|
||||
Duplicate
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
loading={isLoading}
|
||||
onClick={async () =>
|
||||
duplicateTemplate({
|
||||
templateId: id,
|
||||
teamId,
|
||||
})
|
||||
}
|
||||
>
|
||||
Duplicate
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@ -43,8 +43,14 @@ const ZCreateTemplateFormSchema = z.object({
|
||||
|
||||
type TCreateTemplateFormSchema = z.infer<typeof ZCreateTemplateFormSchema>;
|
||||
|
||||
export const NewTemplateDialog = () => {
|
||||
type NewTemplateDialogProps = {
|
||||
teamId?: number;
|
||||
templateRootPath: string;
|
||||
};
|
||||
|
||||
export const NewTemplateDialog = ({ teamId, templateRootPath }: NewTemplateDialogProps) => {
|
||||
const router = useRouter();
|
||||
|
||||
const { data: session } = useSession();
|
||||
const { toast } = useToast();
|
||||
|
||||
@ -99,6 +105,7 @@ export const NewTemplateDialog = () => {
|
||||
});
|
||||
|
||||
const { id } = await createTemplate({
|
||||
teamId,
|
||||
title: values.name ? values.name : file.name,
|
||||
templateDocumentDataId,
|
||||
});
|
||||
@ -112,7 +119,7 @@ export const NewTemplateDialog = () => {
|
||||
|
||||
setShowNewTemplateDialog(false);
|
||||
|
||||
void router.push(`/templates/${id}`);
|
||||
router.push(`${templateRootPath}/${id}`);
|
||||
} catch {
|
||||
toast({
|
||||
title: 'Something went wrong',
|
||||
|
||||
@ -2,57 +2,17 @@ import React from 'react';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
||||
import { getTemplates } from '@documenso/lib/server-only/template/get-templates';
|
||||
|
||||
import { TemplatesDataTable } from './data-table-templates';
|
||||
import { EmptyTemplateState } from './empty-state';
|
||||
import { NewTemplateDialog } from './new-template-dialog';
|
||||
import { TemplatesPageView } from './templates-page-view';
|
||||
import type { TemplatesPageViewProps } from './templates-page-view';
|
||||
|
||||
type TemplatesPageProps = {
|
||||
searchParams?: {
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
};
|
||||
searchParams?: TemplatesPageViewProps['searchParams'];
|
||||
};
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Templates',
|
||||
};
|
||||
|
||||
export default async function TemplatesPage({ searchParams = {} }: TemplatesPageProps) {
|
||||
const { user } = await getRequiredServerComponentSession();
|
||||
const page = Number(searchParams.page) || 1;
|
||||
const perPage = Number(searchParams.perPage) || 10;
|
||||
|
||||
const { templates, totalPages } = await getTemplates({
|
||||
userId: user.id,
|
||||
page: page,
|
||||
perPage: perPage,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-screen-xl px-4 md:px-8">
|
||||
<div className="flex items-baseline justify-between">
|
||||
<h1 className="mb-5 mt-2 truncate text-2xl font-semibold md:text-3xl">Templates</h1>
|
||||
|
||||
<div>
|
||||
<NewTemplateDialog />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
{templates.length > 0 ? (
|
||||
<TemplatesDataTable
|
||||
templates={templates}
|
||||
page={page}
|
||||
perPage={perPage}
|
||||
totalPages={totalPages}
|
||||
/>
|
||||
) : (
|
||||
<EmptyTemplateState />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
export default function TemplatesPage({ searchParams = {} }: TemplatesPageProps) {
|
||||
return <TemplatesPageView searchParams={searchParams} />;
|
||||
}
|
||||
|
||||
@ -0,0 +1,73 @@
|
||||
import React from 'react';
|
||||
|
||||
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
||||
import { findTemplates } from '@documenso/lib/server-only/template/find-templates';
|
||||
import { formatDocumentsPath, formatTemplatesPath } from '@documenso/lib/utils/teams';
|
||||
import type { Team } from '@documenso/prisma/client';
|
||||
import { Avatar, AvatarFallback } from '@documenso/ui/primitives/avatar';
|
||||
|
||||
import { TemplatesDataTable } from './data-table-templates';
|
||||
import { EmptyTemplateState } from './empty-state';
|
||||
import { NewTemplateDialog } from './new-template-dialog';
|
||||
|
||||
export type TemplatesPageViewProps = {
|
||||
searchParams?: {
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
};
|
||||
team?: Team;
|
||||
};
|
||||
|
||||
export const TemplatesPageView = async ({ searchParams = {}, team }: TemplatesPageViewProps) => {
|
||||
const { user } = await getRequiredServerComponentSession();
|
||||
const page = Number(searchParams.page) || 1;
|
||||
const perPage = Number(searchParams.perPage) || 10;
|
||||
|
||||
const documentRootPath = formatDocumentsPath(team?.url);
|
||||
const templateRootPath = formatTemplatesPath(team?.url);
|
||||
|
||||
const { templates, totalPages } = await findTemplates({
|
||||
userId: user.id,
|
||||
teamId: team?.id,
|
||||
page: page,
|
||||
perPage: perPage,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-screen-xl px-4 md:px-8">
|
||||
<div className="flex items-baseline justify-between">
|
||||
<div className="flex flex-row items-center">
|
||||
{team && (
|
||||
<Avatar className="dark:border-border mr-3 h-12 w-12 border-2 border-solid border-white">
|
||||
<AvatarFallback className="text-xs text-gray-400">
|
||||
{team.name.slice(0, 1)}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
)}
|
||||
|
||||
<h1 className="truncate text-2xl font-semibold md:text-3xl">Templates</h1>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<NewTemplateDialog templateRootPath={templateRootPath} teamId={team?.id} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative mt-5">
|
||||
{templates.length > 0 ? (
|
||||
<TemplatesDataTable
|
||||
templates={templates}
|
||||
page={page}
|
||||
perPage={perPage}
|
||||
totalPages={totalPages}
|
||||
documentRootPath={documentRootPath}
|
||||
templateRootPath={templateRootPath}
|
||||
teamId={team?.id}
|
||||
/>
|
||||
) : (
|
||||
<EmptyTemplateState />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -1,7 +1,7 @@
|
||||
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
||||
import { getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
|
||||
|
||||
import DocumentPageComponent from '~/app/(dashboard)/documents/[id]/document-page-view';
|
||||
import { DocumentPageView } from '~/app/(dashboard)/documents/[id]/document-page-view';
|
||||
|
||||
export type DocumentPageProps = {
|
||||
params: {
|
||||
@ -16,5 +16,5 @@ export default async function DocumentPage({ params }: DocumentPageProps) {
|
||||
const { user } = await getRequiredServerComponentSession();
|
||||
const team = await getTeamByUrl({ userId: user.id, teamUrl });
|
||||
|
||||
return <DocumentPageComponent params={params} team={team} />;
|
||||
return <DocumentPageView params={params} team={team} />;
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-
|
||||
import { getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
|
||||
|
||||
import type { DocumentsPageViewProps } from '~/app/(dashboard)/documents/documents-page-view';
|
||||
import DocumentsPageView from '~/app/(dashboard)/documents/documents-page-view';
|
||||
import { DocumentsPageView } from '~/app/(dashboard)/documents/documents-page-view';
|
||||
|
||||
export type TeamsDocumentPageProps = {
|
||||
params: {
|
||||
|
||||
22
apps/web/src/app/(teams)/t/[teamUrl]/templates/[id]/page.tsx
Normal file
22
apps/web/src/app/(teams)/t/[teamUrl]/templates/[id]/page.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
|
||||
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
||||
import { getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
|
||||
|
||||
import type { TemplatePageViewProps } from '~/app/(dashboard)/templates/[id]/template-page-view';
|
||||
import { TemplatePageView } from '~/app/(dashboard)/templates/[id]/template-page-view';
|
||||
|
||||
type TeamTemplatePageProps = {
|
||||
params: TemplatePageViewProps['params'] & {
|
||||
teamUrl: string;
|
||||
};
|
||||
};
|
||||
|
||||
export default async function TeamTemplatePage({ params }: TeamTemplatePageProps) {
|
||||
const { teamUrl } = params;
|
||||
|
||||
const { user } = await getRequiredServerComponentSession();
|
||||
const team = await getTeamByUrl({ userId: user.id, teamUrl });
|
||||
|
||||
return <TemplatePageView params={params} team={team} />;
|
||||
}
|
||||
26
apps/web/src/app/(teams)/t/[teamUrl]/templates/page.tsx
Normal file
26
apps/web/src/app/(teams)/t/[teamUrl]/templates/page.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
|
||||
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
||||
import { getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
|
||||
|
||||
import type { TemplatesPageViewProps } from '~/app/(dashboard)/templates/templates-page-view';
|
||||
import { TemplatesPageView } from '~/app/(dashboard)/templates/templates-page-view';
|
||||
|
||||
type TeamTemplatesPageProps = {
|
||||
searchParams?: TemplatesPageViewProps['searchParams'];
|
||||
params: {
|
||||
teamUrl: string;
|
||||
};
|
||||
};
|
||||
|
||||
export default async function TeamTemplatesPage({
|
||||
searchParams = {},
|
||||
params,
|
||||
}: TeamTemplatesPageProps) {
|
||||
const { teamUrl } = params;
|
||||
|
||||
const { user } = await getRequiredServerComponentSession();
|
||||
const team = await getTeamByUrl({ userId: user.id, teamUrl });
|
||||
|
||||
return <TemplatesPageView searchParams={searchParams} team={team} />;
|
||||
}
|
||||
Reference in New Issue
Block a user