Compare commits

..

4 Commits

Author SHA1 Message Date
d4e259a9a7 refactor: improve layout of completed signing page 2025-11-18 10:16:13 +02:00
798b6bd750 feat: add japanese chinese and korean support (#2202)
## Description

Adds the following languages since we updated our PDF sealing to support
special characters
- Japanese
- Korean
- Chinese (Simplified)

## Tests

Ran through the signing process in these new languages.
2025-11-18 16:57:38 +11:00
8fbace0f61 fix: viewed webhook had stale data (#2208) 2025-11-18 16:57:14 +11:00
1bbd04be9b feat: add field dev mode (#2203) 2025-11-18 16:57:06 +11:00
7 changed files with 121 additions and 82 deletions

View File

@ -278,20 +278,8 @@ export const EnvelopeEditorFieldDragDrop = ({
onMouseDown={() => setSelectedField(field.type)}
data-selected={selectedField === field.type ? true : undefined}
className={cn(
'group flex h-12 cursor-pointer items-center justify-center rounded-lg border px-4 transition-colors',
'border-border group flex h-12 cursor-pointer items-center justify-center rounded-lg border px-4 transition-colors',
RECIPIENT_COLOR_STYLES[selectedRecipientColor].fieldButton,
{
'text-recipient-green border-recipient-green/30':
selectedRecipientColor === 'green',
'text-recipient-blue border-recipient-blue/30': selectedRecipientColor === 'blue',
'text-recipient-purple border-recipient-purple/30':
selectedRecipientColor === 'purple',
'text-recipient-orange border-recipient-orange/30':
selectedRecipientColor === 'orange',
'text-recipient-yellow border-recipient-yellow/30':
selectedRecipientColor === 'yellow',
'text-recipient-pink border-recipient-pink/30': selectedRecipientColor === 'pink',
},
)}
>
<p
@ -299,12 +287,12 @@ export const EnvelopeEditorFieldDragDrop = ({
'text-muted-foreground font-noto group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-sm font-normal',
field.className,
{
'text-recipient-green': selectedRecipientColor === 'green',
'text-recipient-blue': selectedRecipientColor === 'blue',
'text-recipient-purple': selectedRecipientColor === 'purple',
'text-recipient-orange': selectedRecipientColor === 'orange',
'text-recipient-yellow': selectedRecipientColor === 'yellow',
'text-recipient-pink': selectedRecipientColor === 'pink',
'group-hover:text-recipient-green': selectedRecipientColor === 'green',
'group-hover:text-recipient-blue': selectedRecipientColor === 'blue',
'group-hover:text-recipient-purple': selectedRecipientColor === 'purple',
'group-hover:text-recipient-orange': selectedRecipientColor === 'orange',
'group-hover:text-recipient-yellow': selectedRecipientColor === 'yellow',
'group-hover:text-recipient-pink': selectedRecipientColor === 'pink',
},
)}
>

View File

@ -5,7 +5,7 @@ import { msg } from '@lingui/core/macro';
import { Trans, useLingui } from '@lingui/react/macro';
import { FieldType, RecipientRole } from '@prisma/client';
import { FileTextIcon } from 'lucide-react';
import { Link } from 'react-router';
import { Link, useSearchParams } from 'react-router';
import { isDeepEqual } from 'remeda';
import { match } from 'ts-pattern';
@ -65,6 +65,8 @@ const FieldSettingsTypeTranslations: Record<FieldType, MessageDescriptor> = {
};
export const EnvelopeEditorFieldsPage = () => {
const [searchParams] = useSearchParams();
const { envelope, editorFields, relativePath } = useCurrentEnvelopeEditor();
const { currentEnvelopeItem } = useCurrentEnvelopeRender();
@ -106,13 +108,6 @@ export const EnvelopeEditorFieldsPage = () => {
editorFields.setSelectedRecipient(firstSelectableRecipient?.id ?? null);
}, []);
const canRecipientBeModified = useMemo(
() =>
editorFields.selectedRecipient !== null &&
canRecipientFieldsBeModified(editorFields.selectedRecipient, envelope.fields),
[editorFields.selectedRecipient, envelope.fields],
);
return (
<div className="relative flex h-full">
<div className="flex w-full flex-col overflow-y-auto">
@ -182,35 +177,32 @@ export const EnvelopeEditorFieldsPage = () => {
align="end"
/>
{!canRecipientBeModified && (
<Alert className="mt-4" variant="warning">
<AlertDescription>
<Trans>
This recipient can no longer be modified as they have signed a field, or
completed the document.
</Trans>
</AlertDescription>
</Alert>
)}
{editorFields.selectedRecipient &&
!canRecipientFieldsBeModified(editorFields.selectedRecipient, envelope.fields) && (
<Alert className="mt-4" variant="warning">
<AlertDescription>
<Trans>
This recipient can no longer be modified as they have signed a field, or
completed the document.
</Trans>
</AlertDescription>
</Alert>
)}
</section>
{canRecipientBeModified && (
<>
<Separator className="my-4" />
<Separator className="my-4" />
{/* Add fields section. */}
<section className="px-4">
<h3 className="text-foreground mb-2 text-sm font-semibold">
<Trans>Add Fields</Trans>
</h3>
{/* Add fields section. */}
<section className="px-4">
<h3 className="text-foreground mb-2 text-sm font-semibold">
<Trans>Add Fields</Trans>
</h3>
<EnvelopeEditorFieldDragDrop
selectedRecipientId={editorFields.selectedRecipient?.id ?? null}
selectedEnvelopeItemId={currentEnvelopeItem?.id ?? null}
/>
</section>
</>
)}
<EnvelopeEditorFieldDragDrop
selectedRecipientId={editorFields.selectedRecipient?.id ?? null}
selectedEnvelopeItemId={currentEnvelopeItem?.id ?? null}
/>
</section>
{/* Field details section. */}
<AnimateGenericFadeInOut key={editorFields.selectedField?.formId}>
@ -218,6 +210,37 @@ export const EnvelopeEditorFieldsPage = () => {
<section>
<Separator className="my-4" />
{searchParams.get('devmode') && (
<>
<div className="px-4">
<h3 className="text-foreground mb-3 text-sm font-semibold">
<Trans>Developer Mode</Trans>
</h3>
<div className="bg-muted/50 border-border text-foreground space-y-2 rounded-md border p-3 text-sm">
<p>
<span className="text-muted-foreground min-w-12">Pos X:&nbsp;</span>
{selectedField.positionX.toFixed(2)}
</p>
<p>
<span className="text-muted-foreground min-w-12">Pos Y:&nbsp;</span>
{selectedField.positionY.toFixed(2)}
</p>
<p>
<span className="text-muted-foreground min-w-12">Width:&nbsp;</span>
{selectedField.width.toFixed(2)}
</p>
<p>
<span className="text-muted-foreground min-w-12">Height:&nbsp;</span>
{selectedField.height.toFixed(2)}
</p>
</div>
</div>
<Separator className="my-4" />
</>
)}
<div className="[&_label]:text-foreground/70 px-4 [&_label]:text-xs">
<h3 className="text-sm font-semibold">
{t(FieldSettingsTypeTranslations[selectedField.type])}

View File

@ -202,8 +202,12 @@ export default function CompletedSigningPage({ loaderData }: Route.ComponentProp
</p>
))}
<div className="mt-8 flex w-full max-w-sm items-center justify-center gap-4">
<DocumentShareButton documentId={document.id} token={recipient.token} />
<div className="mt-8 flex w-full max-w-xs flex-col items-stretch gap-4 md:w-auto md:max-w-none md:flex-row md:items-center">
<DocumentShareButton
documentId={document.id}
token={recipient.token}
className="w-full max-w-none md:flex-1"
/>
{isDocumentCompleted(document.status) && (
<EnvelopeDownloadDialog
@ -212,13 +216,21 @@ export default function CompletedSigningPage({ loaderData }: Route.ComponentProp
envelopeItems={document.envelopeItems}
token={recipient?.token}
trigger={
<Button type="button" variant="outline" className="flex-1">
<Button type="button" variant="outline" className="flex-1 md:flex-initial">
<DownloadIcon className="mr-2 h-5 w-5" />
<Trans>Download</Trans>
</Button>
}
/>
)}
{user && (
<Button asChild>
<Link to="/">
<Trans>Go Back Home</Trans>
</Link>
</Button>
)}
</div>
</div>
@ -238,12 +250,6 @@ export default function CompletedSigningPage({ loaderData }: Route.ComponentProp
<ClaimAccount defaultName={recipientName} defaultEmail={recipient.email} />
</div>
)}
{user && (
<Link to="/" className="text-documenso-700 hover:text-documenso-600 mt-2">
<Trans>Go Back Home</Trans>
</Link>
)}
</div>
</div>

View File

@ -1,6 +1,16 @@
import { z } from 'zod';
export const SUPPORTED_LANGUAGE_CODES = ['de', 'en', 'fr', 'es', 'it', 'pl'] as const;
export const SUPPORTED_LANGUAGE_CODES = [
'de',
'en',
'fr',
'es',
'it',
'pl',
'ja',
'ko',
'zh',
] as const;
export const ZSupportedLanguageCodeSchema = z.enum(SUPPORTED_LANGUAGE_CODES).catch('en');
@ -54,6 +64,18 @@ export const SUPPORTED_LANGUAGES: Record<string, SupportedLanguage> = {
short: 'pl',
full: 'Polish',
},
ja: {
short: 'ja',
full: 'Japanese',
},
ko: {
short: 'ko',
full: 'Korean',
},
zh: {
short: 'zh',
full: 'Chinese',
},
} satisfies Record<SupportedLanguageCodes, SupportedLanguage>;
export const isValidLanguageCode = (code: unknown): code is SupportedLanguageCodes =>

View File

@ -31,26 +31,16 @@ export const viewedDocument = async ({
type: EnvelopeType.DOCUMENT,
},
},
include: {
envelope: {
include: {
documentMeta: true,
recipients: true,
},
},
},
});
if (!recipient) {
return;
}
const { envelope } = recipient;
await prisma.documentAuditLog.create({
data: createDocumentAuditLogData({
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_VIEWED,
envelopeId: envelope.id,
envelopeId: recipient.envelopeId,
user: {
name: recipient.name,
email: recipient.email,
@ -86,7 +76,7 @@ export const viewedDocument = async ({
await tx.documentAuditLog.create({
data: createDocumentAuditLogData({
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED,
envelopeId: envelope.id,
envelopeId: recipient.envelopeId,
user: {
name: recipient.name,
email: recipient.email,
@ -103,6 +93,16 @@ export const viewedDocument = async ({
});
});
const envelope = await prisma.envelope.findUniqueOrThrow({
where: {
id: recipient.envelopeId,
},
include: {
documentMeta: true,
recipients: true,
},
});
await triggerWebhook({
event: WebhookTriggerEvents.DOCUMENT_OPENED,
data: ZWebhookDocumentSchema.parse(mapEnvelopeToWebhookDocumentPayload(envelope)),

View File

@ -127,11 +127,11 @@ export const DocumentShareButton = ({
<Button
variant="outline"
disabled={!token || !documentId}
className={cn('flex-1 text-[11px]', className)}
className={cn('w-full max-w-lg flex-1 text-[11px]', className)}
loading={isLoading}
>
{!isLoading && <Sparkles className="mr-2 h-5 w-5" />}
<Trans>Share Signature Card</Trans>
<Trans>Share</Trans>
</Button>
)}
</DialogTrigger>

View File

@ -39,7 +39,7 @@ export const RECIPIENT_COLOR_STYLES = {
baseRing: 'rgba(122, 195, 85, 1)',
baseRingHover: 'rgba(122, 195, 85, 0.3)',
baseTextHover: 'rgba(122, 195, 85, 1)',
fieldButton: 'hover:border-recipient-green bg-recipient-green/5 hover:bg-recipient-green/30 ',
fieldButton: 'hover:border-recipient-green hover:bg-recipient-green/30 ',
fieldItem: 'group/field-item rounded-[2px]',
fieldItemInitials: 'group-hover/field-item:bg-recipient-green',
comboxBoxTrigger:
@ -52,7 +52,7 @@ export const RECIPIENT_COLOR_STYLES = {
baseRing: 'rgba(56, 123, 199, 1)',
baseRingHover: 'rgba(56, 123, 199, 0.3)',
baseTextHover: 'rgba(56, 123, 199, 1)',
fieldButton: 'hover:border-recipient-blue bg-recipient-blue/5 hover:bg-recipient-blue/30',
fieldButton: 'hover:border-recipient-blue hover:bg-recipient-blue/30',
fieldItem: 'group/field-item rounded-[2px]',
fieldItemInitials: 'group-hover/field-item:bg-recipient-blue',
comboxBoxTrigger:
@ -65,7 +65,7 @@ export const RECIPIENT_COLOR_STYLES = {
baseRing: 'rgba(151, 71, 255, 1)',
baseRingHover: 'rgba(151, 71, 255, 0.3)',
baseTextHover: 'rgba(151, 71, 255, 1)',
fieldButton: 'hover:border-recipient-purple bg-recipient-purple/5 hover:bg-recipient-purple/30',
fieldButton: 'hover:border-recipient-purple hover:bg-recipient-purple/30',
fieldItem: 'group/field-item rounded-[2px]',
fieldItemInitials: 'group-hover/field-item:bg-recipient-purple',
comboxBoxTrigger:
@ -78,7 +78,7 @@ export const RECIPIENT_COLOR_STYLES = {
baseRing: 'rgba(246, 159, 30, 1)',
baseRingHover: 'rgba(246, 159, 30, 0.3)',
baseTextHover: 'rgba(246, 159, 30, 1)',
fieldButton: 'hover:border-recipient-orange bg-recipient-orange/5 hover:bg-recipient-orange/30',
fieldButton: 'hover:border-recipient-orange hover:bg-recipient-orange/30',
fieldItem: 'group/field-item rounded-[2px]',
fieldItemInitials: 'group-hover/field-item:bg-recipient-orange',
comboxBoxTrigger:
@ -91,7 +91,7 @@ export const RECIPIENT_COLOR_STYLES = {
baseRing: 'rgba(219, 186, 0, 1)',
baseRingHover: 'rgba(219, 186, 0, 0.3)',
baseTextHover: 'rgba(219, 186, 0, 1)',
fieldButton: 'hover:border-recipient-yellow bg-recipient-yellow/5 hover:bg-recipient-yellow/30',
fieldButton: 'hover:border-recipient-yellow hover:bg-recipient-yellow/30',
fieldItem: 'group/field-item rounded-[2px]',
fieldItemInitials: 'group-hover/field-item:bg-recipient-yellow',
comboxBoxTrigger:
@ -104,7 +104,7 @@ export const RECIPIENT_COLOR_STYLES = {
baseRing: 'rgba(217, 74, 186, 1)',
baseRingHover: 'rgba(217, 74, 186, 0.3)',
baseTextHover: 'rgba(217, 74, 186, 1)',
fieldButton: 'hover:border-recipient-pink bg-recipient-pink/5 hover:bg-recipient-pink/30',
fieldButton: 'hover:border-recipient-pink hover:bg-recipient-pink/30',
fieldItem: 'group/field-item rounded-[2px]',
fieldItemInitials: 'group-hover/field-item:bg-recipient-pink',
comboxBoxTrigger: