fix: migrate certificate generation (#2251)

Generate certificates and audit logs using Konva instead of browserless.

This should:
- Reduce the changes of generations failing
- Improve sealing speed
This commit is contained in:
David Nguyen
2026-01-06 14:26:19 +11:00
committed by GitHub
parent c1217c5a58
commit c2ea5e5859
36 changed files with 2008 additions and 233 deletions
+4 -4
View File
@@ -212,12 +212,12 @@ export const ApiTokenForm = ({ className, tokens }: ApiTokenFormProps) => {
/>
<div>
<FormLabel className="text-muted-foreground mt-2">
<FormLabel className="mt-2 text-muted-foreground">
<Trans>Never expire</Trans>
</FormLabel>
<div className="block md:py-1.5">
<Switch
className="bg-background mt-2"
className="mt-2 bg-background"
checked={noExpirationDate}
onCheckedChange={setNoExpirationDate}
/>
@@ -254,14 +254,14 @@ export const ApiTokenForm = ({ className, tokens }: ApiTokenFormProps) => {
>
<Card gradient>
<CardContent className="p-4">
<p className="text-muted-foreground mt-2 text-sm">
<p className="mt-2 text-sm text-muted-foreground">
<Trans>
Your token was created successfully! Make sure to copy it because you won't be
able to see it again!
</Trans>
</p>
<p className="bg-muted-foreground/10 my-4 rounded-md px-2.5 py-1 font-mono text-sm">
<p className="my-4 rounded-md bg-muted-foreground/10 px-2.5 py-1 font-mono text-sm">
{newlyCreatedToken.token}
</p>
@@ -35,7 +35,7 @@ export const DocumentSigningMobileWidget = () => {
return (
<div className="pointer-events-none fixed bottom-0 left-0 right-0 z-50 flex justify-center px-2 pb-2 sm:px-4 sm:pb-6">
<div className="pointer-events-auto w-full max-w-[760px]">
<div className="bg-card border-border overflow-hidden rounded-xl border shadow-2xl">
<div className="overflow-hidden rounded-xl border border-border bg-card shadow-2xl">
{/* Main Header Bar */}
<div className="flex items-center justify-between gap-4 p-4">
<div className="flex-1">
@@ -48,15 +48,15 @@ export const DocumentSigningMobileWidget = () => {
aria-label={isExpanded ? 'Collapse' : 'Expand'}
>
{isExpanded ? (
<LucideChevronDown className="text-muted-foreground h-5 w-5 flex-shrink-0" />
<LucideChevronDown className="h-5 w-5 flex-shrink-0 text-muted-foreground" />
) : (
<LucideChevronUp className="text-muted-foreground h-5 w-5 flex-shrink-0" />
<LucideChevronUp className="h-5 w-5 flex-shrink-0 text-muted-foreground" />
)}
</Button>
)}
<div>
<h2 className="text-foreground text-lg font-semibold">
<h2 className="text-lg font-semibold text-foreground">
{match(recipient.role)
.with(RecipientRole.VIEWER, () => <Trans>View Document</Trans>)
.with(RecipientRole.SIGNER, () => <Trans>Sign Document</Trans>)
@@ -65,7 +65,7 @@ export const DocumentSigningMobileWidget = () => {
.otherwise(() => null)}
</h2>
<p className="text-muted-foreground -mt-0.5 text-sm">
<p className="-mt-0.5 text-sm text-muted-foreground">
{recipientFieldsRemaining.length === 0 ? (
match(recipient.role)
.with(RecipientRole.VIEWER, () => (
@@ -102,11 +102,11 @@ export const DocumentSigningMobileWidget = () => {
{recipient.role !== RecipientRole.VIEWER &&
recipient.role !== RecipientRole.ASSISTANT && (
<div className="px-4 pb-3">
<div className="bg-muted relative h-[4px] rounded-md">
<div className="relative h-[4px] rounded-md bg-muted">
<motion.div
layout="size"
layoutId="document-signing-mobile-widget-progress-bar"
className="bg-primary absolute inset-y-0 left-0"
className="absolute inset-y-0 left-0 bg-primary"
style={{
width: `${100 - (100 / requiredRecipientFields.length) * (recipientFieldsRemaining.length ?? 0)}%`,
}}
@@ -117,11 +117,11 @@ export const DocumentSigningMobileWidget = () => {
{/* Expandable Content */}
{isExpanded && (
<div className="border-border animate-in slide-in-from-bottom-2 border-t p-4 duration-200">
<div className="border-t border-border p-4 duration-200 animate-in slide-in-from-bottom-2">
<EnvelopeSignerForm />
{!hidePoweredBy && (
<div className="bg-primary text-primary-foreground mt-2 inline-block rounded px-2 py-1 text-xs font-medium opacity-60 hover:opacity-100 lg:hidden">
<div className="mt-2 inline-block rounded bg-primary px-2 py-1 text-xs font-medium text-primary-foreground opacity-60 hover:opacity-100 lg:hidden">
<span>Powered by</span>
<BrandingLogo className="ml-2 inline-block h-[14px]" />
</div>
@@ -22,7 +22,7 @@ export const DocumentPageViewRecentActivity = ({
documentId,
userId,
}: DocumentPageViewRecentActivityProps) => {
const { _ } = useLingui();
const { _, i18n } = useLingui();
const {
data,
@@ -48,9 +48,9 @@ export const DocumentPageViewRecentActivity = ({
const documentAuditLogs = useMemo(() => (data?.pages ?? []).flatMap((page) => page.data), [data]);
return (
<section className="dark:bg-background border-border bg-widget flex flex-col rounded-xl border">
<section className="flex flex-col rounded-xl border border-border bg-widget dark:bg-background">
<div className="flex flex-row items-center justify-between border-b px-4 py-3">
<h1 className="text-foreground font-medium">
<h1 className="font-medium text-foreground">
<Trans>Recent activity</Trans>
</h1>
@@ -59,18 +59,18 @@ export const DocumentPageViewRecentActivity = ({
{isLoading && (
<div className="flex h-full items-center justify-center py-16">
<Loader className="text-muted-foreground h-6 w-6 animate-spin" />
<Loader className="h-6 w-6 animate-spin text-muted-foreground" />
</div>
)}
{isLoadingError && (
<div className="flex h-full flex-col items-center justify-center py-16">
<p className="text-foreground/80 text-sm">
<p className="text-sm text-foreground/80">
<Trans>Unable to load document history</Trans>
</p>
<button
onClick={async () => refetch()}
className="text-foreground/70 hover:text-muted-foreground mt-2 text-sm"
className="mt-2 text-sm text-foreground/70 hover:text-muted-foreground"
>
<Trans>Click here to retry</Trans>
</button>
@@ -83,16 +83,16 @@ export const DocumentPageViewRecentActivity = ({
{hasNextPage && (
<li className="relative flex gap-x-4">
<div className="absolute -bottom-6 left-0 top-0 flex w-6 justify-center">
<div className="bg-border w-px" />
<div className="w-px bg-border" />
</div>
<div className="bg-widget relative flex h-6 w-6 flex-none items-center justify-center">
<div className="bg-widget h-1.5 w-1.5 rounded-full ring-1 ring-gray-300 dark:ring-neutral-600" />
<div className="relative flex h-6 w-6 flex-none items-center justify-center bg-widget">
<div className="h-1.5 w-1.5 rounded-full bg-widget ring-1 ring-gray-300 dark:ring-neutral-600" />
</div>
<button
onClick={async () => fetchNextPage()}
className="text-foreground/70 hover:text-muted-foreground text-xs"
className="text-xs text-foreground/70 hover:text-muted-foreground"
>
{isFetchingNextPage ? _(msg`Loading...`) : _(msg`Load older activity`)}
</button>
@@ -101,7 +101,7 @@ export const DocumentPageViewRecentActivity = ({
{documentAuditLogs.length === 0 && (
<div className="flex items-center justify-center py-4">
<p className="text-muted-foreground/70 text-sm">
<p className="text-sm text-muted-foreground/70">
<Trans>No recent activity</Trans>
</p>
</div>
@@ -115,44 +115,44 @@ export const DocumentPageViewRecentActivity = ({
'absolute left-0 top-0 flex w-6 justify-center',
)}
>
<div className="bg-border w-px" />
<div className="w-px bg-border" />
</div>
<div className="bg-widget text-foreground/40 relative flex h-6 w-6 flex-none items-center justify-center">
<div className="relative flex h-6 w-6 flex-none items-center justify-center bg-widget text-foreground/40">
{match(auditLog.type)
.with(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED, () => (
<div className="bg-widget rounded-full border border-gray-300 p-1 dark:border-neutral-600">
<div className="rounded-full border border-gray-300 bg-widget p-1 dark:border-neutral-600">
<CheckCheckIcon className="h-3 w-3" aria-hidden="true" />
</div>
))
.with(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED, () => (
<div className="bg-widget rounded-full border border-gray-300 p-1 dark:border-neutral-600">
<div className="rounded-full border border-gray-300 bg-widget p-1 dark:border-neutral-600">
<CheckIcon className="h-3 w-3" aria-hidden="true" />
</div>
))
.with(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_REJECTED, () => (
<div className="bg-widget rounded-full border border-gray-300 p-1 dark:border-neutral-600">
<div className="rounded-full border border-gray-300 bg-widget p-1 dark:border-neutral-600">
<AlertTriangle className="h-3 w-3" aria-hidden="true" />
</div>
))
.with(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED, () => (
<div className="bg-widget rounded-full border border-gray-300 p-1 dark:border-neutral-600">
<div className="rounded-full border border-gray-300 bg-widget p-1 dark:border-neutral-600">
<MailOpen className="h-3 w-3" aria-hidden="true" />
</div>
))
.otherwise(() => (
<div className="bg-widget h-1.5 w-1.5 rounded-full ring-1 ring-gray-300 dark:ring-neutral-600" />
<div className="h-1.5 w-1.5 rounded-full bg-widget ring-1 ring-gray-300 dark:ring-neutral-600" />
))}
</div>
<p
className="text-muted-foreground dark:text-muted-foreground/70 flex-auto truncate py-0.5 text-xs leading-5"
title={formatDocumentAuditLogAction(_, auditLog, userId).description}
className="flex-auto truncate py-0.5 text-xs leading-5 text-muted-foreground dark:text-muted-foreground/70"
title={formatDocumentAuditLogAction(i18n, auditLog, userId).description}
>
{formatDocumentAuditLogAction(_, auditLog, userId).description}
{formatDocumentAuditLogAction(i18n, auditLog, userId).description}
</p>
<time className="text-muted-foreground dark:text-muted-foreground/70 flex-none py-0.5 text-xs leading-5">
<time className="flex-none py-0.5 text-xs leading-5 text-muted-foreground dark:text-muted-foreground/70">
{DateTime.fromJSDate(auditLog.createdAt).toRelative({ style: 'short' })}
</time>
</li>
@@ -339,7 +339,7 @@ export const EnvelopeEditorSettingsDialog = ({
<DialogContent className="flex w-full !max-w-5xl flex-row gap-0 p-0">
{/* Sidebar. */}
<div className="bg-accent/20 flex w-80 flex-col border-r">
<div className="flex w-80 flex-col border-r bg-accent/20">
<DialogHeader className="p-6 pb-4">
<DialogTitle>Document Settings</DialogTitle>
</DialogHeader>
@@ -390,7 +390,7 @@ export const EnvelopeEditorSettingsDialog = ({
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-foreground max-w-md space-y-2 p-4">
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
<Trans>
Controls the language for the document, including the language
to be used for email notifications, and the final certificate
@@ -441,7 +441,7 @@ export const EnvelopeEditorSettingsDialog = ({
}))}
selectedValues={field.value}
onChange={field.onChange}
className="bg-background w-full"
className="w-full bg-background"
emptySelectionPlaceholder="Select signature types"
/>
</FormControl>
@@ -518,7 +518,7 @@ export const EnvelopeEditorSettingsDialog = ({
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-muted-foreground max-w-xs">
<TooltipContent className="max-w-xs text-muted-foreground">
<Trans>
Add an external ID to the document. This can be used to identify
the document in external systems.
@@ -548,7 +548,7 @@ export const EnvelopeEditorSettingsDialog = ({
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-muted-foreground max-w-xs">
<TooltipContent className="max-w-xs text-muted-foreground">
<Trans>
Add a URL to redirect the user to once the document is signed
</Trans>
@@ -576,7 +576,7 @@ export const EnvelopeEditorSettingsDialog = ({
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-foreground max-w-md space-y-2 p-4">
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
<h2>
<strong>
<Trans>Document Distribution Method</Trans>
@@ -735,14 +735,14 @@ export const EnvelopeEditorSettingsDialog = ({
<TooltipTrigger>
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="text-muted-foreground p-4">
<TooltipContent className="p-4 text-muted-foreground">
<DocumentSendEmailMessageHelper />
</TooltipContent>
</Tooltip>
</FormLabel>
<FormControl>
<Textarea className="bg-background h-16 resize-none" {...field} />
<Textarea className="h-16 resize-none bg-background" {...field} />
</FormControl>
<FormMessage />
@@ -95,7 +95,7 @@ export const DocumentLogsTable = ({ documentId, userId }: DocumentLogsTableProps
header: _(msg`Action`),
accessorKey: 'type',
cell: ({ row }) => (
<span>{formatDocumentAuditLogAction(_, row.original, userId).description}</span>
<span>{formatDocumentAuditLogAction(i18n, row.original, userId).description}</span>
),
},
{
@@ -65,7 +65,7 @@ const formatUserAgent = (userAgent: string | null | undefined, userAgentInfo: UA
};
export const InternalAuditLogTable = ({ logs }: AuditLogDataTableProps) => {
const { _ } = useLingui();
const { _, i18n } = useLingui();
const parser = new UAParser();
@@ -73,7 +73,7 @@ export const InternalAuditLogTable = ({ logs }: AuditLogDataTableProps) => {
<div className="space-y-4">
{logs.map((log, index) => {
parser.setUA(log.userAgent || '');
const formattedAction = formatDocumentAuditLogAction(_, log);
const formattedAction = formatDocumentAuditLogAction(i18n, log);
const userAgentInfo = parser.getResult();
return (
@@ -95,17 +95,17 @@ export const InternalAuditLogTable = ({ logs }: AuditLogDataTableProps) => {
/>
<div>
<div className="text-muted-foreground text-sm font-medium uppercase tracking-wide print:text-[8pt]">
<div className="text-sm font-medium uppercase tracking-wide text-muted-foreground print:text-[8pt]">
{log.type.replace(/_/g, ' ')}
</div>
<div className="text-foreground text-sm font-medium print:text-[8pt]">
<div className="text-sm font-medium text-foreground print:text-[8pt]">
{formattedAction.description}
</div>
</div>
</div>
<div className="text-muted-foreground text-sm print:text-[8pt]">
<div className="text-sm text-muted-foreground print:text-[8pt]">
{DateTime.fromJSDate(log.createdAt)
.setLocale(APP_I18N_OPTIONS.defaultLocale)
.toLocaleString(dateFormat)}
@@ -117,27 +117,27 @@ export const InternalAuditLogTable = ({ logs }: AuditLogDataTableProps) => {
{/* Details Section - Two column layout */}
<div className="grid grid-cols-2 gap-x-8 gap-y-2 text-xs print:text-[6pt]">
<div>
<div className="text-muted-foreground/70 font-medium uppercase tracking-wide">
<div className="font-medium uppercase tracking-wide text-muted-foreground/70">
{_(msg`User`)}
</div>
<div className="text-foreground mt-1 font-mono">{log.email || 'N/A'}</div>
<div className="mt-1 font-mono text-foreground">{log.email || 'N/A'}</div>
</div>
<div className="text-right">
<div className="text-muted-foreground/70 font-medium uppercase tracking-wide">
<div className="font-medium uppercase tracking-wide text-muted-foreground/70">
{_(msg`IP Address`)}
</div>
<div className="text-foreground mt-1 font-mono">{log.ipAddress || 'N/A'}</div>
<div className="mt-1 font-mono text-foreground">{log.ipAddress || 'N/A'}</div>
</div>
<div className="col-span-2">
<div className="text-muted-foreground/70 font-medium uppercase tracking-wide">
<div className="font-medium uppercase tracking-wide text-muted-foreground/70">
{_(msg`User Agent`)}
</div>
<div className="text-foreground mt-1">
<div className="mt-1 text-foreground">
{_(formatUserAgent(log.userAgent, userAgentInfo))}
</div>
</div>
@@ -40,8 +40,8 @@ export default function OrganisationSettingsTeamsPage() {
if (organisation.teams.length === 0) {
return (
<div className="flex flex-col items-center justify-center px-4 py-16">
<div className="bg-muted mb-6 flex h-20 w-20 items-center justify-center rounded-full">
<UsersIcon className="text-muted-foreground h-10 w-10" />
<div className="mb-6 flex h-20 w-20 items-center justify-center rounded-full bg-muted">
<UsersIcon className="h-10 w-10 text-muted-foreground" />
</div>
<h2 className="mb-2 text-xl font-semibold">
@@ -53,7 +53,7 @@ export default function OrganisationSettingsTeamsPage() {
organisation.currentOrganisationRole,
) ? (
<>
<p className="text-muted-foreground mb-8 max-w-md text-center text-sm">
<p className="mb-8 max-w-md text-center text-sm text-muted-foreground">
<Trans>
Teams help you organise your work and collaborate with others. Create your first
team to get started.
@@ -73,21 +73,21 @@ export default function OrganisationSettingsTeamsPage() {
<h3 className="mb-2 font-medium">
<Trans>What you can do with teams:</Trans>
</h3>
<ul className="text-muted-foreground space-y-2 text-sm">
<ul className="space-y-2 text-sm text-muted-foreground">
<li className="flex flex-row items-center gap-2">
<div className="bg-muted mt-0.5 flex h-5 w-5 items-center justify-center rounded-full font-bold">
<div className="mt-0.5 flex h-5 w-5 items-center justify-center rounded-full bg-muted font-bold">
<span className="text-xs">1</span>
</div>
<Trans>Organize your documents and templates</Trans>
</li>
<li className="flex flex-row items-center gap-2">
<div className="bg-muted mt-0.5 flex h-5 w-5 items-center justify-center rounded-full font-bold">
<div className="mt-0.5 flex h-5 w-5 items-center justify-center rounded-full bg-muted font-bold">
<span className="text-xs">2</span>
</div>
<Trans>Invite team members to collaborate</Trans>
</li>
<li className="flex flex-row items-center gap-2">
<div className="bg-muted mt-0.5 flex h-5 w-5 items-center justify-center rounded-full font-bold">
<div className="mt-0.5 flex h-5 w-5 items-center justify-center rounded-full bg-muted font-bold">
<span className="text-xs">3</span>
</div>
<Trans>Manage permissions and access controls</Trans>
@@ -96,7 +96,7 @@ export default function OrganisationSettingsTeamsPage() {
</div>
</>
) : (
<p className="text-muted-foreground mb-8 max-w-md text-center text-sm">
<p className="mb-8 max-w-md text-center text-sm text-muted-foreground">
<Trans>
You currently have no access to any teams within this organisation. Please contact
your organisation to request access.
@@ -114,7 +114,7 @@ export default function OrganisationSettingsTeamsPage() {
<h1 className="text-2xl font-semibold tracking-tight">
<Trans>{organisation.name} Teams</Trans>
</h1>
<p className="text-muted-foreground mt-1 text-sm">
<p className="mt-1 text-sm text-muted-foreground">
<Trans>Select a team to view its dashboard</Trans>
</p>
</div>
@@ -129,7 +129,7 @@ export default function OrganisationSettingsTeamsPage() {
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
{organisation.teams.map((team) => (
<Link to={`/t/${team.url}`} key={team.id}>
<Card className="hover:bg-muted/50 border-border h-full border transition-all">
<Card className="h-full border border-border transition-all hover:bg-muted/50">
<CardContent className="p-4">
<div className="flex items-center gap-3">
<Avatar className="h-10 w-10 border-2 border-solid">
@@ -145,7 +145,7 @@ export default function OrganisationSettingsTeamsPage() {
<div className="flex items-center justify-between">
<div>
<h3 className="font-medium">{team.name}</h3>
<div className="text-muted-foreground truncate text-xs">
<div className="truncate text-xs text-muted-foreground">
{formatTeamUrl(team.url)}
</div>
</div>
@@ -154,11 +154,11 @@ export default function OrganisationSettingsTeamsPage() {
</div>
<div className="mt-2 flex items-center gap-4">
<div className="text-muted-foreground flex items-center gap-1 text-xs">
<div className="flex items-center gap-1 text-xs text-muted-foreground">
<CalendarIcon className="h-3 w-3" />
{i18n.date(team.createdAt, { dateStyle: 'short' })}
</div>
<div className="text-muted-foreground flex items-center gap-1 text-xs">
<div className="flex items-center gap-1 text-xs text-muted-foreground">
<UserIcon className="h-3 w-3" />
<span>{t(TEAM_MEMBER_ROLE_MAP[team.currentTeamRole])}</span>
</div>
@@ -79,7 +79,7 @@ export default function OrganisationSettingsBrandingPage() {
if (isLoadingOrganisation || !organisationWithSettings) {
return (
<div className="flex items-center justify-center rounded-lg py-32">
<Loader className="text-muted-foreground h-6 w-6 animate-spin" />
<Loader className="h-6 w-6 animate-spin text-muted-foreground" />
</div>
);
}
@@ -149,7 +149,7 @@ export default function AuditLog({ loaderData }: Route.ComponentProps) {
<span className="mt-1 block">
{DateTime.fromJSDate(document.createdAt)
.setLocale(APP_I18N_OPTIONS.defaultLocale)
.toFormat('yyyy-mm-dd hh:mm:ss a (ZZZZ)')}
.toFormat('yyyy-MM-dd hh:mm:ss a (ZZZZ)')}
</span>
</p>
@@ -159,7 +159,7 @@ export default function AuditLog({ loaderData }: Route.ComponentProps) {
<span className="mt-1 block">
{DateTime.fromJSDate(document.updatedAt)
.setLocale(APP_I18N_OPTIONS.defaultLocale)
.toFormat('yyyy-mm-dd hh:mm:ss a (ZZZZ)')}
.toFormat('yyyy-MM-dd hh:mm:ss a (ZZZZ)')}
</span>
</p>
@@ -355,16 +355,16 @@ const SigningPageV1 = ({ data }: { data: Awaited<ReturnType<typeof handleV1Loade
</Trans>
</h2>
<p className="text-muted-foreground/60 mt-2.5 max-w-[60ch] text-center text-sm font-medium md:text-base">
<p className="mt-2.5 max-w-[60ch] text-center text-sm font-medium text-muted-foreground/60 md:text-base">
<Trans>This document has been cancelled by the owner.</Trans>
</p>
{user ? (
<Link to="/" className="text-documenso-700 hover:text-documenso-600 mt-36">
<Link to="/" className="mt-36 text-documenso-700 hover:text-documenso-600">
<Trans>Go Back Home</Trans>
</Link>
) : (
<p className="text-muted-foreground/60 mt-36 text-sm">
<p className="mt-36 text-sm text-muted-foreground/60">
<Trans>
Want to send slick signing links like this one?{' '}
<Link
@@ -455,16 +455,16 @@ const SigningPageV2 = ({ data }: { data: Awaited<ReturnType<typeof handleV2Loade
</Trans>
</h2>
<p className="text-muted-foreground/60 mt-2.5 max-w-[60ch] text-center text-sm font-medium md:text-base">
<p className="mt-2.5 max-w-[60ch] text-center text-sm font-medium text-muted-foreground/60 md:text-base">
<Trans>This document has been cancelled by the owner.</Trans>
</p>
{user ? (
<Link to="/" className="text-documenso-700 hover:text-documenso-600 mt-36">
<Link to="/" className="mt-36 text-documenso-700 hover:text-documenso-600">
<Trans>Go Back Home</Trans>
</Link>
) : (
<p className="text-muted-foreground/60 mt-36 text-sm">
<p className="mt-36 text-sm text-muted-foreground/60">
<Trans>
Want to send slick signing links like this one?{' '}
<Link
@@ -91,25 +91,25 @@ export default function RejectedSigningPage({ loaderData }: Route.ComponentProps
<div className="flex flex-col items-center">
<div className="flex items-center gap-x-4">
<XCircle className="text-destructive h-10 w-10" />
<XCircle className="h-10 w-10 text-destructive" />
<h2 className="max-w-[35ch] text-center text-2xl font-semibold leading-normal md:text-3xl lg:text-4xl">
<Trans>Document Rejected</Trans>
</h2>
</div>
<div className="text-destructive mt-4 flex items-center text-center text-sm">
<div className="mt-4 flex items-center text-center text-sm text-destructive">
<Trans>You have rejected this document</Trans>
</div>
<p className="text-muted-foreground mt-6 max-w-[60ch] text-center text-sm">
<p className="mt-6 max-w-[60ch] text-center text-sm text-muted-foreground">
<Trans>
The document owner has been notified of your decision. They may contact you with further
instructions if necessary.
</Trans>
</p>
<p className="text-muted-foreground mt-2 max-w-[60ch] text-center text-sm">
<p className="mt-2 max-w-[60ch] text-center text-sm text-muted-foreground">
<Trans>No further action is required from you at this time.</Trans>
</p>
@@ -78,14 +78,14 @@ export default function WaitingForTurnToSignPage({ loaderData }: Route.Component
<Trans>Waiting for Your Turn</Trans>
</h2>
<p className="text-muted-foreground mt-2 text-sm">
<p className="mt-2 text-sm text-muted-foreground">
<Trans>
It's currently not your turn to sign. You will receive an email with instructions once
it's your turn to sign the document.
</Trans>
</p>
<p className="text-muted-foreground mt-4 text-sm">
<p className="mt-4 text-sm text-muted-foreground">
<Trans>Please check your email for updates.</Trans>
</p>