feat: web i18n (#1286)

This commit is contained in:
David Nguyen
2024-08-27 20:34:39 +09:00
committed by GitHub
parent 0829311214
commit 75c8772a02
294 changed files with 14846 additions and 2229 deletions

View File

@@ -2,6 +2,9 @@
import Link from 'next/link';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import type { Recipient } from '@documenso/prisma/client';
import { type Document, SigningStatus } from '@documenso/prisma/client';
import { trpc } from '@documenso/trpc/react';
@@ -22,20 +25,21 @@ export type AdminActionsProps = {
};
export const AdminActions = ({ className, document, recipients }: AdminActionsProps) => {
const { _ } = useLingui();
const { toast } = useToast();
const { mutate: resealDocument, isLoading: isResealDocumentLoading } =
trpc.admin.resealDocument.useMutation({
onSuccess: () => {
toast({
title: 'Success',
description: 'Document resealed',
title: _(msg`Success`),
description: _(msg`Document resealed`),
});
},
onError: () => {
toast({
title: 'Error',
description: 'Failed to reseal document',
title: _(msg`Error`),
description: _(msg`Failed to reseal document`),
variant: 'destructive',
});
},
@@ -54,19 +58,23 @@ export const AdminActions = ({ className, document, recipients }: AdminActionsPr
)}
onClick={() => resealDocument({ id: document.id })}
>
Reseal document
<Trans>Reseal document</Trans>
</Button>
</TooltipTrigger>
<TooltipContent className="max-w-[40ch]">
Attempts sealing the document again, useful for after a code change has occurred to
resolve an erroneous document.
<Trans>
Attempts sealing the document again, useful for after a code change has occurred to
resolve an erroneous document.
</Trans>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Button variant="outline" asChild>
<Link href={`/admin/users/${document.userId}`}>Go to owner</Link>
<Link href={`/admin/users/${document.userId}`}>
<Trans>Go to owner</Trans>
</Link>
</Button>
</div>
);

View File

@@ -1,5 +1,7 @@
import { Trans } from '@lingui/macro';
import { DateTime } from 'luxon';
import { setupI18nSSR } from '@documenso/lib/client-only/providers/i18n.server';
import { getEntireDocument } from '@documenso/lib/server-only/admin/get-entire-document';
import {
Accordion,
@@ -23,6 +25,8 @@ type AdminDocumentDetailsPageProps = {
};
export default async function AdminDocumentDetailsPage({ params }: AdminDocumentDetailsPageProps) {
setupI18nSSR();
const document = await getEntireDocument({ id: Number(params.id) });
return (
@@ -35,28 +39,34 @@ export default async function AdminDocumentDetailsPage({ params }: AdminDocument
{document.deletedAt && (
<Badge size="large" variant="destructive">
Deleted
<Trans>Deleted</Trans>
</Badge>
)}
</div>
<div className="text-muted-foreground mt-4 text-sm">
<div>
Created on: <LocaleDate date={document.createdAt} format={DateTime.DATETIME_MED} />
<Trans>Created on</Trans>:{' '}
<LocaleDate date={document.createdAt} format={DateTime.DATETIME_MED} />
</div>
<div>
Last updated at: <LocaleDate date={document.updatedAt} format={DateTime.DATETIME_MED} />
<Trans>Last updated at</Trans>:{' '}
<LocaleDate date={document.updatedAt} format={DateTime.DATETIME_MED} />
</div>
</div>
<hr className="my-4" />
<h2 className="text-lg font-semibold">Admin Actions</h2>
<h2 className="text-lg font-semibold">
<Trans>Admin Actions</Trans>
</h2>
<AdminActions className="mt-2" document={document} recipients={document.Recipient} />
<hr className="my-4" />
<h2 className="text-lg font-semibold">Recipients</h2>
<h2 className="text-lg font-semibold">
<Trans>Recipients</Trans>
</h2>
<div className="mt-4">
<Accordion type="multiple" className="space-y-4">

View File

@@ -2,6 +2,8 @@
import { useRouter } from 'next/navigation';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
@@ -43,7 +45,9 @@ export type RecipientItemProps = {
};
export const RecipientItem = ({ recipient }: RecipientItemProps) => {
const { _ } = useLingui();
const { toast } = useToast();
const router = useRouter();
const form = useForm<TAdminUpdateRecipientFormSchema>({
@@ -64,14 +68,14 @@ export const RecipientItem = ({ recipient }: RecipientItemProps) => {
});
toast({
title: 'Recipient updated',
description: 'The recipient has been updated successfully',
title: _(msg`Recipient updated`),
description: _(msg`The recipient has been updated successfully`),
});
router.refresh();
} catch (error) {
toast({
title: 'Failed to update recipient',
title: _(msg`Failed to update recipient`),
description: error.message,
variant: 'destructive',
});
@@ -93,7 +97,9 @@ export const RecipientItem = ({ recipient }: RecipientItemProps) => {
name="name"
render={({ field }) => (
<FormItem className="flex-1">
<FormLabel required>Name</FormLabel>
<FormLabel required>
<Trans>Name</Trans>
</FormLabel>
<FormControl>
<Input {...field} />
@@ -109,7 +115,9 @@ export const RecipientItem = ({ recipient }: RecipientItemProps) => {
name="email"
render={({ field }) => (
<FormItem className="flex-1">
<FormLabel required>Email</FormLabel>
<FormLabel required>
<Trans>Email</Trans>
</FormLabel>
<FormControl>
<Input type="email" {...field} />
@@ -122,7 +130,7 @@ export const RecipientItem = ({ recipient }: RecipientItemProps) => {
<div>
<Button type="submit" loading={form.formState.isSubmitting}>
Update Recipient
<Trans>Update Recipient</Trans>
</Button>
</div>
</fieldset>
@@ -131,7 +139,9 @@ export const RecipientItem = ({ recipient }: RecipientItemProps) => {
<hr className="my-4" />
<h2 className="mb-4 text-lg font-semibold">Fields</h2>
<h2 className="mb-4 text-lg font-semibold">
<Trans>Fields</Trans>
</h2>
<DataTable
data={recipient.Field}
@@ -142,22 +152,22 @@ export const RecipientItem = ({ recipient }: RecipientItemProps) => {
cell: ({ row }) => <div>{row.original.id}</div>,
},
{
header: 'Type',
header: _(msg`Type`),
accessorKey: 'type',
cell: ({ row }) => <div>{row.original.type}</div>,
},
{
header: 'Inserted',
header: _(msg`Inserted`),
accessorKey: 'inserted',
cell: ({ row }) => <div>{row.original.inserted ? 'True' : 'False'}</div>,
},
{
header: 'Value',
header: _(msg`Value`),
accessorKey: 'customText',
cell: ({ row }) => <div>{row.original.customText}</div>,
},
{
header: 'Signature',
header: _(msg`Signature`),
accessorKey: 'signature',
cell: ({ row }) => (
<div>

View File

@@ -4,6 +4,9 @@ import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import type { Document } from '@documenso/prisma/client';
import { TRPCClientError } from '@documenso/trpc/client';
import { trpc } from '@documenso/trpc/react';
@@ -26,7 +29,9 @@ export type SuperDeleteDocumentDialogProps = {
};
export const SuperDeleteDocumentDialog = ({ document }: SuperDeleteDocumentDialogProps) => {
const { _ } = useLingui();
const { toast } = useToast();
const router = useRouter();
const [reason, setReason] = useState('');
@@ -43,7 +48,7 @@ export const SuperDeleteDocumentDialog = ({ document }: SuperDeleteDocumentDialo
await deleteDocument({ id: document.id, reason });
toast({
title: 'Document deleted',
title: _(msg`Document deleted`),
description: 'The Document has been deleted successfully.',
duration: 5000,
});
@@ -52,13 +57,13 @@ export const SuperDeleteDocumentDialog = ({ document }: SuperDeleteDocumentDialo
} catch (err) {
if (err instanceof TRPCClientError && err.data?.code === 'BAD_REQUEST') {
toast({
title: 'An error occurred',
title: _(msg`An error occurred`),
description: err.message,
variant: 'destructive',
});
} else {
toast({
title: 'An unknown error occurred',
title: _(msg`An unknown error occurred`),
variant: 'destructive',
description:
err.message ??
@@ -76,31 +81,41 @@ export const SuperDeleteDocumentDialog = ({ document }: SuperDeleteDocumentDialo
variant="neutral"
>
<div>
<AlertTitle>Delete Document</AlertTitle>
<AlertTitle>
<Trans>Delete Document</Trans>
</AlertTitle>
<AlertDescription className="mr-2">
Delete the document. This action is irreversible so proceed with caution.
<Trans>
Delete the document. This action is irreversible so proceed with caution.
</Trans>
</AlertDescription>
</div>
<div className="flex-shrink-0">
<Dialog>
<DialogTrigger asChild>
<Button variant="destructive">Delete Document</Button>
<Button variant="destructive">
<Trans>Delete Document</Trans>
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader className="space-y-4">
<DialogTitle>Delete Document</DialogTitle>
<DialogTitle>
<Trans>Delete Document</Trans>
</DialogTitle>
<Alert variant="destructive">
<AlertDescription className="selection:bg-red-100">
This action is not reversible. Please be certain.
<Trans>This action is not reversible. Please be certain.</Trans>
</AlertDescription>
</Alert>
</DialogHeader>
<div>
<DialogDescription>To confirm, please enter the reason</DialogDescription>
<DialogDescription>
<Trans>To confirm, please enter the reason</Trans>
</DialogDescription>
<Input
className="mt-2"
@@ -117,7 +132,7 @@ export const SuperDeleteDocumentDialog = ({ document }: SuperDeleteDocumentDialo
variant="destructive"
disabled={!reason}
>
{isDeletingDocument ? 'Deleting document...' : 'Delete Document'}
<Trans>Delete document</Trans>
</Button>
</DialogFooter>
</DialogContent>