mirror of
https://github.com/documenso/documenso.git
synced 2025-11-23 13:11:32 +10:00
feat: web i18n (#1286)
This commit is contained in:
@ -2,6 +2,9 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
|
||||
import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard';
|
||||
import { getRecipientType } from '@documenso/lib/client-only/recipient-type';
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
@ -21,6 +24,8 @@ export type AvatarWithRecipientProps = {
|
||||
|
||||
export function AvatarWithRecipient({ recipient, documentStatus }: AvatarWithRecipientProps) {
|
||||
const [, copy] = useCopyToClipboard();
|
||||
|
||||
const { _ } = useLingui();
|
||||
const { toast } = useToast();
|
||||
|
||||
const signingToken = documentStatus === DocumentStatus.PENDING ? recipient.token : null;
|
||||
@ -32,8 +37,8 @@ export function AvatarWithRecipient({ recipient, documentStatus }: AvatarWithRec
|
||||
|
||||
void copy(`${NEXT_PUBLIC_WEBAPP_URL()}/sign/${signingToken}`).then(() => {
|
||||
toast({
|
||||
title: 'Copied to clipboard',
|
||||
description: 'The signing link has been copied to your clipboard.',
|
||||
title: _(msg`Copied to clipboard`),
|
||||
description: _(msg`The signing link has been copied to your clipboard.`),
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -44,7 +49,7 @@ export function AvatarWithRecipient({ recipient, documentStatus }: AvatarWithRec
|
||||
'cursor-pointer hover:underline': signingToken,
|
||||
})}
|
||||
role={signingToken ? 'button' : undefined}
|
||||
title={signingToken ? 'Click to copy signing link for sending to recipient' : undefined}
|
||||
title={signingToken ? _(msg`Click to copy signing link for sending to recipient`) : undefined}
|
||||
onClick={onRecipientClick}
|
||||
>
|
||||
<StackAvatar
|
||||
@ -56,11 +61,13 @@ export function AvatarWithRecipient({ recipient, documentStatus }: AvatarWithRec
|
||||
|
||||
<div
|
||||
className="text-muted-foreground text-sm"
|
||||
title={signingToken ? 'Click to copy signing link for sending to recipient' : undefined}
|
||||
title={
|
||||
signingToken ? _(msg`Click to copy signing link for sending to recipient`) : undefined
|
||||
}
|
||||
>
|
||||
<p>{recipient.email}</p>
|
||||
<p className="text-muted-foreground/70 text-xs">
|
||||
{RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName}
|
||||
{_(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
|
||||
import { getRecipientType } from '@documenso/lib/client-only/recipient-type';
|
||||
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
|
||||
import { recipientAbbreviation } from '@documenso/lib/utils/recipient-formatter';
|
||||
@ -23,6 +26,8 @@ export const StackAvatarsWithTooltip = ({
|
||||
position,
|
||||
children,
|
||||
}: StackAvatarsWithTooltipProps) => {
|
||||
const { _ } = useLingui();
|
||||
|
||||
const waitingRecipients = recipients.filter(
|
||||
(recipient) => getRecipientType(recipient) === 'waiting',
|
||||
);
|
||||
@ -49,7 +54,9 @@ export const StackAvatarsWithTooltip = ({
|
||||
>
|
||||
{completedRecipients.length > 0 && (
|
||||
<div>
|
||||
<h1 className="text-base font-medium">Completed</h1>
|
||||
<h1 className="text-base font-medium">
|
||||
<Trans>Completed</Trans>
|
||||
</h1>
|
||||
{completedRecipients.map((recipient: Recipient) => (
|
||||
<div key={recipient.id} className="my-1 flex items-center gap-2">
|
||||
<StackAvatar
|
||||
@ -61,7 +68,7 @@ export const StackAvatarsWithTooltip = ({
|
||||
<div className="">
|
||||
<p className="text-muted-foreground text-sm">{recipient.email}</p>
|
||||
<p className="text-muted-foreground/70 text-xs">
|
||||
{RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName}
|
||||
{_(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -71,7 +78,9 @@ export const StackAvatarsWithTooltip = ({
|
||||
|
||||
{waitingRecipients.length > 0 && (
|
||||
<div>
|
||||
<h1 className="text-base font-medium">Waiting</h1>
|
||||
<h1 className="text-base font-medium">
|
||||
<Trans>Waiting</Trans>
|
||||
</h1>
|
||||
{waitingRecipients.map((recipient: Recipient) => (
|
||||
<AvatarWithRecipient
|
||||
key={recipient.id}
|
||||
@ -84,7 +93,9 @@ export const StackAvatarsWithTooltip = ({
|
||||
|
||||
{openedRecipients.length > 0 && (
|
||||
<div>
|
||||
<h1 className="text-base font-medium">Opened</h1>
|
||||
<h1 className="text-base font-medium">
|
||||
<Trans>Opened</Trans>
|
||||
</h1>
|
||||
{openedRecipients.map((recipient: Recipient) => (
|
||||
<AvatarWithRecipient
|
||||
key={recipient.id}
|
||||
@ -97,7 +108,9 @@ export const StackAvatarsWithTooltip = ({
|
||||
|
||||
{uncompletedRecipients.length > 0 && (
|
||||
<div>
|
||||
<h1 className="text-base font-medium">Uncompleted</h1>
|
||||
<h1 className="text-base font-medium">
|
||||
<Trans>Uncompleted</Trans>
|
||||
</h1>
|
||||
{uncompletedRecipients.map((recipient: Recipient) => (
|
||||
<AvatarWithRecipient
|
||||
key={recipient.id}
|
||||
|
||||
@ -4,6 +4,9 @@ import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import type { MessageDescriptor } from '@lingui/core';
|
||||
import { Trans, msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Loader, Monitor, Moon, Sun } from 'lucide-react';
|
||||
import { useTheme } from 'next-themes';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
@ -31,22 +34,22 @@ import { THEMES_TYPE } from '@documenso/ui/primitives/constants';
|
||||
|
||||
const DOCUMENTS_PAGES = [
|
||||
{
|
||||
label: 'All documents',
|
||||
label: msg`All documents`,
|
||||
path: '/documents?status=ALL',
|
||||
shortcut: DOCUMENTS_PAGE_SHORTCUT.replace('+', ''),
|
||||
},
|
||||
{ label: 'Draft documents', path: '/documents?status=DRAFT' },
|
||||
{ label: msg`Draft documents`, path: '/documents?status=DRAFT' },
|
||||
{
|
||||
label: 'Completed documents',
|
||||
label: msg`Completed documents`,
|
||||
path: '/documents?status=COMPLETED',
|
||||
},
|
||||
{ label: 'Pending documents', path: '/documents?status=PENDING' },
|
||||
{ label: 'Inbox documents', path: '/documents?status=INBOX' },
|
||||
{ label: msg`Pending documents`, path: '/documents?status=PENDING' },
|
||||
{ label: msg`Inbox documents`, path: '/documents?status=INBOX' },
|
||||
];
|
||||
|
||||
const TEMPLATES_PAGES = [
|
||||
{
|
||||
label: 'All templates',
|
||||
label: msg`All templates`,
|
||||
path: '/templates',
|
||||
shortcut: TEMPLATES_PAGE_SHORTCUT.replace('+', ''),
|
||||
},
|
||||
@ -54,12 +57,12 @@ const TEMPLATES_PAGES = [
|
||||
|
||||
const SETTINGS_PAGES = [
|
||||
{
|
||||
label: 'Settings',
|
||||
label: msg`Settings`,
|
||||
path: '/settings',
|
||||
shortcut: SETTINGS_PAGE_SHORTCUT.replace('+', ''),
|
||||
},
|
||||
{ label: 'Profile', path: '/settings/profile' },
|
||||
{ label: 'Password', path: '/settings/password' },
|
||||
{ label: msg`Profile`, path: '/settings/profile' },
|
||||
{ label: msg`Password`, path: '/settings/password' },
|
||||
];
|
||||
|
||||
export type CommandMenuProps = {
|
||||
@ -68,6 +71,7 @@ export type CommandMenuProps = {
|
||||
};
|
||||
|
||||
export function CommandMenu({ open, onOpenChange }: CommandMenuProps) {
|
||||
const { _ } = useLingui();
|
||||
const { setTheme } = useTheme();
|
||||
|
||||
const router = useRouter();
|
||||
@ -174,7 +178,7 @@ export function CommandMenu({ open, onOpenChange }: CommandMenuProps) {
|
||||
<CommandInput
|
||||
value={search}
|
||||
onValueChange={setSearch}
|
||||
placeholder="Type a command or search..."
|
||||
placeholder={_(msg`Type a command or search...`)}
|
||||
/>
|
||||
|
||||
<CommandList>
|
||||
@ -187,26 +191,28 @@ export function CommandMenu({ open, onOpenChange }: CommandMenuProps) {
|
||||
</div>
|
||||
</CommandEmpty>
|
||||
) : (
|
||||
<CommandEmpty>No results found.</CommandEmpty>
|
||||
<CommandEmpty>
|
||||
<Trans>No results found.</Trans>
|
||||
</CommandEmpty>
|
||||
)}
|
||||
{!currentPage && (
|
||||
<>
|
||||
<CommandGroup className="mx-2 p-0 pb-2" heading="Documents">
|
||||
<CommandGroup className="mx-2 p-0 pb-2" heading={_(msg`Documents`)}>
|
||||
<Commands push={push} pages={DOCUMENTS_PAGES} />
|
||||
</CommandGroup>
|
||||
<CommandGroup className="mx-2 p-0 pb-2" heading="Templates">
|
||||
<CommandGroup className="mx-2 p-0 pb-2" heading={_(msg`Templates`)}>
|
||||
<Commands push={push} pages={TEMPLATES_PAGES} />
|
||||
</CommandGroup>
|
||||
<CommandGroup className="mx-2 p-0 pb-2" heading="Settings">
|
||||
<CommandGroup className="mx-2 p-0 pb-2" heading={_(msg`Settings`)}>
|
||||
<Commands push={push} pages={SETTINGS_PAGES} />
|
||||
</CommandGroup>
|
||||
<CommandGroup className="mx-2 p-0 pb-2" heading="Preferences">
|
||||
<CommandGroup className="mx-2 p-0 pb-2" heading={_(msg`Preferences`)}>
|
||||
<CommandItem className="-mx-2 -my-1 rounded-lg" onSelect={() => addPage('theme')}>
|
||||
Change theme
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
{searchResults.length > 0 && (
|
||||
<CommandGroup className="mx-2 p-0 pb-2" heading="Your documents">
|
||||
<CommandGroup className="mx-2 p-0 pb-2" heading={_(msg`Your documents`)}>
|
||||
<Commands push={push} pages={searchResults} />
|
||||
</CommandGroup>
|
||||
)}
|
||||
@ -223,27 +229,31 @@ const Commands = ({
|
||||
pages,
|
||||
}: {
|
||||
push: (_path: string) => void;
|
||||
pages: { label: string; path: string; shortcut?: string; value?: string }[];
|
||||
pages: { label: MessageDescriptor | string; path: string; shortcut?: string; value?: string }[];
|
||||
}) => {
|
||||
const { _ } = useLingui();
|
||||
|
||||
return pages.map((page, idx) => (
|
||||
<CommandItem
|
||||
className="-mx-2 -my-1 rounded-lg"
|
||||
key={page.path + idx}
|
||||
value={page.value ?? page.label}
|
||||
value={page.value ?? (typeof page.label === 'string' ? page.label : _(page.label))}
|
||||
onSelect={() => push(page.path)}
|
||||
>
|
||||
{page.label}
|
||||
{typeof page.label === 'string' ? page.label : _(page.label)}
|
||||
{page.shortcut && <CommandShortcut>{page.shortcut}</CommandShortcut>}
|
||||
</CommandItem>
|
||||
));
|
||||
};
|
||||
|
||||
const ThemeCommands = ({ setTheme }: { setTheme: (_theme: string) => void }) => {
|
||||
const { _ } = useLingui();
|
||||
|
||||
const THEMES = useMemo(
|
||||
() => [
|
||||
{ label: 'Light Mode', theme: THEMES_TYPE.LIGHT, icon: Sun },
|
||||
{ label: 'Dark Mode', theme: THEMES_TYPE.DARK, icon: Moon },
|
||||
{ label: 'System Theme', theme: THEMES_TYPE.SYSTEM, icon: Monitor },
|
||||
{ label: msg`Light Mode`, theme: THEMES_TYPE.LIGHT, icon: Sun },
|
||||
{ label: msg`Dark Mode`, theme: THEMES_TYPE.DARK, icon: Moon },
|
||||
{ label: msg`System Theme`, theme: THEMES_TYPE.SYSTEM, icon: Monitor },
|
||||
],
|
||||
[],
|
||||
);
|
||||
@ -255,7 +265,7 @@ const ThemeCommands = ({ setTheme }: { setTheme: (_theme: string) => void }) =>
|
||||
className="-my-1 mx-2 rounded-lg first:mt-2 last:mb-2"
|
||||
>
|
||||
<theme.icon className="mr-2" />
|
||||
{theme.label}
|
||||
{_(theme.label)}
|
||||
</CommandItem>
|
||||
));
|
||||
};
|
||||
|
||||
@ -4,6 +4,8 @@ import { useEffect, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { useParams, usePathname } from 'next/navigation';
|
||||
|
||||
import { Trans, msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Search } from 'lucide-react';
|
||||
|
||||
import { getRootHref } from '@documenso/lib/utils/params';
|
||||
@ -13,11 +15,11 @@ import { Button } from '@documenso/ui/primitives/button';
|
||||
const navigationLinks = [
|
||||
{
|
||||
href: '/documents',
|
||||
label: 'Documents',
|
||||
label: msg`Documents`,
|
||||
},
|
||||
{
|
||||
href: '/templates',
|
||||
label: 'Templates',
|
||||
label: msg`Templates`,
|
||||
},
|
||||
];
|
||||
|
||||
@ -26,6 +28,8 @@ export type DesktopNavProps = HTMLAttributes<HTMLDivElement> & {
|
||||
};
|
||||
|
||||
export const DesktopNav = ({ className, setIsCommandMenuOpen, ...props }: DesktopNavProps) => {
|
||||
const { _ } = useLingui();
|
||||
|
||||
const pathname = usePathname();
|
||||
const params = useParams();
|
||||
|
||||
@ -62,7 +66,7 @@ export const DesktopNav = ({ className, setIsCommandMenuOpen, ...props }: Deskto
|
||||
},
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
{_(label)}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
@ -74,7 +78,7 @@ export const DesktopNav = ({ className, setIsCommandMenuOpen, ...props }: Deskto
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<Search className="mr-2 h-5 w-5" />
|
||||
Search
|
||||
<Trans>Search</Trans>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
import { Trans, msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { CheckCircle2, ChevronsUpDown, Plus, Settings2 } from 'lucide-react';
|
||||
import { signOut } from 'next-auth/react';
|
||||
@ -35,6 +37,8 @@ export type MenuSwitcherProps = {
|
||||
};
|
||||
|
||||
export const MenuSwitcher = ({ user, teams: initialTeamsData }: MenuSwitcherProps) => {
|
||||
const { _ } = useLingui();
|
||||
|
||||
const pathname = usePathname();
|
||||
|
||||
const isUserAdmin = isAdmin(user);
|
||||
@ -65,14 +69,14 @@ export const MenuSwitcher = ({ user, teams: initialTeamsData }: MenuSwitcherProp
|
||||
|
||||
const formatSecondaryAvatarText = (team?: typeof selectedTeam) => {
|
||||
if (!team) {
|
||||
return 'Personal Account';
|
||||
return _(msg`Personal Account`);
|
||||
}
|
||||
|
||||
if (team.ownerUserId === user.id) {
|
||||
return 'Owner';
|
||||
return _(msg`Owner`);
|
||||
}
|
||||
|
||||
return TEAM_MEMBER_ROLE_MAP[team.currentTeamMember.role];
|
||||
return _(TEAM_MEMBER_ROLE_MAP[team.currentTeamMember.role]);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -121,7 +125,9 @@ export const MenuSwitcher = ({ user, teams: initialTeamsData }: MenuSwitcherProp
|
||||
>
|
||||
{teams ? (
|
||||
<>
|
||||
<DropdownMenuLabel>Personal</DropdownMenuLabel>
|
||||
<DropdownMenuLabel>
|
||||
<Trans>Personal</Trans>
|
||||
</DropdownMenuLabel>
|
||||
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href={formatRedirectUrlOnSwitch()}>
|
||||
@ -147,12 +153,14 @@ export const MenuSwitcher = ({ user, teams: initialTeamsData }: MenuSwitcherProp
|
||||
|
||||
<DropdownMenuLabel>
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
<p>Teams</p>
|
||||
<p>
|
||||
<Trans>Teams</Trans>
|
||||
</p>
|
||||
|
||||
<div className="flex flex-row space-x-2">
|
||||
<DropdownMenuItem asChild>
|
||||
<Button
|
||||
title="Manage teams"
|
||||
title={_(msg`Manage teams`)}
|
||||
variant="ghost"
|
||||
className="text-muted-foreground flex h-5 w-5 items-center justify-center p-0"
|
||||
asChild
|
||||
@ -165,7 +173,7 @@ export const MenuSwitcher = ({ user, teams: initialTeamsData }: MenuSwitcherProp
|
||||
|
||||
<DropdownMenuItem asChild>
|
||||
<Button
|
||||
title="Create team"
|
||||
title={_(msg`Create team`)}
|
||||
variant="ghost"
|
||||
className="text-muted-foreground flex h-5 w-5 items-center justify-center p-0"
|
||||
asChild
|
||||
@ -235,7 +243,7 @@ export const MenuSwitcher = ({ user, teams: initialTeamsData }: MenuSwitcherProp
|
||||
href="/settings/teams?action=add-team"
|
||||
className="flex items-center justify-between"
|
||||
>
|
||||
Create team
|
||||
<Trans>Create team</Trans>
|
||||
<Plus className="ml-2 h-4 w-4" />
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
@ -245,18 +253,24 @@ export const MenuSwitcher = ({ user, teams: initialTeamsData }: MenuSwitcherProp
|
||||
|
||||
{isUserAdmin && (
|
||||
<DropdownMenuItem className="text-muted-foreground px-4 py-2" asChild>
|
||||
<Link href="/admin">Admin panel</Link>
|
||||
<Link href="/admin">
|
||||
<Trans>Admin panel</Trans>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
|
||||
<DropdownMenuItem className="text-muted-foreground px-4 py-2" asChild>
|
||||
<Link href="/settings/profile">User settings</Link>
|
||||
<Link href="/settings/profile">
|
||||
<Trans>User settings</Trans>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
|
||||
{selectedTeam &&
|
||||
canExecuteTeamAction('MANAGE_TEAM', selectedTeam.currentTeamMember.role) && (
|
||||
<DropdownMenuItem className="text-muted-foreground px-4 py-2" asChild>
|
||||
<Link href={`/t/${selectedTeam.url}/settings/`}>Team settings</Link>
|
||||
<Link href={`/t/${selectedTeam.url}/settings/`}>
|
||||
<Trans>Team settings</Trans>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
|
||||
@ -268,7 +282,7 @@ export const MenuSwitcher = ({ user, teams: initialTeamsData }: MenuSwitcherProp
|
||||
})
|
||||
}
|
||||
>
|
||||
Sign Out
|
||||
<Trans>Sign Out</Trans>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
||||
@ -4,6 +4,8 @@ import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { useParams } from 'next/navigation';
|
||||
|
||||
import { Trans, msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { signOut } from 'next-auth/react';
|
||||
|
||||
import LogoImage from '@documenso/assets/logo.png';
|
||||
@ -17,6 +19,8 @@ export type MobileNavigationProps = {
|
||||
};
|
||||
|
||||
export const MobileNavigation = ({ isMenuOpen, onMenuOpenChange }: MobileNavigationProps) => {
|
||||
const { _ } = useLingui();
|
||||
|
||||
const params = useParams();
|
||||
|
||||
const handleMenuItemClick = () => {
|
||||
@ -28,19 +32,19 @@ export const MobileNavigation = ({ isMenuOpen, onMenuOpenChange }: MobileNavigat
|
||||
const menuNavigationLinks = [
|
||||
{
|
||||
href: `${rootHref}/documents`,
|
||||
text: 'Documents',
|
||||
text: msg`Documents`,
|
||||
},
|
||||
{
|
||||
href: `${rootHref}/templates`,
|
||||
text: 'Templates',
|
||||
text: msg`Templates`,
|
||||
},
|
||||
{
|
||||
href: '/settings/teams',
|
||||
text: 'Teams',
|
||||
text: msg`Teams`,
|
||||
},
|
||||
{
|
||||
href: '/settings/profile',
|
||||
text: 'Settings',
|
||||
text: msg`Settings`,
|
||||
},
|
||||
];
|
||||
|
||||
@ -65,7 +69,7 @@ export const MobileNavigation = ({ isMenuOpen, onMenuOpenChange }: MobileNavigat
|
||||
href={href}
|
||||
onClick={() => handleMenuItemClick()}
|
||||
>
|
||||
{text}
|
||||
{_(text)}
|
||||
</Link>
|
||||
))}
|
||||
|
||||
@ -77,7 +81,7 @@ export const MobileNavigation = ({ isMenuOpen, onMenuOpenChange }: MobileNavigat
|
||||
})
|
||||
}
|
||||
>
|
||||
Sign Out
|
||||
<Trans>Sign Out</Trans>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { Trans, msg } from '@lingui/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { AlertTriangle } from 'lucide-react';
|
||||
|
||||
import { ONE_DAY, ONE_SECOND } from '@documenso/lib/constants/time';
|
||||
@ -22,7 +24,9 @@ export type VerifyEmailBannerProps = {
|
||||
const RESEND_CONFIRMATION_EMAIL_TIMEOUT = 20 * ONE_SECOND;
|
||||
|
||||
export const VerifyEmailBanner = ({ email }: VerifyEmailBannerProps) => {
|
||||
const { _ } = useLingui();
|
||||
const { toast } = useToast();
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const [isButtonDisabled, setIsButtonDisabled] = useState(false);
|
||||
@ -37,8 +41,8 @@ export const VerifyEmailBanner = ({ email }: VerifyEmailBannerProps) => {
|
||||
await sendConfirmationEmail({ email: email });
|
||||
|
||||
toast({
|
||||
title: 'Success',
|
||||
description: 'Verification email sent successfully.',
|
||||
title: _(msg`Success`),
|
||||
description: _(msg`Verification email sent successfully.`),
|
||||
});
|
||||
|
||||
setIsOpen(false);
|
||||
@ -47,8 +51,8 @@ export const VerifyEmailBanner = ({ email }: VerifyEmailBannerProps) => {
|
||||
setIsButtonDisabled(false);
|
||||
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'Something went wrong while sending the confirmation email.',
|
||||
title: _(msg`Error`),
|
||||
description: _(msg`Something went wrong while sending the confirmation email.`),
|
||||
variant: 'destructive',
|
||||
});
|
||||
}
|
||||
@ -81,7 +85,7 @@ export const VerifyEmailBanner = ({ email }: VerifyEmailBannerProps) => {
|
||||
<div className="mx-auto flex max-w-screen-xl items-center justify-center gap-x-4 px-4 py-2 text-sm font-medium text-yellow-900">
|
||||
<div className="flex items-center">
|
||||
<AlertTriangle className="mr-2.5 h-5 w-5" />
|
||||
Verify your email address to unlock all features.
|
||||
<Trans>Verify your email address to unlock all features.</Trans>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@ -92,7 +96,11 @@ export const VerifyEmailBanner = ({ email }: VerifyEmailBannerProps) => {
|
||||
onClick={() => setIsOpen(true)}
|
||||
size="sm"
|
||||
>
|
||||
{isButtonDisabled ? 'Verification Email Sent' : 'Verify Now'}
|
||||
{isButtonDisabled ? (
|
||||
<Trans>Verification Email Sent</Trans>
|
||||
) : (
|
||||
<Trans>Verify Now</Trans>
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@ -100,11 +108,15 @@ export const VerifyEmailBanner = ({ email }: VerifyEmailBannerProps) => {
|
||||
|
||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||
<DialogContent>
|
||||
<DialogTitle>Verify your email address</DialogTitle>
|
||||
<DialogTitle>
|
||||
<Trans>Verify your email address</Trans>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogDescription>
|
||||
We've sent a confirmation email to <strong>{email}</strong>. Please check your inbox and
|
||||
click the link in the email to verify your account.
|
||||
<Trans>
|
||||
We've sent a confirmation email to <strong>{email}</strong>. Please check your inbox
|
||||
and click the link in the email to verify your account.
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
|
||||
<div>
|
||||
@ -113,7 +125,7 @@ export const VerifyEmailBanner = ({ email }: VerifyEmailBannerProps) => {
|
||||
loading={isLoading}
|
||||
onClick={onResendConfirmationEmail}
|
||||
>
|
||||
{isLoading ? 'Sending...' : 'Resend Confirmation Email'}
|
||||
{isLoading ? <Trans>Sending...</Trans> : <Trans>Resend Confirmation Email</Trans>}
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
|
||||
@ -4,6 +4,8 @@ import { useMemo } from 'react';
|
||||
|
||||
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@ -49,10 +51,18 @@ export const PeriodSelector = () => {
|
||||
</SelectTrigger>
|
||||
|
||||
<SelectContent position="popper">
|
||||
<SelectItem value="all">All Time</SelectItem>
|
||||
<SelectItem value="7d">Last 7 days</SelectItem>
|
||||
<SelectItem value="14d">Last 14 days</SelectItem>
|
||||
<SelectItem value="30d">Last 30 days</SelectItem>
|
||||
<SelectItem value="all">
|
||||
<Trans>All Time</Trans>
|
||||
</SelectItem>
|
||||
<SelectItem value="7d">
|
||||
<Trans>Last 7 days</Trans>
|
||||
</SelectItem>
|
||||
<SelectItem value="14d">
|
||||
<Trans>Last 14 days</Trans>
|
||||
</SelectItem>
|
||||
<SelectItem value="30d">
|
||||
<Trans>Last 30 days</Trans>
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
|
||||
export default function ActivityPageBackButton() {
|
||||
@ -15,7 +17,7 @@ export default function ActivityPageBackButton() {
|
||||
void router.back();
|
||||
}}
|
||||
>
|
||||
Back
|
||||
<Trans>Back</Trans>
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -5,6 +5,7 @@ import type { HTMLAttributes } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { Braces, CreditCard, Globe2Icon, Lock, User, Users, Webhook } from 'lucide-react';
|
||||
|
||||
import { useFeatureFlags } from '@documenso/lib/client-only/providers/feature-flag';
|
||||
@ -32,7 +33,7 @@ export const DesktopNav = ({ className, ...props }: DesktopNavProps) => {
|
||||
)}
|
||||
>
|
||||
<User className="mr-2 h-5 w-5" />
|
||||
Profile
|
||||
<Trans>Profile</Trans>
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
@ -46,7 +47,7 @@ export const DesktopNav = ({ className, ...props }: DesktopNavProps) => {
|
||||
)}
|
||||
>
|
||||
<Globe2Icon className="mr-2 h-5 w-5" />
|
||||
Public Profile
|
||||
<Trans>Public Profile</Trans>
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
@ -60,7 +61,7 @@ export const DesktopNav = ({ className, ...props }: DesktopNavProps) => {
|
||||
)}
|
||||
>
|
||||
<Users className="mr-2 h-5 w-5" />
|
||||
Teams
|
||||
<Trans>Teams</Trans>
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
@ -73,7 +74,7 @@ export const DesktopNav = ({ className, ...props }: DesktopNavProps) => {
|
||||
)}
|
||||
>
|
||||
<Lock className="mr-2 h-5 w-5" />
|
||||
Security
|
||||
<Trans>Security</Trans>
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
@ -86,7 +87,7 @@ export const DesktopNav = ({ className, ...props }: DesktopNavProps) => {
|
||||
)}
|
||||
>
|
||||
<Braces className="mr-2 h-5 w-5" />
|
||||
API Tokens
|
||||
<Trans>API Tokens</Trans>
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
@ -99,7 +100,7 @@ export const DesktopNav = ({ className, ...props }: DesktopNavProps) => {
|
||||
)}
|
||||
>
|
||||
<Webhook className="mr-2 h-5 w-5" />
|
||||
Webhooks
|
||||
<Trans>Webhooks</Trans>
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
@ -113,7 +114,7 @@ export const DesktopNav = ({ className, ...props }: DesktopNavProps) => {
|
||||
)}
|
||||
>
|
||||
<CreditCard className="mr-2 h-5 w-5" />
|
||||
Billing
|
||||
<Trans>Billing</Trans>
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
@ -5,6 +5,7 @@ import type { HTMLAttributes } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { Braces, CreditCard, Globe2Icon, Lock, User, Users, Webhook } from 'lucide-react';
|
||||
|
||||
import { useFeatureFlags } from '@documenso/lib/client-only/providers/feature-flag';
|
||||
@ -35,7 +36,7 @@ export const MobileNav = ({ className, ...props }: MobileNavProps) => {
|
||||
)}
|
||||
>
|
||||
<User className="mr-2 h-5 w-5" />
|
||||
Profile
|
||||
<Trans>Profile</Trans>
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
@ -49,7 +50,7 @@ export const MobileNav = ({ className, ...props }: MobileNavProps) => {
|
||||
)}
|
||||
>
|
||||
<Globe2Icon className="mr-2 h-5 w-5" />
|
||||
Public Profile
|
||||
<Trans>Public Profile</Trans>
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
@ -63,7 +64,7 @@ export const MobileNav = ({ className, ...props }: MobileNavProps) => {
|
||||
)}
|
||||
>
|
||||
<Users className="mr-2 h-5 w-5" />
|
||||
Teams
|
||||
<Trans>Teams</Trans>
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
@ -76,7 +77,7 @@ export const MobileNav = ({ className, ...props }: MobileNavProps) => {
|
||||
)}
|
||||
>
|
||||
<Lock className="mr-2 h-5 w-5" />
|
||||
Security
|
||||
<Trans>Security</Trans>
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
@ -89,7 +90,7 @@ export const MobileNav = ({ className, ...props }: MobileNavProps) => {
|
||||
)}
|
||||
>
|
||||
<Braces className="mr-2 h-5 w-5" />
|
||||
API Tokens
|
||||
<Trans>API Tokens</Trans>
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
@ -102,7 +103,7 @@ export const MobileNav = ({ className, ...props }: MobileNavProps) => {
|
||||
)}
|
||||
>
|
||||
<Webhook className="mr-2 h-5 w-5" />
|
||||
Webhooks
|
||||
<Trans>Webhooks</Trans>
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
@ -116,7 +117,7 @@ export const MobileNav = ({ className, ...props }: MobileNavProps) => {
|
||||
)}
|
||||
>
|
||||
<CreditCard className="mr-2 h-5 w-5" />
|
||||
Billing
|
||||
<Trans>Billing</Trans>
|
||||
</Button>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
@ -5,6 +5,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 { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
|
||||
@ -44,16 +46,18 @@ export default function DeleteTokenDialog({
|
||||
onDelete,
|
||||
children,
|
||||
}: DeleteTokenDialogProps) {
|
||||
const router = useRouter();
|
||||
const { _ } = useLingui();
|
||||
const { toast } = useToast();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const deleteMessage = `delete ${token.name}`;
|
||||
|
||||
const ZDeleteTokenDialogSchema = z.object({
|
||||
tokenName: z.literal(deleteMessage, {
|
||||
errorMap: () => ({ message: `You must enter '${deleteMessage}' to proceed` }),
|
||||
errorMap: () => ({ message: _(msg`You must enter '${deleteMessage}' to proceed`) }),
|
||||
}),
|
||||
});
|
||||
|
||||
@ -80,8 +84,8 @@ export default function DeleteTokenDialog({
|
||||
});
|
||||
|
||||
toast({
|
||||
title: 'Token deleted',
|
||||
description: 'The token was deleted successfully.',
|
||||
title: _(msg`Token deleted`),
|
||||
description: _(msg`The token was deleted successfully.`),
|
||||
duration: 5000,
|
||||
});
|
||||
|
||||
@ -90,11 +94,12 @@ export default function DeleteTokenDialog({
|
||||
router.refresh();
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: 'An unknown error occurred',
|
||||
title: _(msg`An unknown error occurred`),
|
||||
description: _(
|
||||
msg`We encountered an unknown error while attempting to delete this token. Please try again later.`,
|
||||
),
|
||||
variant: 'destructive',
|
||||
duration: 5000,
|
||||
description:
|
||||
'We encountered an unknown error while attempting to delete this token. Please try again later.',
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -113,18 +118,22 @@ export default function DeleteTokenDialog({
|
||||
<DialogTrigger asChild={true}>
|
||||
{children ?? (
|
||||
<Button className="mr-4" variant="destructive">
|
||||
Delete
|
||||
<Trans>Delete</Trans>
|
||||
</Button>
|
||||
)}
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Are you sure you want to delete this token?</DialogTitle>
|
||||
<DialogTitle>
|
||||
<Trans>Are you sure you want to delete this token?</Trans>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogDescription>
|
||||
Please note that this action is irreversible. Once confirmed, your token will be
|
||||
permanently deleted.
|
||||
<Trans>
|
||||
Please note that this action is irreversible. Once confirmed, your token will be
|
||||
permanently deleted.
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@ -140,10 +149,12 @@ export default function DeleteTokenDialog({
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
Confirm by typing:{' '}
|
||||
<span className="font-sm text-destructive font-semibold">
|
||||
{deleteMessage}
|
||||
</span>
|
||||
<Trans>
|
||||
Confirm by typing:{' '}
|
||||
<span className="font-sm text-destructive font-semibold">
|
||||
{deleteMessage}
|
||||
</span>
|
||||
</Trans>
|
||||
</FormLabel>
|
||||
|
||||
<FormControl>
|
||||
@ -162,7 +173,7 @@ export default function DeleteTokenDialog({
|
||||
className="flex-1"
|
||||
onClick={() => setIsOpen(false)}
|
||||
>
|
||||
Cancel
|
||||
<Trans>Cancel</Trans>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
@ -172,7 +183,7 @@ export default function DeleteTokenDialog({
|
||||
disabled={!form.formState.isValid}
|
||||
loading={form.formState.isSubmitting}
|
||||
>
|
||||
I'm sure! Delete it
|
||||
<Trans>I'm sure! Delete it</Trans>
|
||||
</Button>
|
||||
</div>
|
||||
</DialogFooter>
|
||||
|
||||
@ -5,6 +5,8 @@ import { 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 type * as DialogPrimitive from '@radix-ui/react-dialog';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import type { z } from 'zod';
|
||||
@ -48,9 +50,11 @@ export type CreateWebhookDialogProps = {
|
||||
} & Omit<DialogPrimitive.DialogProps, 'children'>;
|
||||
|
||||
export const CreateWebhookDialog = ({ trigger, ...props }: CreateWebhookDialogProps) => {
|
||||
const router = useRouter();
|
||||
const { _ } = useLingui();
|
||||
const { toast } = useToast();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const team = useOptionalCurrentTeam();
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
@ -85,8 +89,8 @@ export const CreateWebhookDialog = ({ trigger, ...props }: CreateWebhookDialogPr
|
||||
setOpen(false);
|
||||
|
||||
toast({
|
||||
title: 'Webhook created',
|
||||
description: 'The webhook was successfully created.',
|
||||
title: _(msg`Webhook created`),
|
||||
description: _(msg`The webhook was successfully created.`),
|
||||
});
|
||||
|
||||
form.reset();
|
||||
@ -94,8 +98,8 @@ export const CreateWebhookDialog = ({ trigger, ...props }: CreateWebhookDialogPr
|
||||
router.refresh();
|
||||
} catch (err) {
|
||||
toast({
|
||||
title: 'Error',
|
||||
description: 'An error occurred while creating the webhook. Please try again.',
|
||||
title: _(msg`Error`),
|
||||
description: _(msg`An error occurred while creating the webhook. Please try again.`),
|
||||
variant: 'destructive',
|
||||
});
|
||||
}
|
||||
@ -108,13 +112,21 @@ export const CreateWebhookDialog = ({ trigger, ...props }: CreateWebhookDialogPr
|
||||
{...props}
|
||||
>
|
||||
<DialogTrigger onClick={(e) => e.stopPropagation()} asChild>
|
||||
{trigger ?? <Button className="flex-shrink-0">Create Webhook</Button>}
|
||||
{trigger ?? (
|
||||
<Button className="flex-shrink-0">
|
||||
<Trans>Create Webhook</Trans>
|
||||
</Button>
|
||||
)}
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent className="max-w-lg" position="center">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Create webhook</DialogTitle>
|
||||
<DialogDescription>On this page, you can create a new webhook.</DialogDescription>
|
||||
<DialogTitle>
|
||||
<Trans>Create webhook</Trans>
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
<Trans>On this page, you can create a new webhook.</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<Form {...form}>
|
||||
@ -129,13 +141,15 @@ export const CreateWebhookDialog = ({ trigger, ...props }: CreateWebhookDialogPr
|
||||
name="webhookUrl"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex-1">
|
||||
<FormLabel required>Webhook URL</FormLabel>
|
||||
<FormLabel required>
|
||||
<Trans>Webhook URL</Trans>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input className="bg-background" {...field} />
|
||||
</FormControl>
|
||||
|
||||
<FormDescription>
|
||||
The URL for Documenso to send webhook events to.
|
||||
<Trans>The URL for Documenso to send webhook events to.</Trans>
|
||||
</FormDescription>
|
||||
|
||||
<FormMessage />
|
||||
@ -148,7 +162,9 @@ export const CreateWebhookDialog = ({ trigger, ...props }: CreateWebhookDialogPr
|
||||
name="enabled"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Enabled</FormLabel>
|
||||
<FormLabel>
|
||||
<Trans>Enabled</Trans>
|
||||
</FormLabel>
|
||||
|
||||
<div>
|
||||
<FormControl>
|
||||
@ -171,7 +187,9 @@ export const CreateWebhookDialog = ({ trigger, ...props }: CreateWebhookDialogPr
|
||||
name="eventTriggers"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<FormItem className="flex flex-col gap-2">
|
||||
<FormLabel required>Triggers</FormLabel>
|
||||
<FormLabel required>
|
||||
<Trans>Triggers</Trans>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<TriggerMultiSelectCombobox
|
||||
listValues={value}
|
||||
@ -182,7 +200,7 @@ export const CreateWebhookDialog = ({ trigger, ...props }: CreateWebhookDialogPr
|
||||
</FormControl>
|
||||
|
||||
<FormDescription>
|
||||
The events that will trigger a webhook to be sent to your URL.
|
||||
<Trans>The events that will trigger a webhook to be sent to your URL.</Trans>
|
||||
</FormDescription>
|
||||
|
||||
<FormMessage />
|
||||
@ -195,7 +213,9 @@ export const CreateWebhookDialog = ({ trigger, ...props }: CreateWebhookDialogPr
|
||||
name="secret"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Secret</FormLabel>
|
||||
<FormLabel>
|
||||
<Trans>Secret</Trans>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<PasswordInput
|
||||
className="bg-background"
|
||||
@ -205,8 +225,11 @@ export const CreateWebhookDialog = ({ trigger, ...props }: CreateWebhookDialogPr
|
||||
</FormControl>
|
||||
|
||||
<FormDescription>
|
||||
A secret that will be sent to your URL so you can verify that the request has
|
||||
been sent by Documenso.
|
||||
<Trans>
|
||||
A secret that will be sent to your URL so you can verify that the request
|
||||
has been sent by Documenso
|
||||
</Trans>
|
||||
.
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@ -216,10 +239,10 @@ export const CreateWebhookDialog = ({ trigger, ...props }: CreateWebhookDialogPr
|
||||
<DialogFooter>
|
||||
<div className="flex w-full flex-nowrap gap-4">
|
||||
<Button type="button" variant="secondary" onClick={() => setOpen(false)}>
|
||||
Cancel
|
||||
<Trans>Cancel</Trans>
|
||||
</Button>
|
||||
<Button type="submit" loading={form.formState.isSubmitting}>
|
||||
Create
|
||||
<Trans>Create</Trans>
|
||||
</Button>
|
||||
</div>
|
||||
</DialogFooter>
|
||||
|
||||
@ -5,6 +5,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 { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
|
||||
@ -40,9 +42,11 @@ export type DeleteWebhookDialogProps = {
|
||||
};
|
||||
|
||||
export const DeleteWebhookDialog = ({ webhook, children }: DeleteWebhookDialogProps) => {
|
||||
const router = useRouter();
|
||||
const { _ } = useLingui();
|
||||
const { toast } = useToast();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const team = useOptionalCurrentTeam();
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
@ -51,7 +55,7 @@ export const DeleteWebhookDialog = ({ webhook, children }: DeleteWebhookDialogPr
|
||||
|
||||
const ZDeleteWebhookFormSchema = z.object({
|
||||
webhookUrl: z.literal(deleteMessage, {
|
||||
errorMap: () => ({ message: `You must enter '${deleteMessage}' to proceed` }),
|
||||
errorMap: () => ({ message: _(msg`You must enter '${deleteMessage}' to proceed`) }),
|
||||
}),
|
||||
});
|
||||
|
||||
@ -71,9 +75,9 @@ export const DeleteWebhookDialog = ({ webhook, children }: DeleteWebhookDialogPr
|
||||
await deleteWebhook({ id: webhook.id, teamId: team?.id });
|
||||
|
||||
toast({
|
||||
title: 'Webhook deleted',
|
||||
title: _(msg`Webhook deleted`),
|
||||
description: _(msg`The webhook has been successfully deleted.`),
|
||||
duration: 5000,
|
||||
description: 'The webhook has been successfully deleted.',
|
||||
});
|
||||
|
||||
setOpen(false);
|
||||
@ -81,11 +85,12 @@ export const DeleteWebhookDialog = ({ webhook, children }: DeleteWebhookDialogPr
|
||||
router.refresh();
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: 'An unknown error occurred',
|
||||
title: _(msg`An unknown error occurred`),
|
||||
description: _(
|
||||
msg`We encountered an unknown error while attempting to delete it. Please try again later.`,
|
||||
),
|
||||
variant: 'destructive',
|
||||
duration: 5000,
|
||||
description:
|
||||
'We encountered an unknown error while attempting to delete it. Please try again later.',
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -101,18 +106,22 @@ export const DeleteWebhookDialog = ({ webhook, children }: DeleteWebhookDialogPr
|
||||
<DialogTrigger asChild>
|
||||
{children ?? (
|
||||
<Button className="mr-4" variant="destructive">
|
||||
Delete
|
||||
<Trans>Delete</Trans>
|
||||
</Button>
|
||||
)}
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Delete Webhook</DialogTitle>
|
||||
<DialogTitle>
|
||||
<Trans>Delete Webhook</Trans>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogDescription>
|
||||
Please note that this action is irreversible. Once confirmed, your webhook will be
|
||||
permanently deleted.
|
||||
<Trans>
|
||||
Please note that this action is irreversible. Once confirmed, your webhook will be
|
||||
permanently deleted.
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@ -128,10 +137,12 @@ export const DeleteWebhookDialog = ({ webhook, children }: DeleteWebhookDialogPr
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
Confirm by typing:{' '}
|
||||
<span className="font-sm text-destructive font-semibold">
|
||||
{deleteMessage}
|
||||
</span>
|
||||
<Trans>
|
||||
Confirm by typing:{' '}
|
||||
<span className="font-sm text-destructive font-semibold">
|
||||
{deleteMessage}
|
||||
</span>
|
||||
</Trans>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input className="bg-background" type="text" {...field} />
|
||||
@ -149,7 +160,7 @@ export const DeleteWebhookDialog = ({ webhook, children }: DeleteWebhookDialogPr
|
||||
className="flex-1"
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
Cancel
|
||||
<Trans>Cancel</Trans>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
@ -159,7 +170,7 @@ export const DeleteWebhookDialog = ({ webhook, children }: DeleteWebhookDialogPr
|
||||
disabled={!form.formState.isValid}
|
||||
loading={form.formState.isSubmitting}
|
||||
>
|
||||
I'm sure! Delete it
|
||||
<Trans>I'm sure! Delete it</Trans>
|
||||
</Button>
|
||||
</div>
|
||||
</DialogFooter>
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { Plural, Trans } from '@lingui/macro';
|
||||
import { WebhookTriggerEvents } from '@prisma/client/';
|
||||
import { Check, ChevronsUpDown } from 'lucide-react';
|
||||
|
||||
@ -60,7 +61,7 @@ export const TriggerMultiSelectCombobox = ({
|
||||
aria-expanded={isOpen}
|
||||
className="w-[200px] justify-between"
|
||||
>
|
||||
{selectedValues.length > 0 ? selectedValues.length + ' selected...' : 'Select values...'}
|
||||
<Plural value={selectedValues.length} zero="Select values" other="# selected..." />
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
@ -72,7 +73,9 @@ export const TriggerMultiSelectCombobox = ({
|
||||
15,
|
||||
)}
|
||||
/>
|
||||
<CommandEmpty>No value found.</CommandEmpty>
|
||||
<CommandEmpty>
|
||||
<Trans>No value found.</Trans>
|
||||
</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{allEvents.map((value: string, i: number) => (
|
||||
<CommandItem key={i} onSelect={() => handleSelect(value)}>
|
||||
|
||||
Reference in New Issue
Block a user