mirror of
https://github.com/documenso/documenso.git
synced 2025-11-16 01:32:06 +10:00
feat: templates (#537)
Adds the basically ability to create and use templates for repetitive document types
This commit is contained in:
@ -3,6 +3,9 @@
|
||||
import type { HTMLAttributes } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
import { Search } from 'lucide-react';
|
||||
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
@ -10,10 +13,22 @@ import { Button } from '@documenso/ui/primitives/button';
|
||||
|
||||
import { CommandMenu } from '../common/command-menu';
|
||||
|
||||
const navigationLinks = [
|
||||
{
|
||||
href: '/documents',
|
||||
label: 'Documents',
|
||||
},
|
||||
{
|
||||
href: '/templates',
|
||||
label: 'Templates',
|
||||
},
|
||||
];
|
||||
|
||||
export type DesktopNavProps = HTMLAttributes<HTMLDivElement>;
|
||||
|
||||
export const DesktopNav = ({ className, ...props }: DesktopNavProps) => {
|
||||
// const pathname = usePathname();
|
||||
const pathname = usePathname();
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [modifierKey, setModifierKey] = useState(() => 'Ctrl');
|
||||
|
||||
@ -26,9 +41,29 @@ export const DesktopNav = ({ className, ...props }: DesktopNavProps) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn('ml-8 hidden flex-1 gap-x-6 md:flex md:justify-center', className)}
|
||||
className={cn(
|
||||
'ml-8 hidden flex-1 items-center gap-x-12 md:flex md:justify-between',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="flex items-baseline gap-x-6">
|
||||
{navigationLinks.map(({ href, label }) => (
|
||||
<Link
|
||||
key={href}
|
||||
href={href}
|
||||
className={cn(
|
||||
'text-muted-foreground dark:text-muted focus-visible:ring-ring ring-offset-background rounded-md font-medium leading-5 hover:opacity-80 focus-visible:outline-none focus-visible:ring-2',
|
||||
{
|
||||
'text-foreground dark:text-muted-foreground': pathname?.startsWith(href),
|
||||
},
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<CommandMenu open={open} onOpenChange={setOpen} />
|
||||
|
||||
<Button
|
||||
@ -47,19 +82,6 @@ export const DesktopNav = ({ className, ...props }: DesktopNavProps) => {
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
|
||||
{/* We have no other subpaths rn */}
|
||||
{/* <Link
|
||||
href="/documents"
|
||||
className={cn(
|
||||
'text-muted-foreground focus-visible:ring-ring ring-offset-background rounded-md font-medium leading-5 hover:opacity-80 focus-visible:outline-none focus-visible:ring-2',
|
||||
{
|
||||
'text-foreground': pathname?.startsWith('/documents'),
|
||||
},
|
||||
)}
|
||||
>
|
||||
Documents
|
||||
</Link> */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -49,7 +49,7 @@ export const Header = ({ className, user, ...props }: HeaderProps) => {
|
||||
|
||||
<DesktopNav />
|
||||
|
||||
<div className="flex gap-x-4">
|
||||
<div className="flex gap-x-4 md:ml-8">
|
||||
<ProfileDropdown user={user} />
|
||||
|
||||
{/* <Button variant="outline" size="sm" className="h-10 w-10 p-0.5 md:hidden">
|
||||
|
||||
@ -4,6 +4,7 @@ import Link from 'next/link';
|
||||
|
||||
import {
|
||||
CreditCard,
|
||||
FileSpreadsheet,
|
||||
Lock,
|
||||
LogOut,
|
||||
User as LucideUser,
|
||||
@ -106,6 +107,13 @@ export const ProfileDropdown = ({ user }: ProfileDropdownProps) => {
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href="/templates" className="cursor-pointer">
|
||||
<FileSpreadsheet className="mr-2 h-4 w-4" />
|
||||
Templates
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
<DropdownMenuSub>
|
||||
|
||||
50
apps/web/src/components/formatter/template-type.tsx
Normal file
50
apps/web/src/components/formatter/template-type.tsx
Normal file
@ -0,0 +1,50 @@
|
||||
import { HTMLAttributes } from 'react';
|
||||
|
||||
import { Globe, Lock } from 'lucide-react';
|
||||
import type { LucideIcon } from 'lucide-react/dist/lucide-react';
|
||||
|
||||
import { TemplateType as TemplateTypePrisma } from '@documenso/prisma/client';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
|
||||
type TemplateTypeIcon = {
|
||||
label: string;
|
||||
icon?: LucideIcon;
|
||||
color: string;
|
||||
};
|
||||
|
||||
type TemplateTypes = (typeof TemplateTypePrisma)[keyof typeof TemplateTypePrisma];
|
||||
|
||||
const TEMPLATE_TYPES: Record<TemplateTypes, TemplateTypeIcon> = {
|
||||
PRIVATE: {
|
||||
label: 'Private',
|
||||
icon: Lock,
|
||||
color: 'text-blue-600 dark:text-blue-300',
|
||||
},
|
||||
PUBLIC: {
|
||||
label: 'Public',
|
||||
icon: Globe,
|
||||
color: 'text-green-500 dark:text-green-300',
|
||||
},
|
||||
};
|
||||
|
||||
export type TemplateTypeProps = HTMLAttributes<HTMLSpanElement> & {
|
||||
type: TemplateTypes;
|
||||
inheritColor?: boolean;
|
||||
};
|
||||
|
||||
export const TemplateType = ({ className, type, inheritColor, ...props }: TemplateTypeProps) => {
|
||||
const { label, icon: Icon, color } = TEMPLATE_TYPES[type];
|
||||
|
||||
return (
|
||||
<span className={cn('flex items-center', className)} {...props}>
|
||||
{Icon && (
|
||||
<Icon
|
||||
className={cn('mr-2 inline-block h-4 w-4', {
|
||||
[color]: !inheritColor,
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
{label}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user