mirror of
https://github.com/documenso/documenso.git
synced 2025-11-24 21:51:40 +10:00
feat: web i18n (#1286)
This commit is contained in:
@ -4,6 +4,9 @@ import { useEffect, useState } from 'react';
|
||||
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import { msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
|
||||
import {
|
||||
DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
|
||||
SKIP_QUERY_BATCH_META,
|
||||
@ -42,7 +45,9 @@ export const EditTemplateForm = ({
|
||||
isEnterprise,
|
||||
templateRootPath,
|
||||
}: EditTemplateFormProps) => {
|
||||
const { _ } = useLingui();
|
||||
const { toast } = useToast();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const team = useOptionalCurrentTeam();
|
||||
@ -68,18 +73,18 @@ export const EditTemplateForm = ({
|
||||
|
||||
const documentFlow: Record<EditTemplateStep, DocumentFlowStep> = {
|
||||
settings: {
|
||||
title: 'General',
|
||||
description: 'Configure general settings for the template.',
|
||||
title: msg`General`,
|
||||
description: msg`Configure general settings for the template.`,
|
||||
stepIndex: 1,
|
||||
},
|
||||
signers: {
|
||||
title: 'Add Placeholders',
|
||||
description: 'Add all relevant placeholders for each recipient.',
|
||||
title: msg`Add Placeholders`,
|
||||
description: msg`Add all relevant placeholders for each recipient.`,
|
||||
stepIndex: 2,
|
||||
},
|
||||
fields: {
|
||||
title: 'Add Fields',
|
||||
description: 'Add all relevant fields for each recipient.',
|
||||
title: msg`Add Fields`,
|
||||
description: msg`Add all relevant fields for each recipient.`,
|
||||
stepIndex: 3,
|
||||
},
|
||||
};
|
||||
@ -144,8 +149,8 @@ export const EditTemplateForm = ({
|
||||
console.error(err);
|
||||
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'An error occurred while updating the document settings.',
|
||||
title: _(msg`Error`),
|
||||
description: _(msg`An error occurred while updating the document settings.`),
|
||||
variant: 'destructive',
|
||||
});
|
||||
}
|
||||
@ -167,8 +172,8 @@ export const EditTemplateForm = ({
|
||||
setStep('fields');
|
||||
} catch (err) {
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'An error occurred while adding signers.',
|
||||
title: _(msg`Error`),
|
||||
description: _(msg`An error occurred while adding signers.`),
|
||||
variant: 'destructive',
|
||||
});
|
||||
}
|
||||
@ -190,8 +195,8 @@ export const EditTemplateForm = ({
|
||||
}
|
||||
|
||||
toast({
|
||||
title: 'Template saved',
|
||||
description: 'Your templates has been saved successfully.',
|
||||
title: _(msg`Template saved`),
|
||||
description: _(msg`Your templates has been saved successfully.`),
|
||||
duration: 5000,
|
||||
});
|
||||
|
||||
@ -201,8 +206,8 @@ export const EditTemplateForm = ({
|
||||
router.push(templateRootPath);
|
||||
} catch (err) {
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'An error occurred while adding signers.',
|
||||
title: _(msg`Error`),
|
||||
description: _(msg`An error occurred while adding signers.`),
|
||||
variant: 'destructive',
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,10 +1,14 @@
|
||||
import React from 'react';
|
||||
|
||||
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
|
||||
|
||||
import type { TemplatePageViewProps } from './template-page-view';
|
||||
import { TemplatePageView } from './template-page-view';
|
||||
|
||||
type TemplatePageProps = Pick<TemplatePageViewProps, 'params'>;
|
||||
|
||||
export default function TemplatePage({ params }: TemplatePageProps) {
|
||||
setupI18nSSR();
|
||||
|
||||
return <TemplatePageView params={params} />;
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { LinkIcon } from 'lucide-react';
|
||||
|
||||
import type { Recipient, Template, TemplateDirectLink } from '@documenso/prisma/client';
|
||||
@ -27,7 +28,12 @@ export const TemplateDirectLinkDialogWrapper = ({ template }: TemplatePageViewPr
|
||||
}}
|
||||
>
|
||||
<LinkIcon className="mr-1.5 h-3.5 w-3.5" />
|
||||
{template.directLink ? 'Manage' : 'Create'} Direct Link
|
||||
|
||||
{template.directLink ? (
|
||||
<Trans>Manage Direct Link</Trans>
|
||||
) : (
|
||||
<Trans>Create Direct Link</Trans>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
<TemplateDirectLinkDialog
|
||||
|
||||
@ -3,6 +3,7 @@ import React from 'react';
|
||||
import Link from 'next/link';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { ChevronLeft } from 'lucide-react';
|
||||
|
||||
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
|
||||
@ -56,7 +57,7 @@ export const TemplatePageView = async ({ params, team }: TemplatePageViewProps)
|
||||
<div>
|
||||
<Link href="/templates" className="flex items-center text-[#7AC455] hover:opacity-80">
|
||||
<ChevronLeft className="mr-2 inline-block h-5 w-5" />
|
||||
Templates
|
||||
<Trans>Templates</Trans>
|
||||
</Link>
|
||||
|
||||
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl" title={template.title}>
|
||||
|
||||
@ -4,6 +4,7 @@ import { useState } from 'react';
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { Copy, Edit, MoreHorizontal, MoveRight, Share2Icon, Trash2 } from 'lucide-react';
|
||||
import { useSession } from 'next-auth/react';
|
||||
|
||||
@ -58,7 +59,7 @@ export const DataTableActionDropdown = ({
|
||||
<DropdownMenuItem disabled={!isOwner && !isTeamTemplate} asChild>
|
||||
<Link href={`${templateRootPath}/${row.id}`}>
|
||||
<Edit className="mr-2 h-4 w-4" />
|
||||
Edit
|
||||
<Trans>Edit</Trans>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
|
||||
@ -67,18 +68,18 @@ export const DataTableActionDropdown = ({
|
||||
onClick={() => setDuplicateDialogOpen(true)}
|
||||
>
|
||||
<Copy className="mr-2 h-4 w-4" />
|
||||
Duplicate
|
||||
<Trans>Duplicate</Trans>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem onClick={() => setTemplateDirectLinkDialogOpen(true)}>
|
||||
<Share2Icon className="mr-2 h-4 w-4" />
|
||||
Direct link
|
||||
<Trans>Direct link</Trans>
|
||||
</DropdownMenuItem>
|
||||
|
||||
{!teamId && (
|
||||
<DropdownMenuItem onClick={() => setMoveDialogOpen(true)}>
|
||||
<MoveRight className="mr-2 h-4 w-4" />
|
||||
Move to Team
|
||||
<Trans>Move to Team</Trans>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
|
||||
@ -87,7 +88,7 @@ export const DataTableActionDropdown = ({
|
||||
onClick={() => setDeleteDialogOpen(true)}
|
||||
>
|
||||
<Trash2 className="mr-2 h-4 w-4" />
|
||||
Delete
|
||||
<Trans>Delete</Trans>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
|
||||
|
||||
@ -4,6 +4,8 @@ import { useTransition } from 'react';
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
import { Trans, msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { AlertTriangle, Globe2Icon, InfoIcon, Link2Icon, Loader, LockIcon } from 'lucide-react';
|
||||
|
||||
import { useLimits } from '@documenso/ee/server-only/limits/provider/client';
|
||||
@ -45,6 +47,7 @@ export const TemplatesDataTable = ({
|
||||
|
||||
const updateSearchParams = useUpdateSearchParams();
|
||||
|
||||
const { _ } = useLingui();
|
||||
const { remaining } = useLimits();
|
||||
|
||||
const onPaginationChange = (page: number, perPage: number) => {
|
||||
@ -61,12 +64,16 @@ export const TemplatesDataTable = ({
|
||||
{remaining.documents === 0 && (
|
||||
<Alert variant="warning" className="mb-4">
|
||||
<AlertTriangle className="h-4 w-4" />
|
||||
<AlertTitle>Document Limit Exceeded!</AlertTitle>
|
||||
<AlertTitle>
|
||||
<Trans>Document Limit Exceeded!</Trans>
|
||||
</AlertTitle>
|
||||
<AlertDescription className="mt-2">
|
||||
You have reached your document limit.{' '}
|
||||
<Link className="underline underline-offset-4" href="/settings/billing">
|
||||
Upgrade your account to continue!
|
||||
</Link>
|
||||
<Trans>
|
||||
You have reached your document limit.{' '}
|
||||
<Link className="underline underline-offset-4" href="/settings/billing">
|
||||
Upgrade your account to continue!
|
||||
</Link>
|
||||
</Trans>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
@ -74,18 +81,18 @@ export const TemplatesDataTable = ({
|
||||
<DataTable
|
||||
columns={[
|
||||
{
|
||||
header: 'Created',
|
||||
header: _(msg`Created`),
|
||||
accessorKey: 'createdAt',
|
||||
cell: ({ row }) => <LocaleDate date={row.original.createdAt} />,
|
||||
},
|
||||
{
|
||||
header: 'Title',
|
||||
header: _(msg`Title`),
|
||||
cell: ({ row }) => <DataTableTitle row={row.original} />,
|
||||
},
|
||||
{
|
||||
header: () => (
|
||||
<div className="flex flex-row items-center">
|
||||
Type
|
||||
<Trans>Type</Trans>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<InfoIcon className="mx-2 h-4 w-4" />
|
||||
@ -96,36 +103,45 @@ export const TemplatesDataTable = ({
|
||||
<li>
|
||||
<h2 className="mb-2 flex flex-row items-center font-semibold">
|
||||
<Globe2Icon className="mr-2 h-5 w-5 text-green-500 dark:text-green-300" />
|
||||
Public
|
||||
<Trans>Public</Trans>
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
Public templates are connected to your public profile. Any modifications
|
||||
to public templates will also appear in your public profile.
|
||||
<Trans>
|
||||
Public templates are connected to your public profile. Any modifications
|
||||
to public templates will also appear in your public profile.
|
||||
</Trans>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<div className="mb-2 flex w-fit flex-row items-center rounded border border-neutral-300 bg-neutral-200 px-1.5 py-0.5 text-xs dark:border-neutral-500 dark:bg-neutral-600">
|
||||
<Link2Icon className="mr-1 h-3 w-3" />
|
||||
direct link
|
||||
<Trans>direct link</Trans>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Direct link templates contain one dynamic recipient placeholder. Anyone
|
||||
with access to this link can sign the document, and it will then appear on
|
||||
your documents page.
|
||||
<Trans>
|
||||
Direct link templates contain one dynamic recipient placeholder. Anyone
|
||||
with access to this link can sign the document, and it will then appear
|
||||
on your documents page.
|
||||
</Trans>
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<h2 className="mb-2 flex flex-row items-center font-semibold">
|
||||
<LockIcon className="mr-2 h-5 w-5 text-blue-600 dark:text-blue-300" />
|
||||
{teamId ? 'Team Only' : 'Private'}
|
||||
{teamId ? <Trans>Team Only</Trans> : <Trans>Private</Trans>}
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
{teamId
|
||||
? 'Team only templates are not linked anywhere and are visible only to your team.'
|
||||
: 'Private templates can only be modified and viewed by you.'}
|
||||
{teamId ? (
|
||||
<Trans>
|
||||
Team only templates are not linked anywhere and are visible only to
|
||||
your team.
|
||||
</Trans>
|
||||
) : (
|
||||
<Trans>Private templates can only be modified and viewed by you.</Trans>
|
||||
)}
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
@ -149,7 +165,7 @@ export const TemplatesDataTable = ({
|
||||
),
|
||||
},
|
||||
{
|
||||
header: 'Actions',
|
||||
header: _(msg`Actions`),
|
||||
accessorKey: 'actions',
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import { Trans, msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
|
||||
import { trpc as trpcReact } from '@documenso/trpc/react';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
@ -27,6 +30,7 @@ export const DeleteTemplateDialog = ({
|
||||
}: DeleteTemplateDialogProps) => {
|
||||
const router = useRouter();
|
||||
|
||||
const { _ } = useLingui();
|
||||
const { toast } = useToast();
|
||||
|
||||
const { mutateAsync: deleteTemplate, isLoading } = trpcReact.template.deleteTemplate.useMutation({
|
||||
@ -34,8 +38,8 @@ export const DeleteTemplateDialog = ({
|
||||
router.refresh();
|
||||
|
||||
toast({
|
||||
title: 'Template deleted',
|
||||
description: 'Your template has been successfully deleted.',
|
||||
title: _(msg`Template deleted`),
|
||||
description: _(msg`Your template has been successfully deleted.`),
|
||||
duration: 5000,
|
||||
});
|
||||
|
||||
@ -43,8 +47,8 @@ export const DeleteTemplateDialog = ({
|
||||
},
|
||||
onError: () => {
|
||||
toast({
|
||||
title: 'Something went wrong',
|
||||
description: 'This template could not be deleted at this time. Please try again.',
|
||||
title: _(msg`Something went wrong`),
|
||||
description: _(msg`This template could not be deleted at this time. Please try again.`),
|
||||
variant: 'destructive',
|
||||
duration: 7500,
|
||||
});
|
||||
@ -55,11 +59,15 @@ export const DeleteTemplateDialog = ({
|
||||
<Dialog open={open} onOpenChange={(value) => !isLoading && onOpenChange(value)}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Do you want to delete this template?</DialogTitle>
|
||||
<DialogTitle>
|
||||
<Trans>Do you want to delete this template?</Trans>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogDescription>
|
||||
Please note that this action is irreversible. Once confirmed, your template will be
|
||||
permanently deleted.
|
||||
<Trans>
|
||||
Please note that this action is irreversible. Once confirmed, your template will be
|
||||
permanently deleted.
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@ -70,7 +78,7 @@ export const DeleteTemplateDialog = ({
|
||||
disabled={isLoading}
|
||||
onClick={() => onOpenChange(false)}
|
||||
>
|
||||
Cancel
|
||||
<Trans>Cancel</Trans>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
@ -79,7 +87,7 @@ export const DeleteTemplateDialog = ({
|
||||
loading={isLoading}
|
||||
onClick={async () => deleteTemplate({ id, teamId })}
|
||||
>
|
||||
Delete
|
||||
<Trans>Delete</Trans>
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import { Trans, msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
|
||||
import { trpc as trpcReact } from '@documenso/trpc/react';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
@ -27,6 +30,7 @@ export const DuplicateTemplateDialog = ({
|
||||
}: DuplicateTemplateDialogProps) => {
|
||||
const router = useRouter();
|
||||
|
||||
const { _ } = useLingui();
|
||||
const { toast } = useToast();
|
||||
|
||||
const { mutateAsync: duplicateTemplate, isLoading } =
|
||||
@ -35,8 +39,8 @@ export const DuplicateTemplateDialog = ({
|
||||
router.refresh();
|
||||
|
||||
toast({
|
||||
title: 'Template duplicated',
|
||||
description: 'Your template has been duplicated successfully.',
|
||||
title: _(msg`Template duplicated`),
|
||||
description: _(msg`Your template has been duplicated successfully.`),
|
||||
duration: 5000,
|
||||
});
|
||||
|
||||
@ -44,8 +48,8 @@ export const DuplicateTemplateDialog = ({
|
||||
},
|
||||
onError: () => {
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'An error occurred while duplicating template.',
|
||||
title: _(msg`Error`),
|
||||
description: _(msg`An error occurred while duplicating template.`),
|
||||
variant: 'destructive',
|
||||
});
|
||||
},
|
||||
@ -55,9 +59,13 @@ export const DuplicateTemplateDialog = ({
|
||||
<Dialog open={open} onOpenChange={(value) => !isLoading && onOpenChange(value)}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Do you want to duplicate this template?</DialogTitle>
|
||||
<DialogTitle>
|
||||
<Trans>Do you want to duplicate this template?</Trans>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogDescription className="pt-2">Your template will be duplicated.</DialogDescription>
|
||||
<DialogDescription className="pt-2">
|
||||
<Trans>Your template will be duplicated.</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<DialogFooter>
|
||||
@ -67,7 +75,7 @@ export const DuplicateTemplateDialog = ({
|
||||
variant="secondary"
|
||||
onClick={() => onOpenChange(false)}
|
||||
>
|
||||
Cancel
|
||||
<Trans>Cancel</Trans>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
@ -80,7 +88,7 @@ export const DuplicateTemplateDialog = ({
|
||||
})
|
||||
}
|
||||
>
|
||||
Duplicate
|
||||
<Trans>Duplicate</Trans>
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { Bird } from 'lucide-react';
|
||||
|
||||
export const EmptyTemplateState = () => {
|
||||
@ -6,10 +7,14 @@ export const EmptyTemplateState = () => {
|
||||
<Bird className="h-12 w-12" strokeWidth={1.5} />
|
||||
|
||||
<div className="text-center">
|
||||
<h3 className="text-lg font-semibold">We're all empty</h3>
|
||||
<h3 className="text-lg font-semibold">
|
||||
<Trans>We're all empty</Trans>
|
||||
</h3>
|
||||
|
||||
<p className="mt-2 max-w-[50ch]">
|
||||
You have not yet created any templates. To create a template please upload one.
|
||||
<Trans>
|
||||
You have not yet created any templates. To create a template please upload one.
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -2,6 +2,9 @@ import { useState } from 'react';
|
||||
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import { Trans, msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/avatar';
|
||||
@ -31,7 +34,10 @@ type MoveTemplateDialogProps = {
|
||||
|
||||
export const MoveTemplateDialog = ({ templateId, open, onOpenChange }: MoveTemplateDialogProps) => {
|
||||
const router = useRouter();
|
||||
|
||||
const { toast } = useToast();
|
||||
const { _ } = useLingui();
|
||||
|
||||
const [selectedTeamId, setSelectedTeamId] = useState<number | null>(null);
|
||||
|
||||
const { data: teams, isLoading: isLoadingTeams } = trpc.team.getTeams.useQuery();
|
||||
@ -39,16 +45,16 @@ export const MoveTemplateDialog = ({ templateId, open, onOpenChange }: MoveTempl
|
||||
onSuccess: () => {
|
||||
router.refresh();
|
||||
toast({
|
||||
title: 'Template moved',
|
||||
description: 'The template has been successfully moved to the selected team.',
|
||||
title: _(msg`Template moved`),
|
||||
description: _(msg`The template has been successfully moved to the selected team.`),
|
||||
duration: 5000,
|
||||
});
|
||||
onOpenChange(false);
|
||||
},
|
||||
onError: (error) => {
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: error.message || 'An error occurred while moving the template.',
|
||||
title: _(msg`Error`),
|
||||
description: error.message || _(msg`An error occurred while moving the template.`),
|
||||
variant: 'destructive',
|
||||
duration: 7500,
|
||||
});
|
||||
@ -67,20 +73,22 @@ export const MoveTemplateDialog = ({ templateId, open, onOpenChange }: MoveTempl
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Move Template to Team</DialogTitle>
|
||||
<DialogTitle>
|
||||
<Trans>Move Template to Team</Trans>
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
Select a team to move this template to. This action cannot be undone.
|
||||
<Trans>Select a team to move this template to. This action cannot be undone.</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<Select onValueChange={(value) => setSelectedTeamId(Number(value))}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select a team" />
|
||||
<SelectValue placeholder={_(msg`Select a team`)} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{isLoadingTeams ? (
|
||||
<SelectItem value="loading" disabled>
|
||||
Loading teams...
|
||||
<Trans>Loading teams...</Trans>
|
||||
</SelectItem>
|
||||
) : (
|
||||
teams?.map((team) => (
|
||||
@ -108,10 +116,10 @@ export const MoveTemplateDialog = ({ templateId, open, onOpenChange }: MoveTempl
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="secondary" onClick={() => onOpenChange(false)}>
|
||||
Cancel
|
||||
<Trans>Cancel</Trans>
|
||||
</Button>
|
||||
<Button onClick={onMove} loading={isLoading} disabled={!selectedTeamId || isLoading}>
|
||||
{isLoading ? 'Moving...' : 'Move'}
|
||||
{isLoading ? <Trans>Moving...</Trans> : <Trans>Move</Trans>}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
||||
@ -4,6 +4,8 @@ import React, { useState } from 'react';
|
||||
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import { Trans, msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { FilePlus, Loader } from 'lucide-react';
|
||||
import { useSession } from 'next-auth/react';
|
||||
|
||||
@ -34,6 +36,7 @@ export const NewTemplateDialog = ({ teamId, templateRootPath }: NewTemplateDialo
|
||||
|
||||
const { data: session } = useSession();
|
||||
const { toast } = useToast();
|
||||
const { _ } = useLingui();
|
||||
|
||||
const { mutateAsync: createTemplate } = trpc.template.createTemplate.useMutation();
|
||||
|
||||
@ -61,9 +64,10 @@ export const NewTemplateDialog = ({ teamId, templateRootPath }: NewTemplateDialo
|
||||
});
|
||||
|
||||
toast({
|
||||
title: 'Template document uploaded',
|
||||
description:
|
||||
'Your document has been uploaded successfully. You will be redirected to the template page.',
|
||||
title: _(msg`Template document uploaded`),
|
||||
description: _(
|
||||
msg`Your document has been uploaded successfully. You will be redirected to the template page.`,
|
||||
),
|
||||
duration: 5000,
|
||||
});
|
||||
|
||||
@ -72,8 +76,8 @@ export const NewTemplateDialog = ({ teamId, templateRootPath }: NewTemplateDialo
|
||||
router.push(`${templateRootPath}/${id}`);
|
||||
} catch {
|
||||
toast({
|
||||
title: 'Something went wrong',
|
||||
description: 'Please try again later.',
|
||||
title: _(msg`Something went wrong`),
|
||||
description: _(msg`Please try again later.`),
|
||||
variant: 'destructive',
|
||||
});
|
||||
|
||||
@ -89,15 +93,20 @@ export const NewTemplateDialog = ({ teamId, templateRootPath }: NewTemplateDialo
|
||||
<DialogTrigger asChild>
|
||||
<Button className="cursor-pointer" disabled={!session?.user.emailVerified}>
|
||||
<FilePlus className="-ml-1 mr-2 h-4 w-4" />
|
||||
New Template
|
||||
<Trans>New Template</Trans>
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent className="w-full max-w-xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>New Template</DialogTitle>
|
||||
<DialogTitle>
|
||||
<Trans>New Template</Trans>
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
Templates allow you to quickly generate documents with pre-filled recipients and fields.
|
||||
<Trans>
|
||||
Templates allow you to quickly generate documents with pre-filled recipients and
|
||||
fields.
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@ -114,7 +123,7 @@ export const NewTemplateDialog = ({ teamId, templateRootPath }: NewTemplateDialo
|
||||
<DialogFooter>
|
||||
<DialogClose asChild>
|
||||
<Button type="button" variant="secondary" disabled={isUploadingFile}>
|
||||
Close
|
||||
<Trans>Close</Trans>
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</DialogFooter>
|
||||
|
||||
@ -2,6 +2,8 @@ import React from 'react';
|
||||
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
|
||||
|
||||
import { TemplatesPageView } from './templates-page-view';
|
||||
import type { TemplatesPageViewProps } from './templates-page-view';
|
||||
|
||||
@ -14,5 +16,7 @@ export const metadata: Metadata = {
|
||||
};
|
||||
|
||||
export default function TemplatesPage({ searchParams = {} }: TemplatesPageProps) {
|
||||
setupI18nSSR();
|
||||
|
||||
return <TemplatesPageView searchParams={searchParams} />;
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { Trans, msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Link2Icon } from 'lucide-react';
|
||||
|
||||
import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard';
|
||||
@ -19,13 +21,15 @@ export const TemplateDirectLinkBadge = ({
|
||||
className,
|
||||
}: TemplateDirectLinkBadgeProps) => {
|
||||
const [, copy] = useCopyToClipboard();
|
||||
|
||||
const { _ } = useLingui();
|
||||
const { toast } = useToast();
|
||||
|
||||
const onCopyClick = async (token: string) =>
|
||||
copy(formatDirectTemplatePath(token)).then(() => {
|
||||
toast({
|
||||
title: 'Copied to clipboard',
|
||||
description: 'The direct link has been copied to your clipboard',
|
||||
title: _(msg`Copied to clipboard`),
|
||||
description: _(msg`The direct link has been copied to your clipboard`),
|
||||
});
|
||||
});
|
||||
|
||||
@ -39,7 +43,7 @@ export const TemplateDirectLinkBadge = ({
|
||||
onClick={async () => onCopyClick(token)}
|
||||
>
|
||||
<Link2Icon className="mr-1 h-3 w-3" />
|
||||
direct link {!enabled && 'disabled'}
|
||||
{enabled ? <Trans>direct link</Trans> : <Trans>direct link disabled</Trans>}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
@ -3,16 +3,16 @@ import { useEffect, useMemo, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import { Trans, msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { CircleDotIcon, CircleIcon, ClipboardCopyIcon, InfoIcon, LoaderIcon } from 'lucide-react';
|
||||
import { P, match } from 'ts-pattern';
|
||||
|
||||
import { useLimits } from '@documenso/ee/server-only/limits/provider/client';
|
||||
import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard';
|
||||
import { DIRECT_TEMPLATE_RECIPIENT_EMAIL } from '@documenso/lib/constants/direct-templates';
|
||||
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
|
||||
import {
|
||||
DIRECT_TEMPLATE_DOCUMENTATION,
|
||||
DIRECT_TEMPLATE_RECIPIENT_EMAIL,
|
||||
} from '@documenso/lib/constants/template';
|
||||
import { DIRECT_TEMPLATE_DOCUMENTATION } from '@documenso/lib/constants/template';
|
||||
import { formatDirectTemplatePath } from '@documenso/lib/utils/templates';
|
||||
import {
|
||||
type Recipient,
|
||||
@ -66,6 +66,7 @@ export const TemplateDirectLinkDialog = ({
|
||||
}: TemplateDirectLinkDialogProps) => {
|
||||
const { toast } = useToast();
|
||||
const { quota, remaining } = useLimits();
|
||||
const { _ } = useLingui();
|
||||
|
||||
const team = useOptionalCurrentTeam();
|
||||
|
||||
@ -100,8 +101,8 @@ export const TemplateDirectLinkDialog = ({
|
||||
setSelectedRecipientId(null);
|
||||
|
||||
toast({
|
||||
title: 'Something went wrong',
|
||||
description: 'Unable to create direct template access. Please try again later.',
|
||||
title: _(msg`Something went wrong`),
|
||||
description: _(msg`Unable to create direct template access. Please try again later.`),
|
||||
variant: 'destructive',
|
||||
});
|
||||
},
|
||||
@ -110,17 +111,21 @@ export const TemplateDirectLinkDialog = ({
|
||||
const { mutateAsync: toggleTemplateDirectLink, isLoading: isTogglingTemplateAccess } =
|
||||
trpcReact.template.toggleTemplateDirectLink.useMutation({
|
||||
onSuccess: (data) => {
|
||||
const enabledDescription = msg`Direct link signing has been enabled`;
|
||||
const disabledDescription = msg`Direct link signing has been disabled`;
|
||||
|
||||
toast({
|
||||
title: 'Success',
|
||||
description: `Direct link signing has been ${data.enabled ? 'enabled' : 'disabled'}`,
|
||||
title: _(msg`Success`),
|
||||
description: _(data.enabled ? enabledDescription : disabledDescription),
|
||||
});
|
||||
},
|
||||
onError: (_ctx, data) => {
|
||||
const enabledDescription = msg`An error occurred while enabling direct link signing.`;
|
||||
const disabledDescription = msg`An error occurred while disabling direct link signing.`;
|
||||
|
||||
toast({
|
||||
title: 'Something went wrong',
|
||||
description: `An error occurred while ${
|
||||
data.enabled ? 'enabling' : 'disabling'
|
||||
} direct link signing.`,
|
||||
title: _(msg`Something went wrong`),
|
||||
description: _(data.enabled ? enabledDescription : disabledDescription),
|
||||
variant: 'destructive',
|
||||
});
|
||||
},
|
||||
@ -133,8 +138,8 @@ export const TemplateDirectLinkDialog = ({
|
||||
setToken(null);
|
||||
|
||||
toast({
|
||||
title: 'Success',
|
||||
description: 'Direct template link deleted',
|
||||
title: _(msg`Success`),
|
||||
description: _(msg`Direct template link deleted`),
|
||||
duration: 5000,
|
||||
});
|
||||
|
||||
@ -143,9 +148,10 @@ export const TemplateDirectLinkDialog = ({
|
||||
},
|
||||
onError: () => {
|
||||
toast({
|
||||
title: 'Something went wrong',
|
||||
description:
|
||||
'We encountered an error while removing the direct template link. Please try again later.',
|
||||
title: _(msg`Something went wrong`),
|
||||
description: _(
|
||||
msg`We encountered an error while removing the direct template link. Please try again later.`,
|
||||
),
|
||||
variant: 'destructive',
|
||||
});
|
||||
},
|
||||
@ -154,8 +160,8 @@ export const TemplateDirectLinkDialog = ({
|
||||
const onCopyClick = async (token: string) =>
|
||||
copy(formatDirectTemplatePath(token)).then(() => {
|
||||
toast({
|
||||
title: 'Copied to clipboard',
|
||||
description: 'The direct link has been copied to your clipboard',
|
||||
title: _(msg`Copied to clipboard`),
|
||||
description: _(msg`The direct link has been copied to your clipboard`),
|
||||
});
|
||||
});
|
||||
|
||||
@ -192,9 +198,13 @@ export const TemplateDirectLinkDialog = ({
|
||||
.with({ token: P.nullish, currentStep: 'ONBOARD' }, () => (
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Create Direct Signing Link</DialogTitle>
|
||||
<DialogTitle>
|
||||
<Trans>Create Direct Signing Link</Trans>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogDescription>Here's how it works:</DialogDescription>
|
||||
<DialogDescription>
|
||||
<Trans>Here's how it works:</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<ul className="mt-4 space-y-4 pl-12">
|
||||
@ -206,8 +216,8 @@ export const TemplateDirectLinkDialog = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 className="font-semibold">{step.title}</h3>
|
||||
<p className="text-muted-foreground mt-1 text-sm">{step.description}</p>
|
||||
<h3 className="font-semibold">{_(step.title)}</h3>
|
||||
<p className="text-muted-foreground mt-1 text-sm">{_(step.description)}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
@ -215,18 +225,22 @@ export const TemplateDirectLinkDialog = ({
|
||||
{remaining.directTemplates === 0 && (
|
||||
<Alert variant="warning">
|
||||
<AlertTitle>
|
||||
Direct template link usage exceeded ({quota.directTemplates}/
|
||||
{quota.directTemplates})
|
||||
<Trans>
|
||||
Direct template link usage exceeded ({quota.directTemplates}/
|
||||
{quota.directTemplates})
|
||||
</Trans>
|
||||
</AlertTitle>
|
||||
<AlertDescription>
|
||||
You have reached the maximum limit of {quota.directTemplates} direct
|
||||
templates.{' '}
|
||||
<Link
|
||||
className="mt-1 block underline underline-offset-4"
|
||||
href="/settings/billing"
|
||||
>
|
||||
Upgrade your account to continue!
|
||||
</Link>
|
||||
<Trans>
|
||||
You have reached the maximum limit of {quota.directTemplates} direct
|
||||
templates.{' '}
|
||||
<Link
|
||||
className="mt-1 block underline underline-offset-4"
|
||||
href="/settings/billing"
|
||||
>
|
||||
Upgrade your account to continue!
|
||||
</Link>
|
||||
</Trans>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
@ -234,7 +248,7 @@ export const TemplateDirectLinkDialog = ({
|
||||
{remaining.directTemplates !== 0 && (
|
||||
<DialogFooter className="mx-auto mt-4">
|
||||
<Button type="button" onClick={() => setCurrentStep('SELECT_RECIPIENT')}>
|
||||
Enable direct link signing
|
||||
<Trans> Enable direct link signing</Trans>
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
)}
|
||||
@ -249,10 +263,12 @@ export const TemplateDirectLinkDialog = ({
|
||||
)}
|
||||
|
||||
<DialogHeader>
|
||||
<DialogTitle>Choose Direct Link Recipient</DialogTitle>
|
||||
<DialogTitle>
|
||||
<Trans>Choose Direct Link Recipient</Trans>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogDescription>
|
||||
Choose an existing recipient from below to continue
|
||||
<Trans>Choose an existing recipient from below to continue</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@ -260,8 +276,12 @@ export const TemplateDirectLinkDialog = ({
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Recipient</TableHead>
|
||||
<TableHead>Role</TableHead>
|
||||
<TableHead>
|
||||
<Trans>Recipient</Trans>
|
||||
</TableHead>
|
||||
<TableHead>
|
||||
<Trans>Role</Trans>
|
||||
</TableHead>
|
||||
<TableHead></TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
@ -269,7 +289,9 @@ export const TemplateDirectLinkDialog = ({
|
||||
{validDirectTemplateRecipients.length === 0 && (
|
||||
<TableRow>
|
||||
<TableCell colSpan={3} className="h-16 text-center">
|
||||
<p className="text-muted-foreground">No valid recipients found</p>
|
||||
<p className="text-muted-foreground">
|
||||
<Trans>No valid recipients found</Trans>
|
||||
</p>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
@ -288,7 +310,7 @@ export const TemplateDirectLinkDialog = ({
|
||||
</TableCell>
|
||||
|
||||
<TableCell className="text-muted-foreground text-sm">
|
||||
{RECIPIENT_ROLES_DESCRIPTION[row.role].roleName}
|
||||
{_(RECIPIENT_ROLES_DESCRIPTION[row.role].roleName)}
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
@ -311,7 +333,9 @@ export const TemplateDirectLinkDialog = ({
|
||||
<DialogFooter className="mx-auto">
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
{validDirectTemplateRecipients.length !== 0 && (
|
||||
<p className="text-muted-foreground text-sm">Or</p>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
<Trans>Or</Trans>
|
||||
</p>
|
||||
)}
|
||||
|
||||
<Button
|
||||
@ -325,7 +349,7 @@ export const TemplateDirectLinkDialog = ({
|
||||
})
|
||||
}
|
||||
>
|
||||
Create one automatically
|
||||
<Trans>Create one automatically</Trans>
|
||||
</Button>
|
||||
</div>
|
||||
</DialogFooter>
|
||||
@ -335,23 +359,28 @@ export const TemplateDirectLinkDialog = ({
|
||||
.with({ token: P.string, currentStep: 'MANAGE' }, ({ token }) => (
|
||||
<DialogContent className="relative">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Direct Link Signing</DialogTitle>
|
||||
<DialogTitle>
|
||||
<Trans>Direct Link Signing</Trans>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogDescription>
|
||||
Manage the direct link signing for this template
|
||||
<Trans>Manage the direct link signing for this template</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div>
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
<Label className="flex flex-row">
|
||||
Enable Direct Link Signing
|
||||
<Trans>Enable Direct Link Signing</Trans>
|
||||
<Tooltip>
|
||||
<TooltipTrigger tabIndex={-1} className="ml-2">
|
||||
<InfoIcon className="h-4 w-4" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="text-foreground z-9999 max-w-md p-4">
|
||||
Disabling direct link signing will prevent anyone from accessing the link.
|
||||
<Trans>
|
||||
Disabling direct link signing will prevent anyone from accessing the
|
||||
link.
|
||||
</Trans>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</Label>
|
||||
@ -364,7 +393,9 @@ export const TemplateDirectLinkDialog = ({
|
||||
</div>
|
||||
|
||||
<div className="mt-2">
|
||||
<Label htmlFor="copy-direct-link">Copy Shareable Link</Label>
|
||||
<Label htmlFor="copy-direct-link">
|
||||
<Trans>Copy Shareable Link</Trans>
|
||||
</Label>
|
||||
|
||||
<div className="relative mt-1">
|
||||
<Input
|
||||
@ -397,7 +428,7 @@ export const TemplateDirectLinkDialog = ({
|
||||
loading={isDeletingTemplateDirectLink}
|
||||
onClick={() => setCurrentStep('CONFIRM_DELETE')}
|
||||
>
|
||||
Remove
|
||||
<Trans>Remove</Trans>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
@ -410,7 +441,7 @@ export const TemplateDirectLinkDialog = ({
|
||||
})
|
||||
}
|
||||
>
|
||||
Save
|
||||
<Trans>Save</Trans>
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
@ -418,11 +449,15 @@ export const TemplateDirectLinkDialog = ({
|
||||
.with({ token: P.string, currentStep: 'CONFIRM_DELETE' }, () => (
|
||||
<DialogContent className="relative">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Are you sure?</DialogTitle>
|
||||
<DialogTitle>
|
||||
<Trans>Are you sure?</Trans>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogDescription>
|
||||
Please note that proceeding will remove direct linking recipient and turn it
|
||||
into a placeholder.
|
||||
<Trans>
|
||||
Please note that proceeding will remove direct linking recipient and turn it
|
||||
into a placeholder.
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@ -432,7 +467,7 @@ export const TemplateDirectLinkDialog = ({
|
||||
variant="secondary"
|
||||
onClick={() => setCurrentStep('MANAGE')}
|
||||
>
|
||||
Cancel
|
||||
<Trans>Cancel</Trans>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
@ -441,7 +476,7 @@ export const TemplateDirectLinkDialog = ({
|
||||
loading={isDeletingTemplateDirectLink}
|
||||
onClick={() => void deleteTemplateDirectLink({ templateId: template.id })}
|
||||
>
|
||||
Confirm
|
||||
<Trans>Confirm</Trans>
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
||||
import { findTemplates } from '@documenso/lib/server-only/template/find-templates';
|
||||
@ -49,7 +51,9 @@ export const TemplatesPageView = async ({ searchParams = {}, team }: TemplatesPa
|
||||
</Avatar>
|
||||
)}
|
||||
|
||||
<h1 className="truncate text-2xl font-semibold md:text-3xl">Templates</h1>
|
||||
<h1 className="truncate text-2xl font-semibold md:text-3xl">
|
||||
<Trans>Templates</Trans>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@ -3,6 +3,8 @@ import { useEffect, useState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Trans, msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { InfoIcon, Plus } from 'lucide-react';
|
||||
import { useFieldArray, useForm } from 'react-hook-form';
|
||||
import * as z from 'zod';
|
||||
@ -94,7 +96,9 @@ export function UseTemplateDialog({
|
||||
templateId,
|
||||
}: UseTemplateDialogProps) {
|
||||
const router = useRouter();
|
||||
|
||||
const { toast } = useToast();
|
||||
const { _ } = useLingui();
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
@ -135,8 +139,8 @@ export function UseTemplateDialog({
|
||||
});
|
||||
|
||||
toast({
|
||||
title: 'Document created',
|
||||
description: 'Your document has been created from the template successfully.',
|
||||
title: _(msg`Document created`),
|
||||
description: _(msg`Your document has been created from the template successfully.`),
|
||||
duration: 5000,
|
||||
});
|
||||
|
||||
@ -145,13 +149,15 @@ export function UseTemplateDialog({
|
||||
const error = AppError.parseError(err);
|
||||
|
||||
const toastPayload: Toast = {
|
||||
title: 'Error',
|
||||
description: 'An error occurred while creating document from template.',
|
||||
title: _(msg`Error`),
|
||||
description: _(msg`An error occurred while creating document from template.`),
|
||||
variant: 'destructive',
|
||||
};
|
||||
|
||||
if (error.code === 'DOCUMENT_SEND_FAILED') {
|
||||
toastPayload.description = 'The document was created but could not be sent to recipients.';
|
||||
toastPayload.description = _(
|
||||
msg`The document was created but could not be sent to recipients.`,
|
||||
);
|
||||
}
|
||||
|
||||
toast(toastPayload);
|
||||
@ -174,16 +180,20 @@ export function UseTemplateDialog({
|
||||
<DialogTrigger asChild>
|
||||
<Button variant="outline" className="bg-background">
|
||||
<Plus className="-ml-1 mr-2 h-4 w-4" />
|
||||
Use Template
|
||||
<Trans>Use Template</Trans>
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Create document from template</DialogTitle>
|
||||
<DialogTitle>
|
||||
<Trans>Create document from template</Trans>
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
{recipients.length === 0
|
||||
? 'A draft document will be created'
|
||||
: 'Add the recipients to create the document with'}
|
||||
{recipients.length === 0 ? (
|
||||
<Trans>A draft document will be created</Trans>
|
||||
) : (
|
||||
<Trans>Add the recipients to create the document with</Trans>
|
||||
)}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@ -198,10 +208,17 @@ export function UseTemplateDialog({
|
||||
name={`recipients.${index}.email`}
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
{index === 0 && <FormLabel required>Email</FormLabel>}
|
||||
{index === 0 && (
|
||||
<FormLabel required>
|
||||
<Trans>Email</Trans>
|
||||
</FormLabel>
|
||||
)}
|
||||
|
||||
<FormControl>
|
||||
<Input {...field} placeholder={recipients[index].email || 'Email'} />
|
||||
<Input
|
||||
{...field}
|
||||
placeholder={recipients[index].email || _(msg`Email`)}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -213,10 +230,17 @@ export function UseTemplateDialog({
|
||||
name={`recipients.${index}.name`}
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
{index === 0 && <FormLabel>Name</FormLabel>}
|
||||
{index === 0 && (
|
||||
<FormLabel>
|
||||
<Trans>Name</Trans>
|
||||
</FormLabel>
|
||||
)}
|
||||
|
||||
<FormControl>
|
||||
<Input {...field} placeholder={recipients[index].name || 'Name'} />
|
||||
<Input
|
||||
{...field}
|
||||
placeholder={recipients[index].name || _(msg`Name`)}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -246,7 +270,7 @@ export function UseTemplateDialog({
|
||||
className="text-muted-foreground ml-2 flex items-center text-sm"
|
||||
htmlFor="sendDocument"
|
||||
>
|
||||
Send document
|
||||
<Trans>Send document</Trans>
|
||||
<Tooltip>
|
||||
<TooltipTrigger type="button">
|
||||
<InfoIcon className="mx-1 h-4 w-4" />
|
||||
@ -254,11 +278,16 @@ export function UseTemplateDialog({
|
||||
|
||||
<TooltipContent className="text-muted-foreground z-[99999] max-w-md space-y-2 p-4">
|
||||
<p>
|
||||
The document will be immediately sent to recipients if this is
|
||||
checked.
|
||||
<Trans>
|
||||
{' '}
|
||||
The document will be immediately sent to recipients if this is
|
||||
checked.
|
||||
</Trans>
|
||||
</p>
|
||||
|
||||
<p>Otherwise, the document will be created as a draft.</p>
|
||||
<p>
|
||||
<Trans>Otherwise, the document will be created as a draft.</Trans>
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</label>
|
||||
@ -272,12 +301,16 @@ export function UseTemplateDialog({
|
||||
<DialogFooter>
|
||||
<DialogClose asChild>
|
||||
<Button type="button" variant="secondary">
|
||||
Close
|
||||
<Trans>Close</Trans>
|
||||
</Button>
|
||||
</DialogClose>
|
||||
|
||||
<Button type="submit" loading={form.formState.isSubmitting}>
|
||||
{form.getValues('sendDocument') ? 'Create and send' : 'Create as draft'}
|
||||
{form.getValues('sendDocument') ? (
|
||||
<Trans>Create and send</Trans>
|
||||
) : (
|
||||
<Trans>Create as draft</Trans>
|
||||
)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</fieldset>
|
||||
|
||||
Reference in New Issue
Block a user