fix: lint project (#2693)

This commit is contained in:
David Nguyen
2026-05-08 16:04:22 +10:00
committed by GitHub
parent edbf65969b
commit 8671f269e8
1414 changed files with 12867 additions and 24335 deletions
@@ -1,6 +1,5 @@
import { redirect } from 'react-router';
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
import { redirect } from 'react-router';
import type { Route } from './+types/_index';
@@ -1,18 +1,13 @@
import { useMemo } from 'react';
import { msg } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { SubscriptionStatus } from '@prisma/client';
import { Link, Outlet } from 'react-router';
import {
DEFAULT_MINIMUM_ENVELOPE_ITEM_COUNT,
PAID_PLAN_LIMITS,
} from '@documenso/ee/server-only/limits/constants';
import { DEFAULT_MINIMUM_ENVELOPE_ITEM_COUNT, PAID_PLAN_LIMITS } from '@documenso/ee/server-only/limits/constants';
import { LimitsProvider } from '@documenso/ee/server-only/limits/provider/client';
import { useOptionalCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
import { TrpcProvider } from '@documenso/trpc/react';
import { Button } from '@documenso/ui/primitives/button';
import { msg } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { SubscriptionStatus } from '@prisma/client';
import { useMemo } from 'react';
import { Link, Outlet } from 'react-router';
import { GenericErrorLayout } from '~/components/general/generic-error-layout';
import { useOptionalCurrentTeam } from '~/providers/team';
@@ -26,10 +21,7 @@ export default function Layout() {
return undefined;
}
if (
organisation?.subscription &&
organisation.subscription.status === SubscriptionStatus.INACTIVE
) {
if (organisation?.subscription && organisation.subscription.status === SubscriptionStatus.INACTIVE) {
return {
quota: {
documents: 0,
@@ -1,10 +1,3 @@
import { msg } from '@lingui/core/macro';
import { Plural, Trans, useLingui } from '@lingui/react/macro';
import { DocumentStatus } from '@prisma/client';
import { ChevronLeft, Users2 } from 'lucide-react';
import { Link } from 'react-router';
import { match } from 'ts-pattern';
import { EnvelopeRenderProvider } from '@documenso/lib/client-only/providers/envelope-render-provider';
import { useSession } from '@documenso/lib/client-only/providers/session';
import { PDF_VIEWER_ERROR_MESSAGES } from '@documenso/lib/constants/pdf-viewer-i18n';
@@ -22,6 +15,12 @@ import { Badge } from '@documenso/ui/primitives/badge';
import { Button } from '@documenso/ui/primitives/button';
import { Card, CardContent } from '@documenso/ui/primitives/card';
import { Spinner } from '@documenso/ui/primitives/spinner';
import { msg } from '@lingui/core/macro';
import { Plural, Trans, useLingui } from '@lingui/react/macro';
import { DocumentStatus } from '@prisma/client';
import { ChevronLeft, Users2 } from 'lucide-react';
import { Link } from 'react-router';
import { match } from 'ts-pattern';
import { DocumentPageViewButton } from '~/components/general/document/document-page-view-button';
import { DocumentPageViewDropdown } from '~/components/general/document/document-page-view-dropdown';
@@ -68,8 +67,7 @@ export default function DocumentPage({ params }: Route.ComponentProps) {
},
{
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
enabled:
envelope && envelope.internalVersion === 2 && envelope.status === DocumentStatus.PENDING,
enabled: envelope && envelope.internalVersion === 2 && envelope.status === DocumentStatus.PENDING,
},
);
@@ -121,16 +119,12 @@ export default function DocumentPage({ params }: Route.ComponentProps) {
<div className="flex flex-row justify-between gap-4">
<div className="min-w-0">
<h1 className="mt-4 block text-2xl font-semibold md:text-3xl" title={envelope.title}>
<h1 className="mt-4 block font-semibold text-2xl md:text-3xl" title={envelope.title}>
{envelope.title}
</h1>
<div className="mt-2.5 flex items-center gap-x-6">
<DocumentStatusComponent
inheritColor
status={envelope.status}
className="text-muted-foreground"
/>
<DocumentStatusComponent inheritColor status={envelope.status} className="text-muted-foreground" />
{envelope.recipients.length > 0 && (
<div className="flex items-center text-muted-foreground">
@@ -142,11 +136,7 @@ export default function DocumentPage({ params }: Route.ComponentProps) {
position="bottom"
>
<span>
<Plural
value={envelope.recipients.length}
one="# Recipient"
other="# Recipients"
/>
<Plural value={envelope.recipients.length} one="# Recipient" other="# Recipients" />
</span>
</StackAvatarsWithTooltip>
</div>
@@ -177,9 +167,7 @@ export default function DocumentPage({ params }: Route.ComponentProps) {
showRecipientTooltip: true,
}}
>
{isMultiEnvelopeItem && (
<EnvelopeRendererFileSelector fields={envelope.fields} className="mb-4 p-0" />
)}
{isMultiEnvelopeItem && <EnvelopeRendererFileSelector fields={envelope.fields} className="mb-4 p-0" />}
<Card className="rounded-xl before:rounded-xl" gradient>
<CardContent className="p-2">
@@ -193,10 +181,7 @@ export default function DocumentPage({ params }: Route.ComponentProps) {
</EnvelopeRenderProvider>
</div>
) : (
<Card
className="relative col-span-12 rounded-xl before:rounded-xl lg:col-span-6 xl:col-span-7"
gradient
>
<Card className="relative col-span-12 rounded-xl before:rounded-xl lg:col-span-6 xl:col-span-7" gradient>
<CardContent className="p-2">
{envelope.status !== DocumentStatus.COMPLETED && (
<DocumentReadOnlyFields
@@ -224,27 +209,21 @@ export default function DocumentPage({ params }: Route.ComponentProps) {
</Card>
)}
<div
className={cn('col-span-12 lg:col-span-6 xl:col-span-5', isMultiEnvelopeItem && 'mt-20')}
>
<div className={cn('col-span-12 lg:col-span-6 xl:col-span-5', isMultiEnvelopeItem && 'mt-20')}>
<div className="space-y-6">
<section className="flex flex-col rounded-xl border border-border bg-widget pb-4 pt-6">
<section className="flex flex-col rounded-xl border border-border bg-widget pt-6 pb-4">
<div className="flex flex-row items-center justify-between px-4">
<h3 className="text-2xl font-semibold text-foreground">
<h3 className="font-semibold text-2xl text-foreground">
{t(FRIENDLY_STATUS_MAP[envelope.status].labelExtended)}
</h3>
<DocumentPageViewDropdown envelope={envelope} />
</div>
<p className="mt-2 px-4 text-sm text-muted-foreground">
<p className="mt-2 px-4 text-muted-foreground text-sm">
{match(envelope.status)
.with(DocumentStatus.COMPLETED, () => (
<Trans>This document has been signed by all recipients</Trans>
))
.with(DocumentStatus.REJECTED, () => (
<Trans>This document has been rejected by a recipient</Trans>
))
.with(DocumentStatus.COMPLETED, () => <Trans>This document has been signed by all recipients</Trans>)
.with(DocumentStatus.REJECTED, () => <Trans>This document has been rejected by a recipient</Trans>)
.with(DocumentStatus.DRAFT, () => (
<Trans>This document is currently a draft and has not been sent</Trans>
))
@@ -1,15 +1,14 @@
import { msg } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { EnvelopeType } from '@prisma/client';
import { ChevronLeftIcon } from 'lucide-react';
import { Link, Outlet, isRouteErrorResponse, redirect } from 'react-router';
import type { ShouldRevalidateFunctionArgs } from 'react-router';
import { getSession } from '@documenso/auth/server/lib/utils/get-session';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
import { getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
import { Button } from '@documenso/ui/primitives/button';
import { msg } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { EnvelopeType } from '@prisma/client';
import { ChevronLeftIcon } from 'lucide-react';
import type { ShouldRevalidateFunctionArgs } from 'react-router';
import { isRouteErrorResponse, Link, Outlet, redirect } from 'react-router';
import { GenericErrorLayout } from '~/components/general/generic-error-layout';
@@ -1,16 +1,14 @@
import { useEffect } from 'react';
import { msg } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { EnvelopeType } from '@prisma/client';
import { Link, useNavigate } from 'react-router';
import { EnvelopeEditorProvider } from '@documenso/lib/client-only/providers/envelope-editor-provider';
import { DO_NOT_INVALIDATE_QUERY_ON_MUTATION } from '@documenso/lib/constants/trpc';
import { formatDocumentsPath, formatTemplatesPath } from '@documenso/lib/utils/teams';
import { trpc } from '@documenso/trpc/react';
import { Button } from '@documenso/ui/primitives/button';
import { Spinner } from '@documenso/ui/primitives/spinner';
import { msg } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { EnvelopeType } from '@prisma/client';
import { useEffect } from 'react';
import { Link, useNavigate } from 'react-router';
import { EnvelopeEditor } from '~/components/general/envelope-editor/envelope-editor';
import { EnvelopeEditorRenderProviderWrapper } from '~/components/general/envelope-editor/envelope-editor-renderer-provider-wrapper';
@@ -48,9 +46,7 @@ export default function EnvelopeEditorPage({ params }: Route.ComponentProps) {
}
const pathPrefix =
envelope.type === EnvelopeType.DOCUMENT
? formatDocumentsPath(team.url)
: formatTemplatesPath(team.url);
envelope.type === EnvelopeType.DOCUMENT ? formatDocumentsPath(team.url) : formatTemplatesPath(team.url);
if (envelope.teamId !== team.id) {
void navigate(pathPrefix, { replace: true });
@@ -1,13 +1,12 @@
import { Plural, Trans } from '@lingui/react/macro';
import { ChevronLeft, Users2 } from 'lucide-react';
import { Link, redirect } from 'react-router';
import { getSession } from '@documenso/auth/server/lib/utils/get-session';
import { getDocumentWithDetailsById } from '@documenso/lib/server-only/document/get-document-with-details-by-id';
import { getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { logDocumentAccess } from '@documenso/lib/utils/logger';
import { canAccessTeamDocument, formatDocumentsPath } from '@documenso/lib/utils/teams';
import { Plural, Trans } from '@lingui/react/macro';
import { ChevronLeft, Users2 } from 'lucide-react';
import { Link, redirect } from 'react-router';
import { DocumentAttachmentsPopover } from '~/components/general/document/document-attachments-popover';
import { DocumentEditForm } from '~/components/general/document/document-edit-form';
@@ -92,28 +91,20 @@ export default function DocumentEditPage() {
<div className="mt-4 flex w-full items-end justify-between">
<div className="flex-1">
<h1
className="block max-w-[20rem] truncate text-2xl font-semibold md:max-w-[30rem] md:text-3xl"
className="block max-w-[20rem] truncate font-semibold text-2xl md:max-w-[30rem] md:text-3xl"
title={document.title}
>
{document.title}
</h1>
<div className="mt-2.5 flex items-center gap-x-6">
<DocumentStatus
inheritColor
status={document.status}
className="text-muted-foreground"
/>
<DocumentStatus inheritColor status={document.status} className="text-muted-foreground" />
{recipients.length > 0 && (
<div className="flex items-center text-muted-foreground">
<Users2 className="mr-2 h-5 w-5" />
<StackAvatarsWithTooltip
recipients={recipients}
documentStatus={document.status}
position="bottom"
>
<StackAvatarsWithTooltip recipients={recipients} documentStatus={document.status} position="bottom">
<span>
<Plural one="1 Recipient" other="# Recipients" value={recipients.length} />
</span>
@@ -126,17 +117,11 @@ export default function DocumentEditPage() {
<div className="flex items-center gap-x-4">
<DocumentAttachmentsPopover envelopeId={document.envelopeId} />
{document.useLegacyFieldInsertion && (
<LegacyFieldWarningPopover type="document" documentId={document.id} />
)}
{document.useLegacyFieldInsertion && <LegacyFieldWarningPopover type="document" documentId={document.id} />}
</div>
</div>
<DocumentEditForm
className="mt-6"
initialDocument={document}
documentRootPath={documentRootPath}
/>
<DocumentEditForm className="mt-6" initialDocument={document} documentRootPath={documentRootPath} />
</div>
);
}
@@ -1,3 +1,10 @@
import { getSession } from '@documenso/auth/server/lib/utils/get-session';
import { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
import { getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
import { mapSecondaryIdToDocumentId } from '@documenso/lib/utils/envelope';
import { logDocumentAccess } from '@documenso/lib/utils/logger';
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
import { Card } from '@documenso/ui/primitives/card';
import type { MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
@@ -7,14 +14,6 @@ import { ChevronLeft } from 'lucide-react';
import { DateTime } from 'luxon';
import { Link } from 'react-router';
import { getSession } from '@documenso/auth/server/lib/utils/get-session';
import { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
import { getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
import { mapSecondaryIdToDocumentId } from '@documenso/lib/utils/envelope';
import { logDocumentAccess } from '@documenso/lib/utils/logger';
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
import { Card } from '@documenso/ui/primitives/card';
import { DocumentAuditLogDownloadButton } from '~/components/general/document/document-audit-log-download-button';
import { DocumentCertificateDownloadButton } from '~/components/general/document/document-certificate-download-button';
import {
@@ -99,9 +98,7 @@ export default function DocumentsLogsPage({ loaderData }: Route.ComponentProps)
},
{
description: msg`Created by`,
value: document.user.name
? `${document.user.name} (${document.user.email})`
: document.user.email,
value: document.user.name ? `${document.user.name} (${document.user.email})` : document.user.email,
},
{
description: msg`Date created`,
@@ -143,7 +140,7 @@ export default function DocumentsLogsPage({ loaderData }: Route.ComponentProps)
<div className="flex flex-col">
<div>
<h1
className="mt-4 block max-w-[20rem] truncate text-2xl font-semibold md:max-w-[30rem] md:text-3xl"
className="mt-4 block max-w-[20rem] truncate font-semibold text-2xl md:max-w-[30rem] md:text-3xl"
title={document.title}
>
{document.title}
@@ -151,11 +148,7 @@ export default function DocumentsLogsPage({ loaderData }: Route.ComponentProps)
</div>
<div className="mt-1 flex flex-col justify-between sm:flex-row">
<div className="mt-2.5 flex items-center gap-x-6">
<DocumentStatusComponent
inheritColor
status={document.status}
className="text-muted-foreground"
/>
<DocumentStatusComponent inheritColor status={document.status} className="text-muted-foreground" />
</div>
<div className="mt-4 flex w-full flex-row sm:mt-0 sm:w-auto sm:self-end">
<DocumentCertificateDownloadButton
@@ -172,13 +165,13 @@ export default function DocumentsLogsPage({ loaderData }: Route.ComponentProps)
<section className="mt-6">
<Card className="grid grid-cols-1 gap-4 p-4 sm:grid-cols-2" degrees={45} gradient>
{documentInformation.map((info, i) => (
<div className="text-sm text-foreground" key={i}>
<div className="text-foreground text-sm" key={i}>
<h3 className="font-semibold">{_(info.description)}</h3>
<p className="truncate text-muted-foreground">{info.value}</p>
</div>
))}
<div className="text-sm text-foreground">
<div className="text-foreground text-sm">
<h3 className="font-semibold">
<Trans>Recipients</Trans>
</h3>
@@ -1,13 +1,3 @@
import { useEffect, useMemo, useState } from 'react';
import { msg } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { EnvelopeType } from '@prisma/client';
import { FolderType, OrganisationType } from '@prisma/client';
import { useParams, useSearchParams } from 'react-router';
import { Link } from 'react-router';
import { z } from 'zod';
import { useSessionStorage } from '@documenso/lib/client-only/hooks/use-session-storage';
import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
import { STATS_COUNT_CAP } from '@documenso/lib/constants/document';
@@ -22,6 +12,12 @@ import { ZFindDocumentsInternalRequestSchema } from '@documenso/trpc/server/docu
import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/avatar';
import type { RowSelectionState } from '@documenso/ui/primitives/data-table';
import { Tabs, TabsList, TabsTrigger } from '@documenso/ui/primitives/tabs';
import { msg } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { EnvelopeType, FolderType, OrganisationType } from '@prisma/client';
import { useEffect, useMemo, useState } from 'react';
import { Link, useParams, useSearchParams } from 'react-router';
import { z } from 'zod';
import { DocumentMoveToFolderDialog } from '~/components/dialogs/document-move-to-folder-dialog';
import { EnvelopesBulkDeleteDialog } from '~/components/dialogs/envelopes-bulk-delete-dialog';
@@ -62,10 +58,7 @@ export default function DocumentsPage() {
const [isMovingDocument, setIsMovingDocument] = useState(false);
const [documentToMove, setDocumentToMove] = useState<number | null>(null);
const [rowSelection, setRowSelection] = useSessionStorage<RowSelectionState>(
'documents-bulk-selection',
{},
);
const [rowSelection, setRowSelection] = useSessionStorage<RowSelectionState>('documents-bulk-selection', {});
const [isBulkMoveDialogOpen, setIsBulkMoveDialogOpen] = useState(false);
const [isBulkDeleteDialogOpen, setIsBulkDeleteDialogOpen] = useState(false);
@@ -140,14 +133,12 @@ export default function DocumentsPage() {
<div className="mt-8 flex flex-wrap items-center justify-between gap-x-4 gap-y-8">
<div className="flex flex-row items-center">
<Avatar className="mr-3 h-12 w-12 border-2 border-solid border-white dark:border-border">
<Avatar className="mr-3 h-12 w-12 border-2 border-white border-solid dark:border-border">
{team.avatarImageId && <AvatarImage src={formatAvatarUrl(team.avatarImageId)} />}
<AvatarFallback className="text-xs text-muted-foreground">
{team.name.slice(0, 1)}
</AvatarFallback>
<AvatarFallback className="text-muted-foreground text-xs">{team.name.slice(0, 1)}</AvatarFallback>
</Avatar>
<h2 className="text-4xl font-semibold">
<h2 className="font-semibold text-4xl">
<Trans>Documents</Trans>
</h2>
</div>
@@ -170,20 +161,13 @@ export default function DocumentsPage() {
return true;
})
.map((value) => (
<TabsTrigger
key={value}
className="min-w-[60px] hover:text-foreground"
value={value}
asChild
>
<TabsTrigger key={value} className="min-w-[60px] hover:text-foreground" value={value} asChild>
<Link to={getTabHref(value)} preventScrollReset>
<DocumentStatus status={value} />
{value !== ExtendedDocumentStatus.ALL && (
<span className="ml-1 inline-block opacity-50">
{stats[value] >= STATS_COUNT_CAP
? `${STATS_COUNT_CAP.toLocaleString()}+`
: stats[value]}
{stats[value] >= STATS_COUNT_CAP ? `${STATS_COUNT_CAP.toLocaleString()}+` : stats[value]}
</span>
)}
</Link>
@@ -206,9 +190,7 @@ export default function DocumentsPage() {
<div className="mt-8">
<div>
{data && data.count === 0 ? (
<DocumentsTableEmptyState
status={findDocumentSearchParams.status || ExtendedDocumentStatus.ALL}
/>
<DocumentsTableEmptyState status={findDocumentSearchParams.status || ExtendedDocumentStatus.ALL} />
) : (
<DocumentsTable
data={data}
@@ -1,15 +1,13 @@
import { useState } from 'react';
import { msg } from '@lingui/core/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { FolderIcon, HomeIcon, Loader2, SearchIcon } from 'lucide-react';
import { Link, useSearchParams } from 'react-router';
import { FolderType } from '@documenso/lib/types/folder-type';
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
import { trpc } from '@documenso/trpc/react';
import { type TFolderWithSubfolders } from '@documenso/trpc/server/folder-router/schema';
import type { TFolderWithSubfolders } from '@documenso/trpc/server/folder-router/schema';
import { Input } from '@documenso/ui/primitives/input';
import { msg } from '@lingui/core/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { FolderIcon, HomeIcon, Loader2, SearchIcon } from 'lucide-react';
import { useState } from 'react';
import { Link, useSearchParams } from 'react-router';
import { FolderCreateDialog } from '~/components/dialogs/folder-create-dialog';
import { FolderDeleteDialog } from '~/components/dialogs/folder-delete-dialog';
@@ -64,11 +62,8 @@ export default function DocumentsFoldersPage() {
return (
<div className="mx-auto w-full max-w-screen-xl px-4 md:px-8">
<div className="flex w-full items-center justify-between">
<div className="flex flex-1 items-center text-sm font-medium text-muted-foreground">
<Link
to={formatDocumentsPath(team.url)}
className="flex items-center hover:text-muted-foreground/80"
>
<div className="flex flex-1 items-center font-medium text-muted-foreground text-sm">
<Link to={formatDocumentsPath(team.url)} className="flex items-center hover:text-muted-foreground/80">
<HomeIcon className="mr-2 h-4 w-4" />
<Trans>Home</Trans>
</Link>
@@ -76,10 +71,7 @@ export default function DocumentsFoldersPage() {
{foldersData?.breadcrumbs.map((folder) => (
<div key={folder.id} className="flex items-center">
<span className="px-3">/</span>
<Link
to={formatBreadcrumbPath(folder.id)}
className="flex items-center hover:text-muted-foreground/80"
>
<Link to={formatBreadcrumbPath(folder.id)} className="flex items-center hover:text-muted-foreground/80">
<FolderIcon className="mr-2 h-4 w-4" />
<span>{folder.name}</span>
</Link>
@@ -93,7 +85,7 @@ export default function DocumentsFoldersPage() {
</div>
<div className="relative w-full max-w-md py-6">
<SearchIcon className="absolute left-2 top-9 h-4 w-4 text-muted-foreground" />
<SearchIcon className="absolute top-9 left-2 h-4 w-4 text-muted-foreground" />
<Input
placeholder={t`Search folders...`}
value={searchTerm}
@@ -102,7 +94,7 @@ export default function DocumentsFoldersPage() {
/>
</div>
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl">
<h1 className="mt-4 truncate font-semibold text-2xl md:text-3xl">
<Trans>All Folders</Trans>
</h1>
@@ -1,7 +1,3 @@
import { Trans } from '@lingui/react/macro';
import { CheckCircle2, Clock } from 'lucide-react';
import { P, match } from 'ts-pattern';
import { getSession } from '@documenso/auth/server/lib/utils/get-session';
import { getTeamWithEmail } from '@documenso/lib/server-only/team/get-team-email-by-email';
import { formatAvatarUrl } from '@documenso/lib/utils/avatars';
@@ -9,6 +5,9 @@ import { extractInitials } from '@documenso/lib/utils/recipient-formatter';
import { canExecuteTeamAction } from '@documenso/lib/utils/teams';
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
import { AvatarWithText } from '@documenso/ui/primitives/avatar';
import { Trans } from '@lingui/react/macro';
import { CheckCircle2, Clock } from 'lucide-react';
import { match, P } from 'ts-pattern';
import { TeamDeleteDialog } from '~/components/dialogs/team-delete-dialog';
import { TeamEmailAddDialog } from '~/components/dialogs/team-email-add-dialog';
@@ -55,34 +54,29 @@ export default function TeamsSettingsPage({ loaderData }: Route.ComponentProps)
<AlertDescription className="mr-2">
<Trans>
You can view documents associated with this email and use this identity when sending
documents.
You can view documents associated with this email and use this identity when sending documents.
</Trans>
</AlertDescription>
<hr className="border-border/50 mt-2" />
<hr className="mt-2 border-border/50" />
<div className="flex flex-row items-center justify-between pt-4">
<AvatarWithText
avatarClass="h-12 w-12"
avatarSrc={formatAvatarUrl(team.avatarImageId)}
avatarFallback={extractInitials(
(team.teamEmail?.name || team.emailVerification?.name) ?? '',
)}
avatarFallback={extractInitials((team.teamEmail?.name || team.emailVerification?.name) ?? '')}
primaryText={
<span className="text-foreground/80 text-sm font-semibold">
<span className="font-semibold text-foreground/80 text-sm">
{team.teamEmail?.name || team.emailVerification?.name}
</span>
}
secondaryText={
<span className="text-sm">
{team.teamEmail?.email || team.emailVerification?.email}
</span>
<span className="text-sm">{team.teamEmail?.email || team.emailVerification?.email}</span>
}
/>
<div className="flex flex-row items-center pr-2">
<div className="text-muted-foreground mr-4 flex flex-row items-center text-sm xl:mr-8">
<div className="mr-4 flex flex-row items-center text-muted-foreground text-sm xl:mr-8">
{match({
teamEmail: team.teamEmail,
emailVerification: team.emailVerification,
@@ -96,8 +90,7 @@ export default function TeamsSettingsPage({ loaderData }: Route.ComponentProps)
.with(
{
emailVerification: P.when(
(emailVerification) =>
emailVerification && emailVerification?.expiresAt < new Date(),
(emailVerification) => emailVerification && emailVerification?.expiresAt < new Date(),
),
},
() => (
@@ -123,17 +116,14 @@ export default function TeamsSettingsPage({ loaderData }: Route.ComponentProps)
)}
{!team.teamEmail && !team.emailVerification && (
<Alert
className="flex flex-col justify-between p-6 sm:flex-row sm:items-center"
variant="neutral"
>
<Alert className="flex flex-col justify-between p-6 sm:flex-row sm:items-center" variant="neutral">
<div className="mb-4 sm:mb-0">
<AlertTitle>
<Trans>Team email</Trans>
</AlertTitle>
<AlertDescription className="mr-2">
<ul className="text-muted-foreground mt-0.5 list-inside list-disc text-sm">
<ul className="mt-0.5 list-inside list-disc text-muted-foreground text-sm">
{/* Feature not available yet. */}
{/* <li>Display this name and email when sending documents</li> */}
{/* <li>View documents associated with this email</li> */}
@@ -150,10 +140,7 @@ export default function TeamsSettingsPage({ loaderData }: Route.ComponentProps)
)}
{canExecuteTeamAction('MANAGE_TEAM', currentTeam.currentTeamRole) && (
<Alert
className="flex flex-col justify-between p-6 sm:flex-row sm:items-center"
variant="neutral"
>
<Alert className="flex flex-col justify-between p-6 sm:flex-row sm:items-center" variant="neutral">
<div className="mb-4 sm:mb-0">
<AlertTitle>
<Trans>Delete team</Trans>
@@ -161,8 +148,7 @@ export default function TeamsSettingsPage({ loaderData }: Route.ComponentProps)
<AlertDescription className="mr-2">
<Trans>
This team, and any associated data excluding billing invoices will be permanently
deleted.
This team, and any associated data excluding billing invoices will be permanently deleted.
</Trans>
</AlertDescription>
</div>
@@ -1,21 +1,12 @@
import { msg } from '@lingui/core/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import {
BracesIcon,
Globe2Icon,
GroupIcon,
Settings2Icon,
SettingsIcon,
Users2Icon,
WebhookIcon,
} from 'lucide-react';
import { Link, NavLink, Outlet, redirect } from 'react-router';
import { getSession } from '@documenso/auth/server/lib/utils/get-session';
import { getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
import { canExecuteTeamAction } from '@documenso/lib/utils/teams';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import { msg } from '@lingui/core/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { BracesIcon, Globe2Icon, GroupIcon, Settings2Icon, SettingsIcon, Users2Icon, WebhookIcon } from 'lucide-react';
import { Link, NavLink, Outlet, redirect } from 'react-router';
import { GenericErrorLayout } from '~/components/general/generic-error-layout';
import { useCurrentTeam } from '~/providers/team';
@@ -128,7 +119,7 @@ export default function TeamsSettingsLayout() {
return (
<div className="mx-auto w-full max-w-screen-xl px-4 md:px-8">
<h1 className="text-4xl font-semibold">
<h1 className="font-semibold text-4xl">
<Trans>Team Settings</Trans>
</h1>
@@ -1,10 +1,9 @@
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react/macro';
import { Loader } from 'lucide-react';
import { putFile } from '@documenso/lib/universal/upload/put-file';
import { trpc } from '@documenso/trpc/react';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react/macro';
import { Loader } from 'lucide-react';
import {
BrandingPreferencesForm,
@@ -34,7 +33,7 @@ export default function TeamsSettingsPage() {
try {
const { brandingEnabled, brandingLogo, brandingUrl, brandingCompanyDetails } = data;
let uploadedBrandingLogo: string | undefined = undefined;
let uploadedBrandingLogo: string | undefined;
if (brandingLogo) {
uploadedBrandingLogo = JSON.stringify(await putFile(brandingLogo));
@@ -1,12 +1,11 @@
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react/macro';
import { Loader } from 'lucide-react';
import { useLoaderData } from 'react-router';
import { IS_AI_FEATURES_CONFIGURED } from '@documenso/lib/constants/app';
import { DocumentSignatureType } from '@documenso/lib/constants/document';
import { trpc } from '@documenso/trpc/react';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react/macro';
import { Loader } from 'lucide-react';
import { useLoaderData } from 'react-router';
import {
DocumentPreferencesForm,
@@ -1,14 +1,10 @@
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react/macro';
import { trpc } from '@documenso/trpc/react';
import { SpinnerBox } from '@documenso/ui/primitives/spinner';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react/macro';
import {
EmailPreferencesForm,
type TEmailPreferencesFormSchema,
} from '~/components/forms/email-preferences-form';
import { EmailPreferencesForm, type TEmailPreferencesFormSchema } from '~/components/forms/email-preferences-form';
import { SettingsHeader } from '~/components/general/settings-header';
import { useCurrentTeam } from '~/providers/team';
import { appMetaTags } from '~/utils/meta';
@@ -62,10 +58,7 @@ export default function TeamEmailSettingsGeneral() {
return (
<div className="max-w-2xl">
<SettingsHeader
title={t`Email Preferences`}
subtitle={t`You can manage your email preferences here.`}
/>
<SettingsHeader title={t`Email Preferences`} subtitle={t`You can manage your email preferences here.`} />
<section>
<EmailPreferencesForm
@@ -1,13 +1,11 @@
import { useEffect, useState } from 'react';
import { useLingui } from '@lingui/react/macro';
import { OrganisationGroupType, OrganisationMemberRole } from '@prisma/client';
import { useLocation, useSearchParams } from 'react-router';
import { useDebouncedValue } from '@documenso/lib/client-only/hooks/use-debounced-value';
import { trpc } from '@documenso/trpc/react';
import { AnimateGenericFadeInOut } from '@documenso/ui/components/animate/animate-generic-fade-in-out';
import { Input } from '@documenso/ui/primitives/input';
import { useLingui } from '@lingui/react/macro';
import { OrganisationGroupType, OrganisationMemberRole } from '@prisma/client';
import { useEffect, useState } from 'react';
import { useLocation, useSearchParams } from 'react-router';
import { TeamGroupCreateDialog } from '~/components/dialogs/team-group-create-dialog';
import { SettingsHeader } from '~/components/general/settings-header';
@@ -72,9 +70,7 @@ export default function TeamsSettingsGroupsPage() {
<TeamGroupsTable />
<AnimateGenericFadeInOut key={everyoneGroupQuery.isFetched ? 'true' : 'false'}>
{everyoneGroupQuery.isFetched && (
<TeamInheritMemberAlert memberAccessTeamGroup={memberAccessTeamGroup} />
)}
{everyoneGroupQuery.isFetched && <TeamInheritMemberAlert memberAccessTeamGroup={memberAccessTeamGroup} />}
</AnimateGenericFadeInOut>
</div>
);
@@ -1,10 +1,8 @@
import { useEffect, useState } from 'react';
import { useLingui } from '@lingui/react/macro';
import { useLocation, useSearchParams } from 'react-router';
import { useDebouncedValue } from '@documenso/lib/client-only/hooks/use-debounced-value';
import { Input } from '@documenso/ui/primitives/input';
import { useLingui } from '@lingui/react/macro';
import { useEffect, useState } from 'react';
import { useLocation, useSearchParams } from 'react-router';
import { TeamMemberCreateDialog } from '~/components/dialogs/team-member-create-dialog';
import { SettingsHeader } from '~/components/general/settings-header';
@@ -1,10 +1,3 @@
import { useEffect, useMemo, useState } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react/macro';
import { Trans } from '@lingui/react/macro';
import { type TemplateDirectLink, TemplateType } from '@prisma/client';
import { getSession } from '@documenso/auth/server/lib/utils/get-session';
import { useSession } from '@documenso/lib/client-only/providers/session';
import { getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
@@ -16,6 +9,10 @@ import { Button } from '@documenso/ui/primitives/button';
import { Switch } from '@documenso/ui/primitives/switch';
import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { msg } from '@lingui/core/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { type TemplateDirectLink, TemplateType } from '@prisma/client';
import { useEffect, useMemo, useState } from 'react';
import { ManagePublicTemplateDialog } from '~/components/dialogs/public-profile-template-manage-dialog';
import type { TPublicProfileFormSchema } from '~/components/forms/public-profile-form';
@@ -72,20 +69,16 @@ export default function PublicProfilePage({ loaderData }: Route.ComponentProps)
perPage: 100,
});
const { mutateAsync: updateTeam, isPending: isUpdatingTeamProfile } =
trpc.team.update.useMutation({
onSuccess: async () => {
await refreshSession();
},
});
const { mutateAsync: updateTeam, isPending: isUpdatingTeamProfile } = trpc.team.update.useMutation({
onSuccess: async () => {
await refreshSession();
},
});
const isUpdating = isUpdatingTeamProfile;
const enabledPrivateDirectTemplates = useMemo(
() =>
(data?.data ?? []).filter(
(template): template is DirectTemplate => template.directLink?.enabled === true,
),
() => (data?.data ?? []).filter((template): template is DirectTemplate => template.directLink?.enabled === true),
[data],
);
@@ -143,13 +136,10 @@ export default function PublicProfilePage({ loaderData }: Route.ComponentProps)
<Tooltip open={isTooltipOpen} onOpenChange={setIsTooltipOpen}>
<TooltipTrigger asChild>
<div
className={cn(
'flex flex-row items-center justify-center space-x-2 text-xs text-muted-foreground/50',
{
'[&>*:first-child]:text-muted-foreground': !isPublicProfileVisible,
'[&>*:last-child]:text-muted-foreground': isPublicProfileVisible,
},
)}
className={cn('flex flex-row items-center justify-center space-x-2 text-muted-foreground/50 text-xs', {
'[&>*:first-child]:text-muted-foreground': !isPublicProfileVisible,
'[&>*:last-child]:text-muted-foreground': isPublicProfileVisible,
})}
>
<span>
<Trans>Hide</Trans>
@@ -208,9 +198,7 @@ export default function PublicProfilePage({ loaderData }: Route.ComponentProps)
directTemplates={enabledPrivateDirectTemplates}
trigger={
<Button variant="outline">
<Trans context="Action button to link template to public profile">
Link template
</Trans>
<Trans context="Action button to link template to public profile">Link template</Trans>
</Button>
}
/>
@@ -1,14 +1,12 @@
import { trpc } from '@documenso/trpc/react';
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
import { Button } from '@documenso/ui/primitives/button';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import { TeamMemberRole } from '@prisma/client';
import { DateTime } from 'luxon';
import { trpc } from '@documenso/trpc/react';
import { Alert, AlertDescription } from '@documenso/ui/primitives/alert';
import { AlertTitle } from '@documenso/ui/primitives/alert';
import { Button } from '@documenso/ui/primitives/button';
import TokenDeleteDialog from '~/components/dialogs/token-delete-dialog';
import { ApiTokenForm } from '~/components/forms/token';
import { SettingsHeader } from '~/components/general/settings-header';
@@ -37,6 +35,7 @@ export default function ApiTokensPage() {
className="text-primary underline"
href={'https://docs.documenso.com/developers/public-api'}
target="_blank"
rel="noopener"
>
Documentation
</a>{' '}
@@ -46,10 +45,7 @@ export default function ApiTokensPage() {
/>
{team && team?.currentTeamRole !== TeamMemberRole.ADMIN ? (
<Alert
className="flex flex-col items-center justify-between gap-4 p-6 md:flex-row"
variant="warning"
>
<Alert className="flex flex-col items-center justify-between gap-4 p-6 md:flex-row" variant="warning">
<div>
<AlertTitle>
<Trans>Unauthorized</Trans>
@@ -63,15 +59,15 @@ export default function ApiTokensPage() {
<>
<ApiTokenForm className="max-w-xl" tokens={tokens} />
<hr className="mb-4 mt-8" />
<hr className="mt-8 mb-4" />
<h4 className="text-xl font-medium">
<h4 className="font-medium text-xl">
<Trans>Your existing tokens</Trans>
</h4>
{tokens && tokens.length === 0 && (
<div className="mb-4">
<p className="mt-2 text-sm italic text-muted-foreground">
<p className="mt-2 text-muted-foreground text-sm italic">
<Trans>Your tokens will be shown here once you create them.</Trans>
</p>
</div>
@@ -85,19 +81,15 @@ export default function ApiTokensPage() {
<div>
<h5 className="text-base">{token.name}</h5>
<p className="mt-2 text-xs text-muted-foreground">
<Trans>
Created on {i18n.date(token.createdAt, DateTime.DATETIME_FULL)}
</Trans>
<p className="mt-2 text-muted-foreground text-xs">
<Trans>Created on {i18n.date(token.createdAt, DateTime.DATETIME_FULL)}</Trans>
</p>
{token.expires ? (
<p className="mt-1 text-xs text-muted-foreground">
<Trans>
Expires on {i18n.date(token.expires, DateTime.DATETIME_FULL)}
</Trans>
<p className="mt-1 text-muted-foreground text-xs">
<Trans>Expires on {i18n.date(token.expires, DateTime.DATETIME_FULL)}</Trans>
</p>
) : (
<p className="mt-1 text-xs text-muted-foreground">
<p className="mt-1 text-muted-foreground text-xs">
<Trans>Token doesn't have an expiration date</Trans>
</p>
)}
@@ -1,22 +1,3 @@
import { useMemo, useState } from 'react';
import { useEffect } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react/macro';
import { Trans } from '@lingui/react/macro';
import { WebhookCallStatus, WebhookTriggerEvents } from '@prisma/client';
import {
CheckCircle2Icon,
ChevronRightIcon,
PencilIcon,
TerminalIcon,
XCircleIcon,
} from 'lucide-react';
import { useNavigate } from 'react-router';
import { useLocation, useSearchParams } from 'react-router';
import { Link } from 'react-router';
import { z } from 'zod';
import { useDebouncedValue } from '@documenso/lib/client-only/hooks/use-debounced-value';
import { useIsMounted } from '@documenso/lib/client-only/hooks/use-is-mounted';
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
@@ -35,6 +16,13 @@ import { SpinnerBox } from '@documenso/ui/primitives/spinner';
import { TableCell } from '@documenso/ui/primitives/table';
import { Tabs, TabsList, TabsTrigger } from '@documenso/ui/primitives/tabs';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { msg } from '@lingui/core/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { WebhookCallStatus, WebhookTriggerEvents } from '@prisma/client';
import { CheckCircle2Icon, ChevronRightIcon, PencilIcon, TerminalIcon, XCircleIcon } from 'lucide-react';
import { useEffect, useMemo, useState } from 'react';
import { Link, useLocation, useNavigate, useSearchParams } from 'react-router';
import { z } from 'zod';
import { WebhookEditDialog } from '~/components/dialogs/webhook-edit-dialog';
import { WebhookTestDialog } from '~/components/dialogs/webhook-test-dialog';
@@ -71,9 +59,7 @@ export default function WebhookPage({ params }: Route.ComponentProps) {
const debouncedSearchQuery = useDebouncedValue(searchQuery, 500);
const parsedSearchParams = WebhookSearchParamsSchema.parse(
Object.fromEntries(searchParams ?? []),
);
const parsedSearchParams = WebhookSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
const { data: webhook, isLoading } = trpc.webhook.getWebhookById.useQuery(
{
@@ -151,10 +137,8 @@ export default function WebhookPage({ params }: Route.ComponentProps) {
accessorKey: 'event',
cell: ({ row }) => (
<div>
<p className="text-sm font-semibold text-foreground">
{toFriendlyWebhookEventName(row.original.event)}
</p>
<p className="text-xs text-muted-foreground">{row.original.id}</p>
<p className="font-semibold text-foreground text-sm">{toFriendlyWebhookEventName(row.original.event)}</p>
<p className="text-muted-foreground text-xs">{row.original.id}</p>
</div>
),
},
@@ -170,7 +154,7 @@ export default function WebhookPage({ params }: Route.ComponentProps) {
})}
</p>
<div className="data-state-selected:block opacity-0 transition-opacity group-hover:opacity-100">
<div className="opacity-0 transition-opacity group-hover:opacity-100 data-state-selected:block">
<ChevronRightIcon className="h-4 w-4" />
</div>
</div>
@@ -330,9 +314,7 @@ export default function WebhookPage({ params }: Route.ComponentProps) {
}}
>
{(table) =>
results.totalPages > 1 && (
<DataTablePagination additionalInformation="VisibleCount" table={table} />
)
results.totalPages > 1 && <DataTablePagination additionalInformation="VisibleCount" table={table} />
}
</DataTable>
</div>
@@ -1,12 +1,3 @@
import { useMemo } from 'react';
import { msg } from '@lingui/core/macro';
import { Plural, useLingui } from '@lingui/react/macro';
import { Trans } from '@lingui/react/macro';
import type { Webhook } from '@prisma/client';
import { EditIcon, Loader, MoreHorizontalIcon, ScrollTextIcon, Trash2Icon } from 'lucide-react';
import { Link } from 'react-router';
import { toFriendlyWebhookEventName } from '@documenso/lib/universal/webhook/to-friendly-webhook-event-name';
import { trpc } from '@documenso/trpc/react';
import { Badge } from '@documenso/ui/primitives/badge';
@@ -20,6 +11,12 @@ import {
} from '@documenso/ui/primitives/dropdown-menu';
import { Skeleton } from '@documenso/ui/primitives/skeleton';
import { TableCell } from '@documenso/ui/primitives/table';
import { msg } from '@lingui/core/macro';
import { Plural, Trans, useLingui } from '@lingui/react/macro';
import type { Webhook } from '@prisma/client';
import { EditIcon, Loader, MoreHorizontalIcon, ScrollTextIcon, Trash2Icon } from 'lucide-react';
import { useMemo } from 'react';
import { Link } from 'react-router';
import { WebhookCreateDialog } from '~/components/dialogs/webhook-create-dialog';
import { WebhookDeleteDialog } from '~/components/dialogs/webhook-delete-dialog';
@@ -52,11 +49,8 @@ export default function WebhookPage() {
header: t`Webhook`,
cell: ({ row }) => (
<Link to={`/t/${team.url}/settings/webhooks/${row.original.id}`}>
<p className="text-xs text-muted-foreground">{row.original.id}</p>
<p
className="max-w-sm truncate text-xs font-semibold text-foreground"
title={row.original.webhookUrl}
>
<p className="text-muted-foreground text-xs">{row.original.id}</p>
<p className="max-w-sm truncate font-semibold text-foreground text-xs" title={row.original.webhookUrl}>
{row.original.webhookUrl}
</p>
</Link>
@@ -75,9 +69,7 @@ export default function WebhookPage() {
cell: ({ row }) => (
<p
className="text-foreground"
title={row.original.eventTriggers
.map((event) => toFriendlyWebhookEventName(event))
.join(', ')}
title={row.original.eventTriggers.map((event) => toFriendlyWebhookEventName(event)).join(', ')}
>
<Plural value={row.original.eventTriggers.length} one="# Event" other="# Events" />
</p>
@@ -120,9 +112,7 @@ export default function WebhookPage() {
emptyState={
<div className="flex h-60 flex-col items-center justify-center gap-y-4 text-muted-foreground/60">
<p>
<Trans>
You have no webhooks yet. Your webhooks will be shown here once you create them.
</Trans>
<Trans>You have no webhooks yet. Your webhooks will be shown here once you create them.</Trans>
</p>
</div>
}
@@ -1,9 +1,3 @@
import { msg } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { DocumentSigningOrder, SigningStatus } from '@prisma/client';
import { ChevronLeft, LucideEdit } from 'lucide-react';
import { Link, useNavigate } from 'react-router';
import { EnvelopeRenderProvider } from '@documenso/lib/client-only/providers/envelope-render-provider';
import { useSession } from '@documenso/lib/client-only/providers/session';
import { PDF_VIEWER_ERROR_MESSAGES } from '@documenso/lib/constants/pdf-viewer-i18n';
@@ -16,6 +10,11 @@ import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import { Card, CardContent } from '@documenso/ui/primitives/card';
import { Spinner } from '@documenso/ui/primitives/spinner';
import { msg } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { DocumentSigningOrder, SigningStatus } from '@prisma/client';
import { ChevronLeft, LucideEdit } from 'lucide-react';
import { Link, useNavigate } from 'react-router';
import { TemplateBulkSendDialog } from '~/components/dialogs/template-bulk-send-dialog';
import { TemplateDirectLinkDialog } from '~/components/dialogs/template-direct-link-dialog';
@@ -53,8 +52,7 @@ export default function TemplatePage({ params }: Route.ComponentProps) {
const envelope = teamTemplateQuery.data ?? orgTemplateQuery.data;
const isLoadingEnvelope =
teamTemplateQuery.isLoading ||
(teamTemplateQuery.isError && !orgTemplateQuery.isError && !orgTemplateQuery.data);
teamTemplateQuery.isLoading || (teamTemplateQuery.isError && !orgTemplateQuery.isError && !orgTemplateQuery.data);
const isErrorEnvelope = teamTemplateQuery.isError && orgTemplateQuery.isError;
if (isLoadingEnvelope) {
@@ -94,9 +92,7 @@ export default function TemplatePage({ params }: Route.ComponentProps) {
// Remap to fit the DocumentReadOnlyFields component.
const readOnlyFields = envelope.fields.map((field) => {
const recipient = envelope.recipients.find(
(recipient) => recipient.id === field.recipientId,
) || {
const recipient = envelope.recipients.find((recipient) => recipient.id === field.recipientId) || {
name: '',
email: '',
signingStatus: SigningStatus.NOT_SIGNED,
@@ -122,10 +118,7 @@ export default function TemplatePage({ params }: Route.ComponentProps) {
return (
<div className="mx-auto -mt-4 w-full max-w-screen-xl px-4 md:px-8">
<div className="flex flex-row justify-between">
<Link
to={templateRootPath}
className="flex items-center text-documenso-700 hover:opacity-80"
>
<Link to={templateRootPath} className="flex items-center text-documenso-700 hover:opacity-80">
<ChevronLeft className="mr-2 inline-block h-5 w-5" />
<Trans>Templates</Trans>
</Link>
@@ -157,16 +150,12 @@ export default function TemplatePage({ params }: Route.ComponentProps) {
</div>
<div className="min-w-0">
<h1 className="mt-4 block text-2xl font-semibold md:text-3xl" title={envelope.title}>
<h1 className="mt-4 block font-semibold text-2xl md:text-3xl" title={envelope.title}>
{envelope.title}
</h1>
<div className="mt-2.5 flex items-center">
<TemplateType
inheritColor
className="text-muted-foreground"
type={envelope.templateType}
/>
<TemplateType inheritColor className="text-muted-foreground" type={envelope.templateType} />
{envelope.directLink?.token && (
<TemplateDirectLinkBadge
@@ -192,9 +181,7 @@ export default function TemplatePage({ params }: Route.ComponentProps) {
showRecipientTooltip: true,
}}
>
{isMultiEnvelopeItem && (
<EnvelopeRendererFileSelector fields={envelope.fields} className="mb-4 p-0" />
)}
{isMultiEnvelopeItem && <EnvelopeRendererFileSelector fields={envelope.fields} className="mb-4 p-0" />}
<Card className="rounded-xl before:rounded-xl" gradient>
<CardContent className="p-2">
@@ -208,10 +195,7 @@ export default function TemplatePage({ params }: Route.ComponentProps) {
</EnvelopeRenderProvider>
</div>
) : (
<Card
className="relative col-span-12 rounded-xl before:rounded-xl lg:col-span-6 xl:col-span-7"
gradient
>
<Card className="relative col-span-12 rounded-xl before:rounded-xl lg:col-span-6 xl:col-span-7" gradient>
<CardContent className="p-2">
<DocumentReadOnlyFields
fields={readOnlyFields}
@@ -238,13 +222,11 @@ export default function TemplatePage({ params }: Route.ComponentProps) {
</Card>
)}
<div
className={cn('col-span-12 lg:col-span-6 xl:col-span-5', isMultiEnvelopeItem && 'mt-20')}
>
<div className={cn('col-span-12 lg:col-span-6 xl:col-span-5', isMultiEnvelopeItem && 'mt-20')}>
<div className="space-y-6">
<section className="flex flex-col rounded-xl border border-border bg-widget pb-4 pt-6">
<section className="flex flex-col rounded-xl border border-border bg-widget pt-6 pb-4">
<div className="flex flex-row items-center justify-between px-4">
<h3 className="text-2xl font-semibold text-foreground">
<h3 className="font-semibold text-2xl text-foreground">
<Trans>Template</Trans>
</h3>
@@ -262,7 +244,7 @@ export default function TemplatePage({ params }: Route.ComponentProps) {
</div>
</div>
<p className="mt-2 px-4 text-sm text-muted-foreground">
<p className="mt-2 px-4 text-muted-foreground text-sm">
{isOwnTeamTemplate ? (
<Trans>Manage and view template</Trans>
) : (
@@ -307,13 +289,11 @@ export default function TemplatePage({ params }: Route.ComponentProps) {
</div>
<div className="mt-16" id="documents">
<h1 className="mb-4 text-2xl font-bold">
<h1 className="mb-4 font-bold text-2xl">
<Trans>Documents created from template</Trans>
</h1>
<TemplatePageViewDocumentsTable
templateId={mapSecondaryIdToTemplateId(envelope.secondaryId)}
/>
<TemplatePageViewDocumentsTable templateId={mapSecondaryIdToTemplateId(envelope.secondaryId)} />
</div>
</div>
);
@@ -1,21 +1,14 @@
import { msg } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { EnvelopeType } from '@prisma/client';
import { ChevronLeftIcon } from 'lucide-react';
import {
Link,
Outlet,
type ShouldRevalidateFunctionArgs,
isRouteErrorResponse,
redirect,
} from 'react-router';
import { getSession } from '@documenso/auth/server/lib/utils/get-session';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { getEnvelopeById } from '@documenso/lib/server-only/envelope/get-envelope-by-id';
import { getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
import { getOrganisationTemplateById } from '@documenso/lib/server-only/template/get-organisation-template-by-id';
import { Button } from '@documenso/ui/primitives/button';
import { msg } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { EnvelopeType } from '@prisma/client';
import { ChevronLeftIcon } from 'lucide-react';
import { isRouteErrorResponse, Link, Outlet, redirect, type ShouldRevalidateFunctionArgs } from 'react-router';
import { GenericErrorLayout } from '~/components/general/generic-error-layout';
@@ -1,11 +1,10 @@
import { Trans } from '@lingui/react/macro';
import { ChevronLeft } from 'lucide-react';
import { Link, redirect } from 'react-router';
import { getSession } from '@documenso/auth/server/lib/utils/get-session';
import { getTeamByUrl } from '@documenso/lib/server-only/team/get-team';
import { getTemplateById } from '@documenso/lib/server-only/template/get-template-by-id';
import { formatTemplatesPath } from '@documenso/lib/utils/teams';
import { Trans } from '@lingui/react/macro';
import { ChevronLeft } from 'lucide-react';
import { Link, redirect } from 'react-router';
import { TemplateDirectLinkDialog } from '~/components/dialogs/template-direct-link-dialog';
import { DocumentAttachmentsPopover } from '~/components/general/document/document-attachments-popover';
@@ -68,7 +67,7 @@ export default function TemplateEditPage() {
</Link>
<h1
className="mt-4 block max-w-[20rem] truncate text-2xl font-semibold md:max-w-[30rem] md:text-3xl"
className="mt-4 block max-w-[20rem] truncate font-semibold text-2xl md:max-w-[30rem] md:text-3xl"
title={template.title}
>
{template.title}
@@ -104,11 +103,7 @@ export default function TemplateEditPage() {
</div>
</div>
<TemplateEditForm
className="mt-6"
initialTemplate={template}
templateRootPath={templateRootPath}
/>
<TemplateEditForm className="mt-6" initialTemplate={template} templateRootPath={templateRootPath} />
</div>
);
}
@@ -1,12 +1,3 @@
import { useMemo, useState } from 'react';
import { msg } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { EnvelopeType, OrganisationType } from '@prisma/client';
import { Bird } from 'lucide-react';
import { parseAsStringLiteral, useQueryState } from 'nuqs';
import { useParams, useSearchParams } from 'react-router';
import { useSessionStorage } from '@documenso/lib/client-only/hooks/use-session-storage';
import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
import { FolderType } from '@documenso/lib/types/folder-type';
@@ -16,6 +7,13 @@ import { trpc } from '@documenso/trpc/react';
import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/avatar';
import type { RowSelectionState } from '@documenso/ui/primitives/data-table';
import { Tabs, TabsList, TabsTrigger } from '@documenso/ui/primitives/tabs';
import { msg } from '@lingui/core/macro';
import { Trans } from '@lingui/react/macro';
import { EnvelopeType, OrganisationType } from '@prisma/client';
import { Bird } from 'lucide-react';
import { parseAsStringLiteral, useQueryState } from 'nuqs';
import { useMemo, useState } from 'react';
import { useParams, useSearchParams } from 'react-router';
import { EnvelopesBulkDeleteDialog } from '~/components/dialogs/envelopes-bulk-delete-dialog';
import { EnvelopesBulkMoveDialog } from '~/components/dialogs/envelopes-bulk-move-dialog';
@@ -44,18 +42,12 @@ export default function TemplatesPage() {
const page = Number(searchParams.get('page')) || 1;
const perPage = Number(searchParams.get('perPage')) || 10;
const [view, setView] = useQueryState(
'view',
parseAsStringLiteral(TEMPLATE_VIEWS).withDefault('team'),
);
const [view, setView] = useQueryState('view', parseAsStringLiteral(TEMPLATE_VIEWS).withDefault('team'));
const isOrgView = view === 'organisation';
const showOrgTab = organisation.type !== OrganisationType.PERSONAL;
const [rowSelection, setRowSelection] = useSessionStorage<RowSelectionState>(
'templates-bulk-selection',
{},
);
const [rowSelection, setRowSelection] = useSessionStorage<RowSelectionState>('templates-bulk-selection', {});
const [isBulkMoveDialogOpen, setIsBulkMoveDialogOpen] = useState(false);
const [isBulkDeleteDialogOpen, setIsBulkDeleteDialogOpen] = useState(false);
@@ -104,14 +96,12 @@ export default function TemplatesPage() {
<div className="mt-8">
<div className="flex flex-row items-center">
<Avatar className="mr-3 h-12 w-12 border-2 border-solid border-white dark:border-border">
<Avatar className="mr-3 h-12 w-12 border-2 border-white border-solid dark:border-border">
{team.avatarImageId && <AvatarImage src={formatAvatarUrl(team.avatarImageId)} />}
<AvatarFallback className="text-xs text-muted-foreground">
{team.name.slice(0, 1)}
</AvatarFallback>
<AvatarFallback className="text-muted-foreground text-xs">{team.name.slice(0, 1)}</AvatarFallback>
</Avatar>
<h1 className="truncate text-2xl font-semibold md:text-3xl">
<h1 className="truncate font-semibold text-2xl md:text-3xl">
<Trans>Templates</Trans>
</h1>
</div>
@@ -145,7 +135,7 @@ export default function TemplatesPage() {
<Bird className="h-12 w-12" strokeWidth={1.5} />
<div className="text-center">
<h3 className="text-lg font-semibold">
<h3 className="font-semibold text-lg">
<Trans>We're all empty</Trans>
</h3>
@@ -153,10 +143,7 @@ export default function TemplatesPage() {
{isOrgView ? (
<Trans>No organisation templates are shared with your team yet.</Trans>
) : (
<Trans>
You have not yet created any templates. To create a template please upload
one.
</Trans>
<Trans>You have not yet created any templates. To create a template please upload one.</Trans>
)}
</p>
</div>
@@ -1,15 +1,13 @@
import { useState } from 'react';
import { msg } from '@lingui/core/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { FolderIcon, HomeIcon, Loader2, SearchIcon } from 'lucide-react';
import { Link, useSearchParams } from 'react-router';
import { FolderType } from '@documenso/lib/types/folder-type';
import { formatTemplatesPath } from '@documenso/lib/utils/teams';
import { trpc } from '@documenso/trpc/react';
import { type TFolderWithSubfolders } from '@documenso/trpc/server/folder-router/schema';
import type { TFolderWithSubfolders } from '@documenso/trpc/server/folder-router/schema';
import { Input } from '@documenso/ui/primitives/input';
import { msg } from '@lingui/core/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { FolderIcon, HomeIcon, Loader2, SearchIcon } from 'lucide-react';
import { useState } from 'react';
import { Link, useSearchParams } from 'react-router';
import { FolderCreateDialog } from '~/components/dialogs/folder-create-dialog';
import { FolderDeleteDialog } from '~/components/dialogs/folder-delete-dialog';
@@ -64,11 +62,8 @@ export default function TemplatesFoldersPage() {
return (
<div className="mx-auto w-full max-w-screen-xl px-4 md:px-8">
<div className="flex w-full items-center justify-between">
<div className="flex flex-1 items-center text-sm font-medium text-muted-foreground">
<Link
to={formatTemplatesPath(team.url)}
className="flex items-center hover:text-muted-foreground/80"
>
<div className="flex flex-1 items-center font-medium text-muted-foreground text-sm">
<Link to={formatTemplatesPath(team.url)} className="flex items-center hover:text-muted-foreground/80">
<HomeIcon className="mr-2 h-4 w-4" />
<Trans>Home</Trans>
</Link>
@@ -76,10 +71,7 @@ export default function TemplatesFoldersPage() {
{foldersData?.breadcrumbs.map((folder) => (
<div key={folder.id} className="flex items-center">
<span className="px-3">/</span>
<Link
to={formatBreadcrumbPath(folder.id)}
className="flex items-center hover:text-muted-foreground/80"
>
<Link to={formatBreadcrumbPath(folder.id)} className="flex items-center hover:text-muted-foreground/80">
<FolderIcon className="mr-2 h-4 w-4" />
<span>{folder.name}</span>
</Link>
@@ -93,7 +85,7 @@ export default function TemplatesFoldersPage() {
</div>
<div className="relative w-full max-w-md py-6">
<SearchIcon className="absolute left-2 top-9 h-4 w-4 text-muted-foreground" />
<SearchIcon className="absolute top-9 left-2 h-4 w-4 text-muted-foreground" />
<Input
placeholder={t`Search folders...`}
value={searchTerm}
@@ -102,7 +94,7 @@ export default function TemplatesFoldersPage() {
/>
</div>
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl">
<h1 className="mt-4 truncate font-semibold text-2xl md:text-3xl">
<Trans>All Folders</Trans>
</h1>