Compare commits

...

30 Commits

Author SHA1 Message Date
69ab5eedd5 Feature: Dokumenten sharing deaktiviert 2025-04-25 15:12:49 +02:00
9007acd265 Design: Icons geändert 2025-04-25 15:12:25 +02:00
fcf906db36 Fix: Suchmenü überarbeitet 2025-04-25 15:12:00 +02:00
0e196a68cf Fix: Standartsprache - deutsch & andere Sprachen deaktiviert 2025-04-25 15:11:15 +02:00
643e37a0aa Fix: Links angepasst 2025-04-25 10:03:42 +02:00
f6a1b0bd2d Design: Fonts geändert 2025-04-25 10:03:25 +02:00
28b28f7363 Design: Farben geändert 2025-04-25 10:03:06 +02:00
f1b989bb78 Übersetzungen & Anpassungen 2025-04-24 14:22:00 +02:00
63182f9587 packages/lib/translations/de/web.po aktualisiert 2025-04-19 08:25:59 +00:00
a9d7c3315d packages/lib/translations/de/web.po aktualisiert 2025-04-19 08:19:01 +00:00
de4197fba1 packages/lib/translations/de/web.po aktualisiert 2025-04-19 07:26:31 +00:00
1b304b4f44 packages/lib/translations/de/web.po aktualisiert 2025-04-18 12:01:13 +00:00
5c1a0c683f packages/lib/translations/de/web.po aktualisiert 2025-04-18 11:41:15 +00:00
3af68e9e49 Standartsprache (für Dokumente) - DE 2025-03-24 18:29:28 +01:00
ff712de07c Standartsprache für Dokumente - DE 2025-03-24 18:26:16 +01:00
ce9bd6bb80 Übersetzung: Anpassungen 2025-03-24 18:25:56 +01:00
1447f03456 Standartsprache für Dokumente - DE 2025-03-24 18:10:32 +01:00
335e833170 Übersetzungen und Wording anpassung 2025-03-24 18:10:14 +01:00
e20153c9c5 Anpassen der Standart Support E-Mail 2025-03-24 18:09:58 +01:00
23c6c7935e Übersetzungen 2025-03-24 18:09:40 +01:00
01232624cc Share Button auskommentiert 2025-03-24 18:09:17 +01:00
3045bcefc2 Dokumentenablehnung - Übersetzung 2025-03-24 18:09:00 +01:00
cde5a43410 Übersetung: Anpassung Branding und Ansprechweise 2025-03-24 15:58:02 +01:00
bdf4db2c30 Öffentliches Profil: Button auf Webseite anstatt auf /signup 2025-03-24 15:55:52 +01:00
5747a0d52d Änderung an security.txt 2025-03-24 15:55:23 +01:00
d2b83af9ea Änderungen an Branding - App Name 2025-03-24 15:55:08 +01:00
David Nguyen
063fd32f18 feat: add signature configurations (#1710)
Add ability to enable or disable allowed signature types:
- Drawn
- Typed
- Uploaded

**Tabbed style signature dialog**

![image](https://github.com/user-attachments/assets/a816fab6-b071-42a5-bb5c-6d4a2572431e)

**Document settings**

![image](https://github.com/user-attachments/assets/f0c1bff1-6be1-4c87-b384-1666fa25d7a6)

**Team preferences**

![image](https://github.com/user-attachments/assets/8767b05e-1463-4087-8672-f3f43d8b0f2c)

- Add multiselect to select allowed signatures in document and templates
settings tab
- Add multiselect to select allowed signatures in teams preferences
- Removed "Enable typed signatures" from document/template edit page
- Refactored signature pad to use tabs instead of an all in one
signature pad

Added E2E tests to check settings are applied correctly for documents
and templates
2025-03-24 17:13:11 +11:00
Mythie
231f51bd1f v1.10.0-rc.1 2025-03-22 17:34:33 +11:00
Mythie
a8de8368a2 fix: hide powered by on certificate for platform documents 2025-03-22 12:04:08 +11:00
Mythie
7dd331addf fix: allow blank rejection reasons 2025-03-22 12:01:18 +11:00
180 changed files with 3901 additions and 2061 deletions

View File

@@ -1,7 +1,4 @@
# General Issues
Contact: https://github.com/documenso/documenso/issues/new?assignees=&labels=bug&projects=&template=bug-report.yml
# Report critical issues privately to let us take appropriate action before publishing. # Report critical issues privately to let us take appropriate action before publishing.
Contact: mailto:security@documenso.com Contact: mailto:hello@bls-media.de
Preferred-Languages: en Preferred-Languages: de
Canonical: https://documenso.com/.well-known/security.txt Canonical: https://bls.media/.well-known/security.txt

View File

@@ -150,7 +150,7 @@ Example payload for the `document.created` event:
"id": 52, "id": 52,
"documentId": 10, "documentId": 10,
"templateId": null, "templateId": null,
"email": "signer@documenso.com", "email": "signer@sign.bls.media",
"name": "John Doe", "name": "John Doe",
"token": "vbT8hi3jKQmrFP_LN1WcS", "token": "vbT8hi3jKQmrFP_LN1WcS",
"documentDeletedAt": null, "documentDeletedAt": null,
@@ -212,7 +212,7 @@ Example payload for the `document.sent` event:
"id": 52, "id": 52,
"documentId": 10, "documentId": 10,
"templateId": null, "templateId": null,
"email": "signer2@documenso.com", "email": "signer2@sign.bls.media",
"name": "Signer 2", "name": "Signer 2",
"token": "vbT8hi3jKQmrFP_LN1WcS", "token": "vbT8hi3jKQmrFP_LN1WcS",
"documentDeletedAt": null, "documentDeletedAt": null,
@@ -230,7 +230,7 @@ Example payload for the `document.sent` event:
"id": 53, "id": 53,
"documentId": 10, "documentId": 10,
"templateId": null, "templateId": null,
"email": "signer1@documenso.com", "email": "signer1@sign.bls.media",
"name": "Signer 1", "name": "Signer 1",
"token": "HkrptwS42ZBXdRKj1TyUo", "token": "HkrptwS42ZBXdRKj1TyUo",
"documentDeletedAt": null, "documentDeletedAt": null,
@@ -292,7 +292,7 @@ Example payload for the `document.opened` event:
"id": 52, "id": 52,
"documentId": 10, "documentId": 10,
"templateId": null, "templateId": null,
"email": "signer2@documenso.com", "email": "signer2@sign.bls.media",
"name": "Signer 2", "name": "Signer 2",
"token": "vbT8hi3jKQmrFP_LN1WcS", "token": "vbT8hi3jKQmrFP_LN1WcS",
"documentDeletedAt": null, "documentDeletedAt": null,
@@ -354,7 +354,7 @@ Example payload for the `document.signed` event:
"id": 51, "id": 51,
"documentId": 10, "documentId": 10,
"templateId": null, "templateId": null,
"email": "signer1@documenso.com", "email": "signer1@sign.bls.media",
"name": "Signer 1", "name": "Signer 1",
"token": "HkrptwS42ZBXdRKj1TyUo", "token": "HkrptwS42ZBXdRKj1TyUo",
"documentDeletedAt": null, "documentDeletedAt": null,
@@ -419,7 +419,7 @@ Example payload for the `document.completed` event:
"id": 50, "id": 50,
"documentId": 10, "documentId": 10,
"templateId": null, "templateId": null,
"email": "signer2@documenso.com", "email": "signer2@sign.bls.media",
"name": "Signer 2", "name": "Signer 2",
"token": "vbT8hi3jKQmrFP_LN1WcS", "token": "vbT8hi3jKQmrFP_LN1WcS",
"documentDeletedAt": null, "documentDeletedAt": null,
@@ -440,7 +440,7 @@ Example payload for the `document.completed` event:
"id": 51, "id": 51,
"documentId": 10, "documentId": 10,
"templateId": null, "templateId": null,
"email": "signer1@documenso.com", "email": "signer1@sign.bls.media",
"name": "Signer 1", "name": "Signer 1",
"token": "HkrptwS42ZBXdRKj1TyUo", "token": "HkrptwS42ZBXdRKj1TyUo",
"documentDeletedAt": null, "documentDeletedAt": null,
@@ -505,7 +505,7 @@ Example payload for the `document.rejected` event:
"id": 52, "id": 52,
"documentId": 10, "documentId": 10,
"templateId": null, "templateId": null,
"email": "signer@documenso.com", "email": "signer@sign.bls.media",
"name": "Signer", "name": "Signer",
"token": "vbT8hi3jKQmrFP_LN1WcS", "token": "vbT8hi3jKQmrFP_LN1WcS",
"documentDeletedAt": null, "documentDeletedAt": null,
@@ -598,7 +598,7 @@ Example payload for the `document.rejected` event:
"id": 7, "id": 7,
"documentId": 7, "documentId": 7,
"templateId": null, "templateId": null,
"email": "signer@documenso.com", "email": "signer@sign.bls.media",
"name": "Signer", "name": "Signer",
"token": "XkKx1HCs6Znm2UBJA2j6o", "token": "XkKx1HCs6Znm2UBJA2j6o",
"documentDeletedAt": null, "documentDeletedAt": null,

View File

@@ -2,7 +2,7 @@ import type { DocsThemeConfig } from 'nextra-theme-docs';
import { useConfig } from 'nextra-theme-docs'; import { useConfig } from 'nextra-theme-docs';
const themeConfig: DocsThemeConfig = { const themeConfig: DocsThemeConfig = {
logo: <span>Documenso</span>, logo: <span>BLS sign</span>,
head: function useHead() { head: function useHead() {
const config = useConfig<{ title?: string; description?: string }>(); const config = useConfig<{ title?: string; description?: string }>();

View File

@@ -216,9 +216,9 @@ export const TeamMemberInviteDialog = ({ trigger, ...props }: TeamMemberInviteDi
const downloadTemplate = () => { const downloadTemplate = () => {
const data = [ const data = [
{ email: 'admin@documenso.com', role: 'Admin' }, { email: 'admin@sign.bls.media', role: 'Admin' },
{ email: 'manager@documenso.com', role: 'Manager' }, { email: 'manager@sign.bls.media', role: 'Manager' },
{ email: 'member@documenso.com', role: 'Member' }, { email: 'member@sign.bls.media', role: 'Member' },
]; ];
const csvContent = const csvContent =

View File

@@ -3,8 +3,8 @@ import { useEffect, useLayoutEffect, useState } from 'react';
import { msg } from '@lingui/core/macro'; import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react'; import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro';
import { type DocumentData, type Field, FieldType } from '@prisma/client';
import type { DocumentMeta, Recipient, Signature, TemplateMeta } from '@prisma/client'; import type { DocumentMeta, Recipient, Signature, TemplateMeta } from '@prisma/client';
import { type DocumentData, type Field, FieldType } from '@prisma/client';
import { LucideChevronDown, LucideChevronUp } from 'lucide-react'; import { LucideChevronDown, LucideChevronUp } from 'lucide-react';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { useSearchParams } from 'react-router'; import { useSearchParams } from 'react-router';
@@ -25,12 +25,11 @@ import type {
} from '@documenso/trpc/server/field-router/schema'; } from '@documenso/trpc/server/field-router/schema';
import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip'; import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
import { Card, CardContent } from '@documenso/ui/primitives/card';
import { ElementVisible } from '@documenso/ui/primitives/element-visible'; import { ElementVisible } from '@documenso/ui/primitives/element-visible';
import { Input } from '@documenso/ui/primitives/input'; import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label'; import { Label } from '@documenso/ui/primitives/label';
import { PDFViewer } from '@documenso/ui/primitives/pdf-viewer'; import { PDFViewer } from '@documenso/ui/primitives/pdf-viewer';
import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; import { SignaturePadDialog } from '@documenso/ui/primitives/signature-pad/signature-pad-dialog';
import { useToast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast';
import { BrandingLogo } from '~/components/general/branding-logo'; import { BrandingLogo } from '~/components/general/branding-logo';
@@ -69,16 +68,8 @@ export const EmbedDirectTemplateClientPage = ({
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const { const { fullName, email, signature, setFullName, setEmail, setSignature } =
fullName, useRequiredDocumentSigningContext();
email,
signature,
signatureValid,
setFullName,
setEmail,
setSignature,
setSignatureValid,
} = useRequiredDocumentSigningContext();
const [hasFinishedInit, setHasFinishedInit] = useState(false); const [hasFinishedInit, setHasFinishedInit] = useState(false);
const [hasDocumentLoaded, setHasDocumentLoaded] = useState(false); const [hasDocumentLoaded, setHasDocumentLoaded] = useState(false);
@@ -194,10 +185,6 @@ export const EmbedDirectTemplateClientPage = ({
const onCompleteClick = async () => { const onCompleteClick = async () => {
try { try {
if (hasSignatureField && !signatureValid) {
return;
}
const valid = validateFieldsInserted(pendingFields); const valid = validateFieldsInserted(pendingFields);
if (!valid) { if (!valid) {
@@ -419,34 +406,16 @@ export const EmbedDirectTemplateClientPage = ({
<Trans>Signature</Trans> <Trans>Signature</Trans>
</Label> </Label>
<Card className="mt-2" gradient degrees={-120}> <SignaturePadDialog
<CardContent className="p-0"> className="mt-2"
<SignaturePad disabled={isThrottled || isSubmitting}
className="h-44 w-full" disableAnimation
disabled={isThrottled || isSubmitting} value={signature ?? ''}
defaultValue={signature ?? undefined} onChange={(v) => setSignature(v ?? '')}
onChange={(value) => { typedSignatureEnabled={metadata?.typedSignatureEnabled}
setSignature(value); uploadSignatureEnabled={metadata?.uploadSignatureEnabled}
}} drawSignatureEnabled={metadata?.drawSignatureEnabled}
onValidityChange={(isValid) => { />
setSignatureValid(isValid);
}}
allowTypedSignature={Boolean(
metadata &&
'typedSignatureEnabled' in metadata &&
metadata.typedSignatureEnabled,
)}
/>
</CardContent>
</Card>
{hasSignatureField && !signatureValid && (
<div className="text-destructive mt-2 text-sm">
<Trans>
Signature is too small. Please provide a more complete signature.
</Trans>
</div>
)}
</div> </div>
)} )}
</div> </div>

View File

@@ -20,7 +20,7 @@ export const EmbedDocumentCompleted = ({ name, signature }: EmbedDocumentComplet
<div className="mt-8 w-full max-w-md"> <div className="mt-8 w-full max-w-md">
<SigningCard3D <SigningCard3D
className="mx-auto w-full" className="mx-auto w-full"
name={name || 'Documenso'} name={name || 'BLS sign'}
signature={signature} signature={signature}
signingCelebrationImage={signingCelebration} signingCelebrationImage={signingCelebration}
/> />

View File

@@ -54,6 +54,8 @@ export const EmbedDocumentFields = ({
onSignField={onSignField} onSignField={onSignField}
onUnsignField={onUnsignField} onUnsignField={onUnsignField}
typedSignatureEnabled={metadata?.typedSignatureEnabled} typedSignatureEnabled={metadata?.typedSignatureEnabled}
uploadSignatureEnabled={metadata?.uploadSignatureEnabled}
drawSignatureEnabled={metadata?.drawSignatureEnabled}
/> />
)) ))
.with(FieldType.INITIALS, () => ( .with(FieldType.INITIALS, () => (

View File

@@ -21,13 +21,12 @@ import type { RecipientWithFields } from '@documenso/prisma/types/recipient-with
import { trpc } from '@documenso/trpc/react'; import { trpc } from '@documenso/trpc/react';
import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip'; import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
import { Card, CardContent } from '@documenso/ui/primitives/card';
import { ElementVisible } from '@documenso/ui/primitives/element-visible'; import { ElementVisible } from '@documenso/ui/primitives/element-visible';
import { Input } from '@documenso/ui/primitives/input'; import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label'; import { Label } from '@documenso/ui/primitives/label';
import { PDFViewer } from '@documenso/ui/primitives/pdf-viewer'; import { PDFViewer } from '@documenso/ui/primitives/pdf-viewer';
import { RadioGroup, RadioGroupItem } from '@documenso/ui/primitives/radio-group'; import { RadioGroup, RadioGroupItem } from '@documenso/ui/primitives/radio-group';
import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; import { SignaturePadDialog } from '@documenso/ui/primitives/signature-pad/signature-pad-dialog';
import { useToast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast';
import { BrandingLogo } from '~/components/general/branding-logo'; import { BrandingLogo } from '~/components/general/branding-logo';
@@ -70,15 +69,8 @@ export const EmbedSignDocumentClientPage = ({
const { _ } = useLingui(); const { _ } = useLingui();
const { toast } = useToast(); const { toast } = useToast();
const { const { fullName, email, signature, setFullName, setSignature } =
fullName, useRequiredDocumentSigningContext();
email,
signature,
signatureValid,
setFullName,
setSignature,
setSignatureValid,
} = useRequiredDocumentSigningContext();
const [hasFinishedInit, setHasFinishedInit] = useState(false); const [hasFinishedInit, setHasFinishedInit] = useState(false);
const [hasDocumentLoaded, setHasDocumentLoaded] = useState(false); const [hasDocumentLoaded, setHasDocumentLoaded] = useState(false);
@@ -129,10 +121,6 @@ export const EmbedSignDocumentClientPage = ({
const onCompleteClick = async () => { const onCompleteClick = async () => {
try { try {
if (hasSignatureField && !signatureValid) {
return;
}
const valid = validateFieldsInserted(fieldsRequiringValidation); const valid = validateFieldsInserted(fieldsRequiringValidation);
if (!valid) { if (!valid) {
@@ -432,34 +420,16 @@ export const EmbedSignDocumentClientPage = ({
<Trans>Signature</Trans> <Trans>Signature</Trans>
</Label> </Label>
<Card className="mt-2" gradient degrees={-120}> <SignaturePadDialog
<CardContent className="p-0"> className="mt-2"
<SignaturePad disabled={isThrottled || isSubmitting}
className="h-44 w-full" disableAnimation
disabled={isThrottled || isSubmitting} value={signature ?? ''}
defaultValue={signature ?? undefined} onChange={(v) => setSignature(v ?? '')}
onChange={(value) => { typedSignatureEnabled={metadata?.typedSignatureEnabled}
setSignature(value); uploadSignatureEnabled={metadata?.uploadSignatureEnabled}
}} drawSignatureEnabled={metadata?.drawSignatureEnabled}
onValidityChange={(isValid) => { />
setSignatureValid(isValid);
}}
allowTypedSignature={Boolean(
metadata &&
'typedSignatureEnabled' in metadata &&
metadata.typedSignatureEnabled,
)}
/>
</CardContent>
</Card>
{hasSignatureField && !signatureValid && (
<div className="text-destructive mt-2 text-sm">
<Trans>
Signature is too small. Please provide a more complete signature.
</Trans>
</div>
)}
</div> </div>
)} )}
</> </>
@@ -477,9 +447,7 @@ export const EmbedSignDocumentClientPage = ({
) : ( ) : (
<Button <Button
className={allowDocumentRejection ? 'col-start-2' : 'col-span-2'} className={allowDocumentRejection ? 'col-start-2' : 'col-span-2'}
disabled={ disabled={isThrottled}
isThrottled || (!isAssistantMode && hasSignatureField && !signatureValid)
}
loading={isSubmitting} loading={isSubmitting}
onClick={() => throttledOnCompleteClick()} onClick={() => throttledOnCompleteClick()}
> >

View File

@@ -19,12 +19,15 @@ import {
} from '@documenso/ui/primitives/form/form'; } from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input'; import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label'; import { Label } from '@documenso/ui/primitives/label';
import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; import { SignaturePadDialog } from '@documenso/ui/primitives/signature-pad/signature-pad-dialog';
import { useToast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast';
export const ZProfileFormSchema = z.object({ export const ZProfileFormSchema = z.object({
name: z.string().trim().min(1, { message: 'Please enter a valid name.' }), name: z
signature: z.string().min(1, 'Signature Pad cannot be empty'), .string()
.trim()
.min(1, { message: msg`Please enter a valid name.`.id }),
signature: z.string().min(1, { message: msg`Signature Pad cannot be empty.`.id }),
}); });
export const ZTwoFactorAuthTokenSchema = z.object({ export const ZTwoFactorAuthTokenSchema = z.object({
@@ -109,22 +112,20 @@ export const ProfileForm = ({ className }: ProfileFormProps) => {
</Label> </Label>
<Input id="email" type="email" className="bg-muted mt-2" value={user.email} disabled /> <Input id="email" type="email" className="bg-muted mt-2" value={user.email} disabled />
</div> </div>
<FormField <FormField
control={form.control} control={form.control}
name="signature" name="signature"
render={({ field: { onChange } }) => ( render={({ field: { onChange, value } }) => (
<FormItem> <FormItem>
<FormLabel> <FormLabel>
<Trans>Signature</Trans> <Trans>Signature</Trans>
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<SignaturePad <SignaturePadDialog
className="h-44 w-full"
disabled={isSubmitting} disabled={isSubmitting}
containerClassName={cn('rounded-lg border bg-background')} value={value}
defaultValue={user.signature ?? undefined}
onChange={(v) => onChange(v ?? '')} onChange={(v) => onChange(v ?? '')}
allowTypedSignature={true}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
@@ -134,7 +135,7 @@ export const ProfileForm = ({ className }: ProfileFormProps) => {
</fieldset> </fieldset>
<Button type="submit" loading={isSubmitting} className="self-end"> <Button type="submit" loading={isSubmitting} className="self-end">
{isSubmitting ? <Trans>Updating profile...</Trans> : <Trans>Update profile</Trans>} <Trans>Update profile</Trans>
</Button> </Button>
</form> </form>
</Form> </Form>

View File

@@ -30,7 +30,7 @@ import {
} from '@documenso/ui/primitives/form/form'; } from '@documenso/ui/primitives/form/form';
import { Input } from '@documenso/ui/primitives/input'; import { Input } from '@documenso/ui/primitives/input';
import { PasswordInput } from '@documenso/ui/primitives/password-input'; import { PasswordInput } from '@documenso/ui/primitives/password-input';
import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; import { SignaturePadDialog } from '@documenso/ui/primitives/signature-pad/signature-pad-dialog';
import { useToast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast';
import { UserProfileSkeleton } from '~/components/general/user-profile-skeleton'; import { UserProfileSkeleton } from '~/components/general/user-profile-skeleton';
@@ -353,16 +353,15 @@ export const SignUpForm = ({
<FormField <FormField
control={form.control} control={form.control}
name="signature" name="signature"
render={({ field: { onChange } }) => ( render={({ field: { onChange, value } }) => (
<FormItem> <FormItem>
<FormLabel> <FormLabel>
<Trans>Sign Here</Trans> <Trans>Sign Here</Trans>
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<SignaturePad <SignaturePadDialog
className="h-36 w-full"
disabled={isSubmitting} disabled={isSubmitting}
containerClassName="mt-2 rounded-lg border bg-background" value={value}
onChange={(v) => onChange(v ?? '')} onChange={(v) => onChange(v ?? '')}
/> />
</FormControl> </FormControl>
@@ -535,7 +534,7 @@ export const SignUpForm = ({
<Trans> <Trans>
By proceeding, you agree to our{' '} By proceeding, you agree to our{' '}
<Link <Link
to="https://documen.so/terms" to="https://bls.media/agb/"
target="_blank" target="_blank"
className="text-documenso-700 duration-200 hover:opacity-70" className="text-documenso-700 duration-200 hover:opacity-70"
> >
@@ -543,7 +542,7 @@ export const SignUpForm = ({
</Link>{' '} </Link>{' '}
and{' '} and{' '}
<Link <Link
to="https://documen.so/privacy" to="https://bls.media/datenschutz/"
target="_blank" target="_blank"
className="text-documenso-700 duration-200 hover:opacity-70" className="text-documenso-700 duration-200 hover:opacity-70"
> >

View File

@@ -308,7 +308,7 @@ export function TeamBrandingPreferencesForm({ team, settings }: TeamBrandingPref
<div className="flex flex-row justify-end space-x-4"> <div className="flex flex-row justify-end space-x-4">
<Button type="submit" loading={form.formState.isSubmitting}> <Button type="submit" loading={form.formState.isSubmitting}>
<Trans>Save</Trans> <Trans>Update</Trans>
</Button> </Button>
</div> </div>
</fieldset> </fieldset>

View File

@@ -8,12 +8,15 @@ import { useForm } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import { useSession } from '@documenso/lib/client-only/providers/session'; import { useSession } from '@documenso/lib/client-only/providers/session';
import { DOCUMENT_SIGNATURE_TYPES, DocumentSignatureType } from '@documenso/lib/constants/document';
import { import {
SUPPORTED_LANGUAGES, SUPPORTED_LANGUAGES,
SUPPORTED_LANGUAGE_CODES, SUPPORTED_LANGUAGE_CODES,
isValidLanguageCode, isValidLanguageCode,
} from '@documenso/lib/constants/i18n'; } from '@documenso/lib/constants/i18n';
import { extractTeamSignatureSettings } from '@documenso/lib/utils/teams';
import { trpc } from '@documenso/trpc/react'; import { trpc } from '@documenso/trpc/react';
import { DocumentSignatureSettingsTooltip } from '@documenso/ui/components/document/document-signature-settings-tooltip';
import { Alert } from '@documenso/ui/primitives/alert'; import { Alert } from '@documenso/ui/primitives/alert';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
import { import {
@@ -23,7 +26,9 @@ import {
FormField, FormField,
FormItem, FormItem,
FormLabel, FormLabel,
FormMessage,
} from '@documenso/ui/primitives/form/form'; } from '@documenso/ui/primitives/form/form';
import { MultiSelectCombobox } from '@documenso/ui/primitives/multi-select-combobox';
import { import {
Select, Select,
SelectContent, SelectContent,
@@ -38,8 +43,10 @@ const ZTeamDocumentPreferencesFormSchema = z.object({
documentVisibility: z.nativeEnum(DocumentVisibility), documentVisibility: z.nativeEnum(DocumentVisibility),
documentLanguage: z.enum(SUPPORTED_LANGUAGE_CODES), documentLanguage: z.enum(SUPPORTED_LANGUAGE_CODES),
includeSenderDetails: z.boolean(), includeSenderDetails: z.boolean(),
typedSignatureEnabled: z.boolean(),
includeSigningCertificate: z.boolean(), includeSigningCertificate: z.boolean(),
signatureTypes: z.array(z.nativeEnum(DocumentSignatureType)).min(1, {
message: msg`At least one signature type must be enabled`.id,
}),
}); });
type TTeamDocumentPreferencesFormSchema = z.infer<typeof ZTeamDocumentPreferencesFormSchema>; type TTeamDocumentPreferencesFormSchema = z.infer<typeof ZTeamDocumentPreferencesFormSchema>;
@@ -67,10 +74,10 @@ export const TeamDocumentPreferencesForm = ({
documentVisibility: settings?.documentVisibility ?? 'EVERYONE', documentVisibility: settings?.documentVisibility ?? 'EVERYONE',
documentLanguage: isValidLanguageCode(settings?.documentLanguage) documentLanguage: isValidLanguageCode(settings?.documentLanguage)
? settings?.documentLanguage ? settings?.documentLanguage
: 'en', : 'de',
includeSenderDetails: settings?.includeSenderDetails ?? false, includeSenderDetails: settings?.includeSenderDetails ?? false,
typedSignatureEnabled: settings?.typedSignatureEnabled ?? true,
includeSigningCertificate: settings?.includeSigningCertificate ?? true, includeSigningCertificate: settings?.includeSigningCertificate ?? true,
signatureTypes: extractTeamSignatureSettings(settings),
}, },
resolver: zodResolver(ZTeamDocumentPreferencesFormSchema), resolver: zodResolver(ZTeamDocumentPreferencesFormSchema),
}); });
@@ -84,7 +91,7 @@ export const TeamDocumentPreferencesForm = ({
documentLanguage, documentLanguage,
includeSenderDetails, includeSenderDetails,
includeSigningCertificate, includeSigningCertificate,
typedSignatureEnabled, signatureTypes,
} = data; } = data;
await updateTeamDocumentPreferences({ await updateTeamDocumentPreferences({
@@ -93,8 +100,10 @@ export const TeamDocumentPreferencesForm = ({
documentVisibility, documentVisibility,
documentLanguage, documentLanguage,
includeSenderDetails, includeSenderDetails,
typedSignatureEnabled,
includeSigningCertificate, includeSigningCertificate,
typedSignatureEnabled: signatureTypes.includes(DocumentSignatureType.TYPE),
uploadSignatureEnabled: signatureTypes.includes(DocumentSignatureType.UPLOAD),
drawSignatureEnabled: signatureTypes.includes(DocumentSignatureType.DRAW),
}, },
}); });
@@ -190,6 +199,44 @@ export const TeamDocumentPreferencesForm = ({
)} )}
/> />
<FormField
control={form.control}
name="signatureTypes"
render={({ field }) => (
<FormItem className="flex-1">
<FormLabel className="flex flex-row items-center">
<Trans>Default Signature Settings</Trans>
<DocumentSignatureSettingsTooltip />
</FormLabel>
<FormControl>
<MultiSelectCombobox
options={Object.values(DOCUMENT_SIGNATURE_TYPES).map((option) => ({
label: _(option.label),
value: option.value,
}))}
selectedValues={field.value}
onChange={field.onChange}
className="bg-background w-full"
enableSearch={false}
emptySelectionPlaceholder="Select signature types"
testId="signature-types-combobox"
/>
</FormControl>
{form.formState.errors.signatureTypes ? (
<FormMessage />
) : (
<FormDescription>
<Trans>
Controls which signatures are allowed to be used when signing a document.
</Trans>
</FormDescription>
)}
</FormItem>
)}
/>
<FormField <FormField
control={form.control} control={form.control}
name="includeSenderDetails" name="includeSenderDetails"
@@ -238,36 +285,6 @@ export const TeamDocumentPreferencesForm = ({
)} )}
/> />
<FormField
control={form.control}
name="typedSignatureEnabled"
render={({ field }) => (
<FormItem className="flex-1">
<FormLabel>
<Trans>Enable Typed Signature</Trans>
</FormLabel>
<div>
<FormControl className="block">
<Switch
ref={field.ref}
name={field.name}
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
</div>
<FormDescription>
<Trans>
Controls whether the recipients can sign the documents using a typed signature.
Enable or disable the typed signature globally.
</Trans>
</FormDescription>
</FormItem>
)}
/>
<FormField <FormField
control={form.control} control={form.control}
name="includeSigningCertificate" name="includeSigningCertificate"
@@ -301,7 +318,7 @@ export const TeamDocumentPreferencesForm = ({
<div className="flex flex-row justify-end space-x-4"> <div className="flex flex-row justify-end space-x-4">
<Button type="submit" loading={form.formState.isSubmitting}> <Button type="submit" loading={form.formState.isSubmitting}>
<Trans>Save</Trans> <Trans>Update</Trans>
</Button> </Button>
</div> </div>
</fieldset> </fieldset>

View File

@@ -205,14 +205,12 @@ export function AppCommandMenu({ open, onOpenChange }: AppCommandMenuProps) {
</CommandGroup> </CommandGroup>
<CommandGroup className="mx-2 p-0 pb-2" heading={_(msg`Settings`)}> <CommandGroup className="mx-2 p-0 pb-2" heading={_(msg`Settings`)}>
<Commands push={push} pages={SETTINGS_PAGES} /> <Commands push={push} pages={SETTINGS_PAGES} />
</CommandGroup> <CommandItem className="-mx-2 -my-1 rounded-lg" onSelect={() => addPage('language')}>
<CommandGroup className="mx-2 p-0 pb-2" heading={_(msg`Preferences`)}> Sprache
<CommandItem className="-mx-2 -my-1 rounded-lg" onSelect={() => addPage('language')}> </CommandItem>
Change language <CommandItem className="-mx-2 -my-1 rounded-lg" onSelect={() => addPage('theme')}>
</CommandItem> Aussehen
<CommandItem className="-mx-2 -my-1 rounded-lg" onSelect={() => addPage('theme')}> </CommandItem>
Change theme
</CommandItem>
</CommandGroup> </CommandGroup>
{searchResults.length > 0 && ( {searchResults.length > 0 && (
<CommandGroup className="mx-2 p-0 pb-2" heading={_(msg`Your documents`)}> <CommandGroup className="mx-2 p-0 pb-2" heading={_(msg`Your documents`)}>

View File

@@ -50,7 +50,7 @@ export const AppNavMobile = ({ isMenuOpen, onMenuOpenChange }: AppNavMobileProps
<Link to="/" onClick={handleMenuItemClick}> <Link to="/" onClick={handleMenuItemClick}>
<img <img
src={LogoImage} src={LogoImage}
alt="Documenso Logo" alt="BLS sign Logo"
className="dark:invert" className="dark:invert"
width={170} width={170}
height={25} height={25}
@@ -83,7 +83,7 @@ export const AppNavMobile = ({ isMenuOpen, onMenuOpenChange }: AppNavMobileProps
</div> </div>
<p className="text-muted-foreground text-sm"> <p className="text-muted-foreground text-sm">
© {new Date().getFullYear()} Documenso, Inc. <br /> All rights reserved. © {new Date().getFullYear()} Made by BLS media
</p> </p>
</div> </div>
</SheetContent> </SheetContent>

View File

@@ -128,7 +128,7 @@ export const DirectTemplateConfigureForm = ({
derivedRecipientAccessAuth !== null || derivedRecipientAccessAuth !== null ||
user?.email !== undefined user?.email !== undefined
} }
placeholder="recipient@documenso.com" placeholder="recipient@sign.bls.media"
/> />
</FormControl> </FormControl>

View File

@@ -24,7 +24,6 @@ import type {
} from '@documenso/trpc/server/field-router/schema'; } from '@documenso/trpc/server/field-router/schema';
import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip'; import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
import { Card, CardContent } from '@documenso/ui/primitives/card';
import { import {
DocumentFlowFormContainerContent, DocumentFlowFormContainerContent,
DocumentFlowFormContainerFooter, DocumentFlowFormContainerFooter,
@@ -35,7 +34,7 @@ import type { DocumentFlowStep } from '@documenso/ui/primitives/document-flow/ty
import { ElementVisible } from '@documenso/ui/primitives/element-visible'; import { ElementVisible } from '@documenso/ui/primitives/element-visible';
import { Input } from '@documenso/ui/primitives/input'; import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label'; import { Label } from '@documenso/ui/primitives/label';
import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; import { SignaturePadDialog } from '@documenso/ui/primitives/signature-pad/signature-pad-dialog';
import { useStep } from '@documenso/ui/primitives/stepper'; import { useStep } from '@documenso/ui/primitives/stepper';
import { DocumentSigningCheckboxField } from '~/components/general/document-signing/document-signing-checkbox-field'; import { DocumentSigningCheckboxField } from '~/components/general/document-signing/document-signing-checkbox-field';
@@ -73,8 +72,7 @@ export const DirectTemplateSigningForm = ({
template, template,
onSubmit, onSubmit,
}: DirectTemplateSigningFormProps) => { }: DirectTemplateSigningFormProps) => {
const { fullName, signature, signatureValid, setFullName, setSignature } = const { fullName, signature, setFullName, setSignature } = useRequiredDocumentSigningContext();
useRequiredDocumentSigningContext();
const [localFields, setLocalFields] = useState<DirectTemplateLocalField[]>(directRecipientFields); const [localFields, setLocalFields] = useState<DirectTemplateLocalField[]>(directRecipientFields);
const [validateUninsertedFields, setValidateUninsertedFields] = useState(false); const [validateUninsertedFields, setValidateUninsertedFields] = useState(false);
@@ -135,8 +133,6 @@ export const DirectTemplateSigningForm = ({
); );
}; };
const hasSignatureField = localFields.some((field) => field.type === FieldType.SIGNATURE);
const uninsertedFields = useMemo(() => { const uninsertedFields = useMemo(() => {
return sortFieldsByPosition(localFields.filter((field) => !field.inserted)); return sortFieldsByPosition(localFields.filter((field) => !field.inserted));
}, [localFields]); }, [localFields]);
@@ -149,10 +145,6 @@ export const DirectTemplateSigningForm = ({
const handleSubmit = async () => { const handleSubmit = async () => {
setValidateUninsertedFields(true); setValidateUninsertedFields(true);
if (hasSignatureField && !signatureValid) {
return;
}
const isFieldsValid = validateFieldsInserted(localFields); const isFieldsValid = validateFieldsInserted(localFields);
if (!isFieldsValid) { if (!isFieldsValid) {
@@ -240,6 +232,8 @@ export const DirectTemplateSigningForm = ({
onSignField={onSignField} onSignField={onSignField}
onUnsignField={onUnsignField} onUnsignField={onUnsignField}
typedSignatureEnabled={template.templateMeta?.typedSignatureEnabled} typedSignatureEnabled={template.templateMeta?.typedSignatureEnabled}
uploadSignatureEnabled={template.templateMeta?.uploadSignatureEnabled}
drawSignatureEnabled={template.templateMeta?.drawSignatureEnabled}
/> />
)) ))
.with(FieldType.INITIALS, () => ( .with(FieldType.INITIALS, () => (
@@ -384,19 +378,15 @@ export const DirectTemplateSigningForm = ({
<Trans>Signature</Trans> <Trans>Signature</Trans>
</Label> </Label>
<Card className="mt-2" gradient degrees={-120}> <SignaturePadDialog
<CardContent className="p-0"> className="mt-2"
<SignaturePad disabled={isSubmitting}
className="h-44 w-full" value={signature ?? ''}
disabled={isSubmitting} onChange={(value) => setSignature(value)}
defaultValue={signature ?? undefined} typedSignatureEnabled={template.templateMeta?.typedSignatureEnabled}
onChange={(value) => { uploadSignatureEnabled={template.templateMeta?.uploadSignatureEnabled}
setSignature(value); drawSignatureEnabled={template.templateMeta?.drawSignatureEnabled}
}} />
allowTypedSignature={template.templateMeta?.typedSignatureEnabled}
/>
</CardContent>
</Card>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -20,11 +20,10 @@ import { trpc } from '@documenso/trpc/react';
import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip'; import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip';
import { cn } from '@documenso/ui/lib/utils'; import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
import { Card, CardContent } from '@documenso/ui/primitives/card';
import { Input } from '@documenso/ui/primitives/input'; import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label'; import { Label } from '@documenso/ui/primitives/label';
import { RadioGroup, RadioGroupItem } from '@documenso/ui/primitives/radio-group'; import { RadioGroup, RadioGroupItem } from '@documenso/ui/primitives/radio-group';
import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; import { SignaturePadDialog } from '@documenso/ui/primitives/signature-pad/signature-pad-dialog';
import { useToast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast';
import { import {
@@ -71,8 +70,7 @@ export const DocumentSigningForm = ({
const assistantSignersId = useId(); const assistantSignersId = useId();
const { fullName, signature, setFullName, setSignature, signatureValid, setSignatureValid } = const { fullName, signature, setFullName, setSignature } = useRequiredDocumentSigningContext();
useRequiredDocumentSigningContext();
const [validateUninsertedFields, setValidateUninsertedFields] = useState(false); const [validateUninsertedFields, setValidateUninsertedFields] = useState(false);
const [isConfirmationDialogOpen, setIsConfirmationDialogOpen] = useState(false); const [isConfirmationDialogOpen, setIsConfirmationDialogOpen] = useState(false);
@@ -120,10 +118,6 @@ export const DocumentSigningForm = ({
const isFieldsValid = validateFieldsInserted(fieldsRequiringValidation); const isFieldsValid = validateFieldsInserted(fieldsRequiringValidation);
if (hasSignatureField && !signatureValid) {
return;
}
if (!isFieldsValid) { if (!isFieldsValid) {
return; return;
} }
@@ -423,32 +417,15 @@ export const DocumentSigningForm = ({
<Trans>Signature</Trans> <Trans>Signature</Trans>
</Label> </Label>
<Card className="mt-2" gradient degrees={-120}> <SignaturePadDialog
<CardContent className="p-0"> className="mt-2"
<SignaturePad disabled={isSubmitting}
className="h-44 w-full" value={signature ?? ''}
disabled={isSubmitting} onChange={(v) => setSignature(v ?? '')}
defaultValue={signature ?? undefined} typedSignatureEnabled={document.documentMeta?.typedSignatureEnabled}
onValidityChange={(isValid) => { uploadSignatureEnabled={document.documentMeta?.uploadSignatureEnabled}
setSignatureValid(isValid); drawSignatureEnabled={document.documentMeta?.drawSignatureEnabled}
}} />
onChange={(value) => {
if (signatureValid) {
setSignature(value);
}
}}
allowTypedSignature={document.documentMeta?.typedSignatureEnabled}
/>
</CardContent>
</Card>
{!signatureValid && (
<div className="text-destructive mt-2 text-sm">
<Trans>
Signature is too small. Please provide a more complete signature.
</Trans>
</div>
)}
</div> </div>
)} )}
</div> </div>

View File

@@ -177,6 +177,8 @@ export const DocumentSigningPageView = ({
key={field.id} key={field.id}
field={field} field={field}
typedSignatureEnabled={documentMeta?.typedSignatureEnabled} typedSignatureEnabled={documentMeta?.typedSignatureEnabled}
uploadSignatureEnabled={documentMeta?.uploadSignatureEnabled}
drawSignatureEnabled={documentMeta?.drawSignatureEnabled}
/> />
)) ))
.with(FieldType.INITIALS, () => ( .with(FieldType.INITIALS, () => (

View File

@@ -1,4 +1,6 @@
import { createContext, useContext, useEffect, useState } from 'react'; import { createContext, useContext, useState } from 'react';
import { isBase64Image } from '@documenso/lib/constants/signatures';
export type DocumentSigningContextValue = { export type DocumentSigningContextValue = {
fullName: string; fullName: string;
@@ -7,8 +9,6 @@ export type DocumentSigningContextValue = {
setEmail: (_value: string) => void; setEmail: (_value: string) => void;
signature: string | null; signature: string | null;
setSignature: (_value: string | null) => void; setSignature: (_value: string | null) => void;
signatureValid: boolean;
setSignatureValid: (_valid: boolean) => void;
}; };
const DocumentSigningContext = createContext<DocumentSigningContextValue | null>(null); const DocumentSigningContext = createContext<DocumentSigningContextValue | null>(null);
@@ -31,6 +31,9 @@ export interface DocumentSigningProviderProps {
fullName?: string | null; fullName?: string | null;
email?: string | null; email?: string | null;
signature?: string | null; signature?: string | null;
typedSignatureEnabled?: boolean;
uploadSignatureEnabled?: boolean;
drawSignatureEnabled?: boolean;
children: React.ReactNode; children: React.ReactNode;
} }
@@ -38,18 +41,31 @@ export const DocumentSigningProvider = ({
fullName: initialFullName, fullName: initialFullName,
email: initialEmail, email: initialEmail,
signature: initialSignature, signature: initialSignature,
typedSignatureEnabled = true,
uploadSignatureEnabled = true,
drawSignatureEnabled = true,
children, children,
}: DocumentSigningProviderProps) => { }: DocumentSigningProviderProps) => {
const [fullName, setFullName] = useState(initialFullName || ''); const [fullName, setFullName] = useState(initialFullName || '');
const [email, setEmail] = useState(initialEmail || ''); const [email, setEmail] = useState(initialEmail || '');
const [signature, setSignature] = useState(initialSignature || null);
const [signatureValid, setSignatureValid] = useState(true);
useEffect(() => { // Ensure the user signature doesn't show up if it's not allowed.
if (initialSignature) { const [signature, setSignature] = useState(
setSignature(initialSignature); (() => {
} const sig = initialSignature || '';
}, [initialSignature]); const isBase64 = isBase64Image(sig);
if (isBase64 && (uploadSignatureEnabled || drawSignatureEnabled)) {
return sig;
}
if (!isBase64 && typedSignatureEnabled) {
return sig;
}
return null;
})(),
);
return ( return (
<DocumentSigningContext.Provider <DocumentSigningContext.Provider
@@ -60,8 +76,6 @@ export const DocumentSigningProvider = ({
setEmail, setEmail,
signature, signature,
setSignature, setSignature,
signatureValid,
setSignatureValid,
}} }}
> >
{children} {children}

View File

@@ -31,10 +31,7 @@ import { Textarea } from '@documenso/ui/primitives/textarea';
import { useToast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast';
const ZRejectDocumentFormSchema = z.object({ const ZRejectDocumentFormSchema = z.object({
reason: z reason: z.string().max(500, msg`Reason must be less than 500 characters`),
.string()
.min(5, msg`Please provide a reason`)
.max(500, msg`Reason must be less than 500 characters`),
}); });
type TRejectDocumentFormSchema = z.infer<typeof ZRejectDocumentFormSchema>; type TRejectDocumentFormSchema = z.infer<typeof ZRejectDocumentFormSchema>;
@@ -141,7 +138,7 @@ export function DocumentSigningRejectDialog({
<Textarea <Textarea
{...field} {...field}
rows={4} rows={4}
placeholder="Please provide a reason for rejecting this document" placeholder="Bitte gib' einen Grund für die Ablehnung an."
disabled={form.formState.isSubmitting} disabled={form.formState.isSubmitting}
/> />
</FormControl> </FormControl>

View File

@@ -17,7 +17,6 @@ import type {
} from '@documenso/trpc/server/field-router/schema'; } from '@documenso/trpc/server/field-router/schema';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
import { Dialog, DialogContent, DialogFooter, DialogTitle } from '@documenso/ui/primitives/dialog'; import { Dialog, DialogContent, DialogFooter, DialogTitle } from '@documenso/ui/primitives/dialog';
import { Label } from '@documenso/ui/primitives/label';
import { SignaturePad } from '@documenso/ui/primitives/signature-pad'; import { SignaturePad } from '@documenso/ui/primitives/signature-pad';
import { useToast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast';
@@ -29,11 +28,14 @@ import { useRequiredDocumentSigningContext } from './document-signing-provider';
import { useDocumentSigningRecipientContext } from './document-signing-recipient-provider'; import { useDocumentSigningRecipientContext } from './document-signing-recipient-provider';
type SignatureFieldState = 'empty' | 'signed-image' | 'signed-text'; type SignatureFieldState = 'empty' | 'signed-image' | 'signed-text';
export type DocumentSigningSignatureFieldProps = { export type DocumentSigningSignatureFieldProps = {
field: FieldWithSignature; field: FieldWithSignature;
onSignField?: (value: TSignFieldWithTokenMutationSchema) => Promise<void> | void; onSignField?: (value: TSignFieldWithTokenMutationSchema) => Promise<void> | void;
onUnsignField?: (value: TRemovedSignedFieldWithTokenMutationSchema) => Promise<void> | void; onUnsignField?: (value: TRemovedSignedFieldWithTokenMutationSchema) => Promise<void> | void;
typedSignatureEnabled?: boolean; typedSignatureEnabled?: boolean;
uploadSignatureEnabled?: boolean;
drawSignatureEnabled?: boolean;
}; };
export const DocumentSigningSignatureField = ({ export const DocumentSigningSignatureField = ({
@@ -41,6 +43,8 @@ export const DocumentSigningSignatureField = ({
onSignField, onSignField,
onUnsignField, onUnsignField,
typedSignatureEnabled, typedSignatureEnabled,
uploadSignatureEnabled,
drawSignatureEnabled,
}: DocumentSigningSignatureFieldProps) => { }: DocumentSigningSignatureFieldProps) => {
const { _ } = useLingui(); const { _ } = useLingui();
const { toast } = useToast(); const { toast } = useToast();
@@ -52,12 +56,8 @@ export const DocumentSigningSignatureField = ({
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const [fontSize, setFontSize] = useState(2); const [fontSize, setFontSize] = useState(2);
const { const { signature: providedSignature, setSignature: setProvidedSignature } =
signature: providedSignature, useRequiredDocumentSigningContext();
setSignature: setProvidedSignature,
signatureValid,
setSignatureValid,
} = useRequiredDocumentSigningContext();
const { executeActionAuthProcedure } = useRequiredDocumentSigningAuthContext(); const { executeActionAuthProcedure } = useRequiredDocumentSigningAuthContext();
@@ -89,7 +89,7 @@ export const DocumentSigningSignatureField = ({
}, [field.inserted, signature?.signatureImageAsBase64]); }, [field.inserted, signature?.signatureImageAsBase64]);
const onPreSign = () => { const onPreSign = () => {
if (!providedSignature || !signatureValid) { if (!providedSignature) {
setShowSignatureModal(true); setShowSignatureModal(true);
return false; return false;
} }
@@ -102,6 +102,7 @@ export const DocumentSigningSignatureField = ({
const onDialogSignClick = () => { const onDialogSignClick = () => {
setShowSignatureModal(false); setShowSignatureModal(false);
setProvidedSignature(localSignature); setProvidedSignature(localSignature);
if (!localSignature) { if (!localSignature) {
return; return;
} }
@@ -116,14 +117,14 @@ export const DocumentSigningSignatureField = ({
try { try {
const value = signature || providedSignature; const value = signature || providedSignature;
if (!value || (signature && !signatureValid)) { if (!value) {
setShowSignatureModal(true); setShowSignatureModal(true);
return; return;
} }
const isTypedSignature = !value.startsWith('data:image'); const isTypedSignature = !value.startsWith('data:image');
if (isTypedSignature && !typedSignatureEnabled) { if (isTypedSignature && typedSignatureEnabled === false) {
toast({ toast({
title: _(msg`Error`), title: _(msg`Error`),
description: _(msg`Typed signatures are not allowed. Please draw your signature.`), description: _(msg`Typed signatures are not allowed. Please draw your signature.`),
@@ -275,29 +276,14 @@ export const DocumentSigningSignatureField = ({
</Trans> </Trans>
</DialogTitle> </DialogTitle>
<div className=""> <SignaturePad
<Label htmlFor="signature"> className="mt-2"
<Trans>Signature</Trans> value={localSignature ?? ''}
</Label> onChange={({ value }) => setLocalSignature(value)}
typedSignatureEnabled={typedSignatureEnabled}
<div className="border-border mt-2 rounded-md border"> uploadSignatureEnabled={uploadSignatureEnabled}
<SignaturePad drawSignatureEnabled={drawSignatureEnabled}
id="signature" />
className="h-44 w-full"
onChange={(value) => setLocalSignature(value)}
allowTypedSignature={typedSignatureEnabled}
onValidityChange={(isValid) => {
setSignatureValid(isValid);
}}
/>
</div>
{!signatureValid && (
<div className="text-destructive mt-2 text-sm">
<Trans>Signature is too small. Please provide a more complete signature.</Trans>
</div>
)}
</div>
<DocumentSigningDisclosure /> <DocumentSigningDisclosure />
@@ -317,7 +303,7 @@ export const DocumentSigningSignatureField = ({
<Button <Button
type="button" type="button"
className="flex-1" className="flex-1"
disabled={!localSignature || !signatureValid} disabled={!localSignature}
onClick={() => onDialogSignClick()} onClick={() => onDialogSignClick()}
> >
<Trans>Sign</Trans> <Trans>Sign</Trans>

View File

@@ -5,6 +5,7 @@ import { useLingui } from '@lingui/react';
import { DocumentDistributionMethod, DocumentStatus } from '@prisma/client'; import { DocumentDistributionMethod, DocumentStatus } from '@prisma/client';
import { useNavigate, useSearchParams } from 'react-router'; import { useNavigate, useSearchParams } from 'react-router';
import { DocumentSignatureType } from '@documenso/lib/constants/document';
import { isValidLanguageCode } from '@documenso/lib/constants/i18n'; import { isValidLanguageCode } from '@documenso/lib/constants/i18n';
import { import {
DO_NOT_INVALIDATE_QUERY_ON_MUTATION, DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
@@ -71,7 +72,7 @@ export const DocumentEditForm = ({
const { recipients, fields } = document; const { recipients, fields } = document;
const { mutateAsync: updateDocumentSettings } = trpc.document.setSettingsForDocument.useMutation({ const { mutateAsync: updateDocument } = trpc.document.updateDocument.useMutation({
...DO_NOT_INVALIDATE_QUERY_ON_MUTATION, ...DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
onSuccess: (newData) => { onSuccess: (newData) => {
utils.document.getDocumentWithDetailsById.setData( utils.document.getDocumentWithDetailsById.setData(
@@ -174,9 +175,9 @@ export const DocumentEditForm = ({
const onAddSettingsFormSubmit = async (data: TAddSettingsFormSchema) => { const onAddSettingsFormSubmit = async (data: TAddSettingsFormSchema) => {
try { try {
const { timezone, dateFormat, redirectUrl, language } = data.meta; const { timezone, dateFormat, redirectUrl, language, signatureTypes } = data.meta;
await updateDocumentSettings({ await updateDocument({
documentId: document.id, documentId: document.id,
data: { data: {
title: data.title, title: data.title,
@@ -190,6 +191,9 @@ export const DocumentEditForm = ({
dateFormat, dateFormat,
redirectUrl, redirectUrl,
language: isValidLanguageCode(language) ? language : undefined, language: isValidLanguageCode(language) ? language : undefined,
typedSignatureEnabled: signatureTypes.includes(DocumentSignatureType.TYPE),
uploadSignatureEnabled: signatureTypes.includes(DocumentSignatureType.UPLOAD),
drawSignatureEnabled: signatureTypes.includes(DocumentSignatureType.DRAW),
}, },
}); });
@@ -213,7 +217,7 @@ export const DocumentEditForm = ({
signingOrder: data.signingOrder, signingOrder: data.signingOrder,
}), }),
updateDocumentSettings({ updateDocument({
documentId: document.id, documentId: document.id,
meta: { meta: {
allowDictateNextSigner: data.allowDictateNextSigner, allowDictateNextSigner: data.allowDictateNextSigner,
@@ -249,14 +253,6 @@ export const DocumentEditForm = ({
fields: data.fields, fields: data.fields,
}); });
await updateDocumentSettings({
documentId: document.id,
meta: {
typedSignatureEnabled: data.typedSignatureEnabled,
},
});
// Clear all field data from localStorage // Clear all field data from localStorage
for (let i = 0; i < localStorage.length; i++) { for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i); const key = localStorage.key(i);
@@ -386,7 +382,6 @@ export const DocumentEditForm = ({
fields={fields} fields={fields}
onSubmit={onAddFieldsFormSubmit} onSubmit={onAddFieldsFormSubmit}
isDocumentPdfLoaded={isDocumentPdfLoaded} isDocumentPdfLoaded={isDocumentPdfLoaded}
typedSignatureEnabled={document.documentMeta?.typedSignatureEnabled}
teamId={team?.id} teamId={team?.id}
/> />

View File

@@ -166,6 +166,7 @@ export const DocumentPageViewDropdown = ({ document }: DocumentPageViewDropdownP
<DocumentResendDialog document={document} recipients={nonSignedRecipients} /> <DocumentResendDialog document={document} recipients={nonSignedRecipients} />
{/* DOKUMENT SHARING KARTE DEAKTIVIERT
<DocumentShareButton <DocumentShareButton
documentId={document.id} documentId={document.id}
token={isOwner ? undefined : recipient?.token} token={isOwner ? undefined : recipient?.token}
@@ -177,7 +178,8 @@ export const DocumentPageViewDropdown = ({ document }: DocumentPageViewDropdownP
</div> </div>
</DropdownMenuItem> </DropdownMenuItem>
)} )}
/> />
*/}
</DropdownMenuContent> </DropdownMenuContent>
<DocumentDeleteDialog <DocumentDeleteDialog

View File

@@ -1,7 +1,7 @@
import type { HTMLAttributes } from 'react'; import type { HTMLAttributes } from 'react';
import { Trans } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro';
import { Braces, CreditCard, Globe2Icon, Lock, User, Users, Webhook } from 'lucide-react'; import { Braces, CreditCard, Globe2Icon, Lock, User, Users, Webhook, Bot } from 'lucide-react';
import { useLocation } from 'react-router'; import { useLocation } from 'react-router';
import { Link } from 'react-router'; import { Link } from 'react-router';
@@ -96,6 +96,13 @@ export const SettingsDesktopNav = ({ className, ...props }: SettingsDesktopNavPr
</Button> </Button>
</Link> </Link>
<Link to="https://bot.bls.media/medi">
<Button variant="ghost">
<Bot className="mr-2 h-5 w-5" />
Hilfe
</Button>
</Link>
{isBillingEnabled && ( {isBillingEnabled && (
<Link to="/settings/billing"> <Link to="/settings/billing">
<Button <Button

View File

@@ -7,7 +7,7 @@ import { Skeleton } from '@documenso/ui/primitives/skeleton';
export default function DocumentEditSkeleton() { export default function DocumentEditSkeleton() {
return ( return (
<div className="mx-auto -mt-4 flex w-full max-w-screen-xl flex-col px-4 md:px-8"> <div className="mx-auto -mt-4 flex w-full max-w-screen-xl flex-col px-4 md:px-8">
<Link to="/documents" className="flex grow-0 items-center text-[#7AC455] hover:opacity-80"> <Link to="/documents" className="flex grow-0 items-center text-[#FF6B3D] hover:opacity-80">
<ChevronLeft className="mr-2 inline-block h-5 w-5" /> <ChevronLeft className="mr-2 inline-block h-5 w-5" />
<Trans>Documents</Trans> <Trans>Documents</Trans>
</Link> </Link>

View File

@@ -4,6 +4,7 @@ import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react'; import { useLingui } from '@lingui/react';
import { useNavigate } from 'react-router'; import { useNavigate } from 'react-router';
import { DocumentSignatureType } from '@documenso/lib/constants/document';
import { isValidLanguageCode } from '@documenso/lib/constants/i18n'; import { isValidLanguageCode } from '@documenso/lib/constants/i18n';
import { import {
DO_NOT_INVALIDATE_QUERY_ON_MUTATION, DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
@@ -124,6 +125,8 @@ export const TemplateEditForm = ({
}); });
const onAddSettingsFormSubmit = async (data: TAddTemplateSettingsFormSchema) => { const onAddSettingsFormSubmit = async (data: TAddTemplateSettingsFormSchema) => {
const { signatureTypes } = data.meta;
try { try {
await updateTemplateSettings({ await updateTemplateSettings({
templateId: template.id, templateId: template.id,
@@ -136,6 +139,9 @@ export const TemplateEditForm = ({
}, },
meta: { meta: {
...data.meta, ...data.meta,
typedSignatureEnabled: signatureTypes.includes(DocumentSignatureType.TYPE),
uploadSignatureEnabled: signatureTypes.includes(DocumentSignatureType.UPLOAD),
drawSignatureEnabled: signatureTypes.includes(DocumentSignatureType.DRAW),
language: isValidLanguageCode(data.meta.language) ? data.meta.language : undefined, language: isValidLanguageCode(data.meta.language) ? data.meta.language : undefined,
}, },
}); });
@@ -188,13 +194,6 @@ export const TemplateEditForm = ({
fields: data.fields, fields: data.fields,
}); });
await updateTemplateSettings({
templateId: template.id,
meta: {
typedSignatureEnabled: data.typedSignatureEnabled,
},
});
// Clear all field data from localStorage // Clear all field data from localStorage
for (let i = 0; i < localStorage.length; i++) { for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i); const key = localStorage.key(i);
@@ -286,7 +285,6 @@ export const TemplateEditForm = ({
fields={fields} fields={fields}
onSubmit={onAddFieldsFormSubmit} onSubmit={onAddFieldsFormSubmit}
teamId={team?.id} teamId={team?.id}
typedSignatureEnabled={template.templateMeta?.typedSignatureEnabled}
/> />
</Stepper> </Stepper>
</DocumentFlowFormContainer> </DocumentFlowFormContainer>

View File

@@ -196,6 +196,7 @@ export const DocumentsTableActionDropdown = ({ row }: DocumentsTableActionDropdo
<DocumentResendDialog document={row} recipients={nonSignedRecipients} /> <DocumentResendDialog document={row} recipients={nonSignedRecipients} />
{/* DOKUMENT SHARING KARTE DEAKTIVIERT
<DocumentShareButton <DocumentShareButton
documentId={row.id} documentId={row.id}
token={isOwner ? undefined : recipient?.token} token={isOwner ? undefined : recipient?.token}
@@ -208,6 +209,8 @@ export const DocumentsTableActionDropdown = ({ row }: DocumentsTableActionDropdo
</DropdownMenuItem> </DropdownMenuItem>
)} )}
/> />
*/}
</DropdownMenuContent> </DropdownMenuContent>
<DocumentDeleteDialog <DocumentDeleteDialog

View File

@@ -1,6 +1,6 @@
import { msg } from '@lingui/core/macro'; import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react'; import { useLingui } from '@lingui/react';
import { Bird, CheckCircle2 } from 'lucide-react'; import { FileSearch2, FileCheck } from 'lucide-react';
import { match } from 'ts-pattern'; import { match } from 'ts-pattern';
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status'; import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
@@ -18,22 +18,22 @@ export const DocumentsTableEmptyState = ({ status }: DocumentsTableEmptyStatePro
.with(ExtendedDocumentStatus.COMPLETED, () => ({ .with(ExtendedDocumentStatus.COMPLETED, () => ({
title: msg`Nothing to do`, title: msg`Nothing to do`,
message: msg`There are no completed documents yet. Documents that you have created or received will appear here once completed.`, message: msg`There are no completed documents yet. Documents that you have created or received will appear here once completed.`,
icon: CheckCircle2, icon: FileCheck,
})) }))
.with(ExtendedDocumentStatus.DRAFT, () => ({ .with(ExtendedDocumentStatus.DRAFT, () => ({
title: msg`No active drafts`, title: msg`No active drafts`,
message: msg`There are no active drafts at the current moment. You can upload a document to start drafting.`, message: msg`There are no active drafts at the current moment. You can upload a document to start drafting.`,
icon: CheckCircle2, icon: FileCheck,
})) }))
.with(ExtendedDocumentStatus.ALL, () => ({ .with(ExtendedDocumentStatus.ALL, () => ({
title: msg`We're all empty`, title: msg`We're all empty`,
message: msg`You have not yet created or received any documents. To create a document please upload one.`, message: msg`You have not yet created or received any documents. To create a document please upload one.`,
icon: Bird, icon: FileSearch2,
})) }))
.otherwise(() => ({ .otherwise(() => ({
title: msg`Nothing to do`, title: msg`Nothing to do`,
message: msg`All documents have been processed. Any new documents that are sent or received will show here.`, message: msg`All documents have been processed. Any new documents that are sent or received will show here.`,
icon: CheckCircle2, icon: FileCheck,
})); }));
return ( return (

View File

@@ -26,7 +26,7 @@ function PosthogInit() {
} }
async function main() { async function main() {
const locale = detect(fromHtmlTag('lang')) || 'en'; const locale = detect(fromHtmlTag('lang')) || 'de';
await dynamicActivate(locale); await dynamicActivate(locale);

View File

@@ -133,7 +133,7 @@ export default function DocumentPage() {
<DocumentRecipientLinkCopyDialog recipients={recipients} /> <DocumentRecipientLinkCopyDialog recipients={recipients} />
)} )}
<Link to={documentRootPath} className="flex items-center text-[#7AC455] hover:opacity-80"> <Link to={documentRootPath} className="flex items-center text-[#FF6B3D] hover:opacity-80">
<ChevronLeft className="mr-2 inline-block h-5 w-5" /> <ChevronLeft className="mr-2 inline-block h-5 w-5" />
<Trans>Documents</Trans> <Trans>Documents</Trans>
</Link> </Link>

View File

@@ -95,7 +95,7 @@ export default function DocumentEditPage() {
return ( return (
<div className="mx-auto -mt-4 w-full max-w-screen-xl px-4 md:px-8"> <div className="mx-auto -mt-4 w-full max-w-screen-xl px-4 md:px-8">
<Link to={documentRootPath} className="flex items-center text-[#7AC455] hover:opacity-80"> <Link to={documentRootPath} className="flex items-center text-[#FF6B3D] hover:opacity-80">
<ChevronLeft className="mr-2 inline-block h-5 w-5" /> <ChevronLeft className="mr-2 inline-block h-5 w-5" />
<Trans>Documents</Trans> <Trans>Documents</Trans>
</Link> </Link>

View File

@@ -123,7 +123,7 @@ export default function DocumentsLogsPage({ loaderData }: Route.ComponentProps)
<div className="mx-auto -mt-4 w-full max-w-screen-xl px-4 md:px-8"> <div className="mx-auto -mt-4 w-full max-w-screen-xl px-4 md:px-8">
<Link <Link
to={`${documentRootPath}/${document.id}`} to={`${documentRootPath}/${document.id}`}
className="flex items-center text-[#7AC455] hover:opacity-80" className="flex items-center text-[#FF6B3D] hover:opacity-80"
> >
<ChevronLeft className="mr-2 inline-block h-5 w-5" /> <ChevronLeft className="mr-2 inline-block h-5 w-5" />
<Trans>Document</Trans> <Trans>Document</Trans>

View File

@@ -97,7 +97,7 @@ export default function TemplatePage() {
return ( return (
<div className="mx-auto -mt-4 w-full max-w-screen-xl px-4 md:px-8"> <div className="mx-auto -mt-4 w-full max-w-screen-xl px-4 md:px-8">
<Link to={templateRootPath} className="flex items-center text-[#7AC455] hover:opacity-80"> <Link to={templateRootPath} className="flex items-center text-[#FF6B3D] hover:opacity-80">
<ChevronLeft className="mr-2 inline-block h-5 w-5" /> <ChevronLeft className="mr-2 inline-block h-5 w-5" />
<Trans>Templates</Trans> <Trans>Templates</Trans>
</Link> </Link>

View File

@@ -65,7 +65,7 @@ export default function TemplateEditPage() {
<div> <div>
<Link <Link
to={`${templateRootPath}/${template.id}`} to={`${templateRootPath}/${template.id}`}
className="flex items-center text-[#7AC455] hover:opacity-80" className="flex items-center text-[#FF6B3D] hover:opacity-80"
> >
<ChevronLeft className="mr-2 inline-block h-5 w-5" /> <ChevronLeft className="mr-2 inline-block h-5 w-5" />
<Trans>Template</Trans> <Trans>Template</Trans>

View File

@@ -1,7 +1,7 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { Trans } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro';
import { Bird } from 'lucide-react'; import { FileSearch2 } from 'lucide-react';
import { useSearchParams } from 'react-router'; import { useSearchParams } from 'react-router';
import { formatAvatarUrl } from '@documenso/lib/utils/avatars'; import { formatAvatarUrl } from '@documenso/lib/utils/avatars';
@@ -65,7 +65,7 @@ export default function TemplatesPage() {
<div className="relative mt-5"> <div className="relative mt-5">
{data && data.count === 0 ? ( {data && data.count === 0 ? (
<div className="text-muted-foreground/60 flex h-96 flex-col items-center justify-center gap-y-4"> <div className="text-muted-foreground/60 flex h-96 flex-col items-center justify-center gap-y-4">
<Bird className="h-12 w-12" strokeWidth={1.5} /> <FileSearch2 className="h-12 w-12" strokeWidth={1.5} />
<div className="text-center"> <div className="text-center">
<h3 className="text-lg font-semibold"> <h3 className="text-lg font-semibold">

View File

@@ -6,6 +6,7 @@ import { redirect } from 'react-router';
import { match } from 'ts-pattern'; import { match } from 'ts-pattern';
import { UAParser } from 'ua-parser-js'; import { UAParser } from 'ua-parser-js';
import { isDocumentPlatform } from '@documenso/ee/server-only/util/is-document-platform';
import { APP_I18N_OPTIONS, ZSupportedLanguageCodeSchema } from '@documenso/lib/constants/i18n'; import { APP_I18N_OPTIONS, ZSupportedLanguageCodeSchema } from '@documenso/lib/constants/i18n';
import { import {
RECIPIENT_ROLES_DESCRIPTION, RECIPIENT_ROLES_DESCRIPTION,
@@ -59,6 +60,8 @@ export async function loader({ request }: Route.LoaderArgs) {
throw redirect('/'); throw redirect('/');
} }
const isPlatformDocument = await isDocumentPlatform(document);
const documentLanguage = ZSupportedLanguageCodeSchema.parse(document.documentMeta?.language); const documentLanguage = ZSupportedLanguageCodeSchema.parse(document.documentMeta?.language);
const auditLogs = await getDocumentCertificateAuditLogs({ const auditLogs = await getDocumentCertificateAuditLogs({
@@ -70,6 +73,7 @@ export async function loader({ request }: Route.LoaderArgs) {
return { return {
document, document,
documentLanguage, documentLanguage,
isPlatformDocument,
auditLogs, auditLogs,
messages, messages,
}; };
@@ -85,7 +89,7 @@ export async function loader({ request }: Route.LoaderArgs) {
* Update: Maybe <Trans> tags work now after RR7 migration. * Update: Maybe <Trans> tags work now after RR7 migration.
*/ */
export default function SigningCertificate({ loaderData }: Route.ComponentProps) { export default function SigningCertificate({ loaderData }: Route.ComponentProps) {
const { document, documentLanguage, auditLogs, messages } = loaderData; const { document, documentLanguage, isPlatformDocument, auditLogs, messages } = loaderData;
const { i18n, _ } = useLingui(); const { i18n, _ } = useLingui();
@@ -337,15 +341,17 @@ export default function SigningCertificate({ loaderData }: Route.ComponentProps)
</CardContent> </CardContent>
</Card> </Card>
<div className="my-8 flex-row-reverse"> {isPlatformDocument && (
<div className="flex items-end justify-end gap-x-4"> <div className="my-8 flex-row-reverse">
<p className="flex-shrink-0 text-sm font-medium print:text-xs"> <div className="flex items-end justify-end gap-x-4">
{_(msg`Signing certificate provided by`)}: <p className="flex-shrink-0 text-sm font-medium print:text-xs">
</p> {_(msg`Signing certificate provided by`)}:
</p>
<BrandingLogo className="max-h-6 print:max-h-4" /> <BrandingLogo className="max-h-6 print:max-h-4" />
</div>
</div> </div>
</div> )}
</div> </div>
); );
} }

View File

@@ -75,7 +75,7 @@ export default function PublicProfileLayout() {
</p> </p>
<Button asChild variant="secondary"> <Button asChild variant="secondary">
<Link to="/signup"> <Link to="https://bls.media/sign/">
<div className="hidden flex-row items-center sm:flex"> <div className="hidden flex-row items-center sm:flex">
<PlusIcon className="mr-1 h-5 w-5" /> <PlusIcon className="mr-1 h-5 w-5" />
<Trans>Create now</Trans> <Trans>Create now</Trans>

View File

@@ -79,7 +79,14 @@ export default function DirectTemplatePage() {
const { template, directTemplateRecipient } = data; const { template, directTemplateRecipient } = data;
return ( return (
<DocumentSigningProvider email={user?.email} fullName={user?.name} signature={user?.signature}> <DocumentSigningProvider
email={user?.email}
fullName={user?.name}
signature={user?.signature}
typedSignatureEnabled={template.templateMeta?.typedSignatureEnabled}
uploadSignatureEnabled={template.templateMeta?.uploadSignatureEnabled}
drawSignatureEnabled={template.templateMeta?.drawSignatureEnabled}
>
<DocumentSigningAuthProvider <DocumentSigningAuthProvider
documentAuthOptions={template.authOptions} documentAuthOptions={template.authOptions}
recipient={directTemplateRecipient} recipient={directTemplateRecipient}

View File

@@ -233,6 +233,9 @@ export default function SigningPage() {
email={recipient.email} email={recipient.email}
fullName={user?.email === recipient.email ? user?.name : recipient.name} fullName={user?.email === recipient.email ? user?.name : recipient.name}
signature={user?.email === recipient.email ? user?.signature : undefined} signature={user?.email === recipient.email ? user?.signature : undefined}
typedSignatureEnabled={document.documentMeta?.typedSignatureEnabled}
uploadSignatureEnabled={document.documentMeta?.uploadSignatureEnabled}
drawSignatureEnabled={document.documentMeta?.drawSignatureEnabled}
> >
<DocumentSigningAuthProvider <DocumentSigningAuthProvider
documentAuthOptions={document.authOptions} documentAuthOptions={document.authOptions}

View File

@@ -204,7 +204,7 @@ export default function CompletedSigningPage({ loaderData }: Route.ComponentProp
))} ))}
<div className="mt-8 flex w-full max-w-sm items-center justify-center gap-4"> <div className="mt-8 flex w-full max-w-sm items-center justify-center gap-4">
<DocumentShareButton documentId={document.id} token={recipient.token} /> {/* <DocumentShareButton documentId={document.id} token={recipient.token} /> Share Button ausgeblendet */}
{isDocumentCompleted(document.status) ? ( {isDocumentCompleted(document.status) ? (
<DocumentDownloadButton <DocumentDownloadButton

View File

@@ -115,7 +115,7 @@ export default function RejectedSigningPage({ loaderData }: Route.ComponentProps
{user && ( {user && (
<Button className="mt-6" asChild> <Button className="mt-6" asChild>
<Link to={`/`}>Return Home</Link> <Link to={`/`}>Zurück</Link>
</Button> </Button>
)} )}
</div> </div>

View File

@@ -95,7 +95,7 @@ export default function WaitingForTurnToSignPage({ loaderData }: Route.Component
</Button> </Button>
) : ( ) : (
<Button variant="link" asChild> <Button variant="link" asChild>
<Link to="/documents">Return Home</Link> <Link to="/documents">Zurück</Link>
</Button> </Button>
)} )}
</div> </div>

View File

@@ -3,7 +3,7 @@ import { Link } from 'react-router';
import { Button } from '@documenso/ui/primitives/button'; import { Button } from '@documenso/ui/primitives/button';
const SUPPORT_EMAIL = 'support@documenso.com'; const SUPPORT_EMAIL = 'hello@bls-media.de';
export default function SignatureDisclosure() { export default function SignatureDisclosure() {
return ( return (

View File

@@ -6,15 +6,15 @@ import type { Route } from './+types/share.$slug';
export function meta({ params: { slug } }: Route.MetaArgs) { export function meta({ params: { slug } }: Route.MetaArgs) {
return [ return [
{ title: 'Documenso - Share' }, { title: 'BLS sign - Share' },
{ description: 'I just signed a document in style with Documenso!' }, { description: 'I just signed a document in style with BLS sign!' },
{ {
property: 'og:title', property: 'og:title',
content: 'Documenso - Join the open source signing revolution', content: 'BLS sign - Join the open source signing revolution',
}, },
{ {
property: 'og:description', property: 'og:description',
content: 'I just signed with Documenso!', content: 'I just signed with BLS sign!',
}, },
{ {
property: 'og:type', property: 'og:type',
@@ -38,7 +38,7 @@ export function meta({ params: { slug } }: Route.MetaArgs) {
}, },
{ {
name: 'twitter:description', name: 'twitter:description',
content: 'I just signed with Documenso!', content: 'I just signed with BLS sign!',
}, },
]; ];
} }
@@ -50,8 +50,8 @@ export const loader = ({ request }: Route.LoaderArgs) => {
return null; return null;
} }
// Is hardcoded because this whole meta is hardcoded anyway for Documenso. // Is hardcoded because this whole meta is hardcoded anyway for BLS sign.
throw redirect('https://documenso.com'); throw redirect('https://bls.media/');
}; };
export default function SharePage() { export default function SharePage() {

View File

@@ -131,7 +131,14 @@ export default function EmbedDirectTemplatePage() {
} = useSuperLoaderData<typeof loader>(); } = useSuperLoaderData<typeof loader>();
return ( return (
<DocumentSigningProvider email={user?.email} fullName={user?.name} signature={user?.signature}> <DocumentSigningProvider
email={user?.email}
fullName={user?.name}
signature={user?.signature}
typedSignatureEnabled={template.templateMeta?.typedSignatureEnabled}
uploadSignatureEnabled={template.templateMeta?.uploadSignatureEnabled}
drawSignatureEnabled={template.templateMeta?.drawSignatureEnabled}
>
<DocumentSigningAuthProvider <DocumentSigningAuthProvider
documentAuthOptions={template.authOptions} documentAuthOptions={template.authOptions}
recipient={recipient} recipient={recipient}

View File

@@ -156,6 +156,9 @@ export default function EmbedSignDocumentPage() {
email={recipient.email} email={recipient.email}
fullName={user?.email === recipient.email ? user?.name : recipient.name} fullName={user?.email === recipient.email ? user?.name : recipient.name}
signature={user?.email === recipient.email ? user?.signature : undefined} signature={user?.email === recipient.email ? user?.signature : undefined}
typedSignatureEnabled={document.documentMeta?.typedSignatureEnabled}
uploadSignatureEnabled={document.documentMeta?.uploadSignatureEnabled}
drawSignatureEnabled={document.documentMeta?.drawSignatureEnabled}
> >
<DocumentSigningAuthProvider <DocumentSigningAuthProvider
documentAuthOptions={document.authOptions} documentAuthOptions={document.authOptions}

View File

@@ -2,11 +2,11 @@ import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
export const appMetaTags = (title?: string) => { export const appMetaTags = (title?: string) => {
const description = const description =
'Join Documenso, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.'; 'Join BLS sign, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.';
return [ return [
{ {
title: title ? `${title} - Documenso` : 'Documenso', title: title ? `${title} - BLS sign` : 'BLS sign',
}, },
{ {
name: 'description', name: 'description',
@@ -15,7 +15,7 @@ export const appMetaTags = (title?: string) => {
{ {
name: 'keywords', name: 'keywords',
content: content:
'Documenso, open source, DocuSign alternative, document signing, open signing infrastructure, open-source community, fast signing, beautiful signing, smart templates', 'BLS sign, open source, DocuSign alternative, document signing, open signing infrastructure, open-source community, fast signing, beautiful signing, smart templates',
}, },
{ {
name: 'author', name: 'author',
@@ -27,7 +27,7 @@ export const appMetaTags = (title?: string) => {
}, },
{ {
property: 'og:title', property: 'og:title',
content: 'Documenso - The Open Source DocuSign Alternative', content: 'BLS sign',
}, },
{ {
property: 'og:description', property: 'og:description',

View File

@@ -100,5 +100,5 @@
"vite-plugin-babel-macros": "^1.0.6", "vite-plugin-babel-macros": "^1.0.6",
"vite-tsconfig-paths": "^5.1.4" "vite-tsconfig-paths": "^5.1.4"
}, },
"version": "1.10.0-rc.0" "version": "1.10.0-rc.1"
} }

View File

@@ -2,6 +2,6 @@
Contact: https://github.com/documenso/documenso/issues/new?assignees=&labels=bug&projects=&template=bug-report.yml Contact: https://github.com/documenso/documenso/issues/new?assignees=&labels=bug&projects=&template=bug-report.yml
# Report critical issues privately to let us take appropriate action before publishing. # Report critical issues privately to let us take appropriate action before publishing.
Contact: mailto:security@documenso.com Contact: mailto:security@sign.bls.media
Preferred-Languages: en Preferred-Languages: en
Canonical: https://documenso.com/.well-known/security.txt Canonical: https://documenso.com/.well-known/security.txt

View File

@@ -1,6 +1,6 @@
{ {
"name": "Documenso", "name": "BLS sign",
"short_name": "Documenso", "short_name": "BLS sign",
"icons": [ "icons": [
{ {
"src": "/android-chrome-192x192.png", "src": "/android-chrome-192x192.png",
@@ -13,7 +13,7 @@
"type": "image/png" "type": "image/png"
} }
], ],
"theme_color": "#A2E771", //"theme_color": "#A2E771",
"background_color": "#FFFFFF", "background_color": "#F8F8F8",
"display": "standalone" "display": "standalone"
} }

6
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "@documenso/root", "name": "@documenso/root",
"version": "1.10.0-rc.0", "version": "1.10.0-rc.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@documenso/root", "name": "@documenso/root",
"version": "1.10.0-rc.0", "version": "1.10.0-rc.1",
"workspaces": [ "workspaces": [
"apps/*", "apps/*",
"packages/*" "packages/*"
@@ -95,7 +95,7 @@
}, },
"apps/remix": { "apps/remix": {
"name": "@documenso/remix", "name": "@documenso/remix",
"version": "1.10.0-rc.0", "version": "1.10.0-rc.1",
"dependencies": { "dependencies": {
"@documenso/api": "*", "@documenso/api": "*",
"@documenso/assets": "*", "@documenso/assets": "*",

View File

@@ -1,6 +1,6 @@
{ {
"private": true, "private": true,
"version": "1.10.0-rc.0", "version": "1.10.0-rc.1",
"scripts": { "scripts": {
"build": "turbo run build", "build": "turbo run build",
"dev": "turbo run dev --filter=@documenso/remix", "dev": "turbo run dev --filter=@documenso/remix",

View File

@@ -326,6 +326,8 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
allowDictateNextSigner: body.meta.allowDictateNextSigner, allowDictateNextSigner: body.meta.allowDictateNextSigner,
language: body.meta.language, language: body.meta.language,
typedSignatureEnabled: body.meta.typedSignatureEnabled, typedSignatureEnabled: body.meta.typedSignatureEnabled,
uploadSignatureEnabled: body.meta.uploadSignatureEnabled,
drawSignatureEnabled: body.meta.drawSignatureEnabled,
distributionMethod: body.meta.distributionMethod, distributionMethod: body.meta.distributionMethod,
emailSettings: body.meta.emailSettings, emailSettings: body.meta.emailSettings,
requestMetadata: metadata, requestMetadata: metadata,

View File

@@ -9,9 +9,9 @@ export const OpenAPIV1 = Object.assign(
ApiContractV1, ApiContractV1,
{ {
info: { info: {
title: 'Documenso API', title: 'BLS sign API',
version: '1.0.0', version: '1.0.0',
description: 'The Documenso API for retrieving, creating, updating and deleting documents.', description: 'The BLS sign API for retrieving, creating, updating and deleting documents.',
}, },
servers: [ servers: [
{ {

View File

@@ -158,6 +158,8 @@ export const ZCreateDocumentMutationSchema = z.object({
allowDictateNextSigner: z.boolean().optional(), allowDictateNextSigner: z.boolean().optional(),
language: z.enum(SUPPORTED_LANGUAGE_CODES).optional(), language: z.enum(SUPPORTED_LANGUAGE_CODES).optional(),
typedSignatureEnabled: z.boolean().optional().default(true), typedSignatureEnabled: z.boolean().optional().default(true),
uploadSignatureEnabled: z.boolean().optional().default(true),
drawSignatureEnabled: z.boolean().optional().default(true),
distributionMethod: z.nativeEnum(DocumentDistributionMethod).optional(), distributionMethod: z.nativeEnum(DocumentDistributionMethod).optional(),
emailSettings: ZDocumentEmailSettingsSchema.optional(), emailSettings: ZDocumentEmailSettingsSchema.optional(),
}) })
@@ -291,6 +293,8 @@ export const ZGenerateDocumentFromTemplateMutationSchema = z.object({
language: z.enum(SUPPORTED_LANGUAGE_CODES), language: z.enum(SUPPORTED_LANGUAGE_CODES),
distributionMethod: z.nativeEnum(DocumentDistributionMethod), distributionMethod: z.nativeEnum(DocumentDistributionMethod),
typedSignatureEnabled: z.boolean(), typedSignatureEnabled: z.boolean(),
uploadSignatureEnabled: z.boolean(),
drawSignatureEnabled: z.boolean(),
emailSettings: ZDocumentEmailSettingsSchema, emailSettings: ZDocumentEmailSettingsSchema,
}) })
.partial() .partial()

View File

@@ -16,7 +16,7 @@ test('[DOCUMENT_AUTH]: should grant access when not required', async ({ page })
const document = await seedPendingDocument(user, [ const document = await seedPendingDocument(user, [
recipientWithAccount, recipientWithAccount,
'recipientwithoutaccount@documenso.com', 'recipientwithoutaccount@sign.bls.media',
]); ]);
const recipients = await prisma.recipient.findMany({ const recipients = await prisma.recipient.findMany({
@@ -40,7 +40,7 @@ test('[DOCUMENT_AUTH]: should allow or deny access when required', async ({ page
const document = await seedPendingDocument( const document = await seedPendingDocument(
user, user,
[recipientWithAccount, 'recipientwithoutaccount@documenso.com'], [recipientWithAccount, 'recipientwithoutaccount@sign.bls.media'],
{ {
createDocumentOptions: { createDocumentOptions: {
authOptions: createDocumentAuthOptions({ authOptions: createDocumentAuthOptions({

View File

@@ -246,7 +246,9 @@ test('[DOCUMENT_AUTH]: should allow field signing when required for recipient au
}); });
} }
await signSignaturePad(page); if (fields.some((field) => field.type === FieldType.SIGNATURE)) {
await signSignaturePad(page);
}
for (const field of fields) { for (const field of fields) {
await page.locator(`#field-${field.id}`).getByRole('button').click(); await page.locator(`#field-${field.id}`).getByRole('button').click();
@@ -349,7 +351,9 @@ test('[DOCUMENT_AUTH]: should allow field signing when required for recipient an
}); });
} }
await signSignaturePad(page); if (fields.some((field) => field.type === FieldType.SIGNATURE)) {
await signSignaturePad(page);
}
for (const field of fields) { for (const field of fields) {
await page.locator(`#field-${field.id}`).getByRole('button').click(); await page.locator(`#field-${field.id}`).getByRole('button').click();

View File

@@ -11,7 +11,7 @@ import { prisma } from '@documenso/prisma';
import { seedPendingDocumentWithFullFields } from '@documenso/prisma/seed/documents'; import { seedPendingDocumentWithFullFields } from '@documenso/prisma/seed/documents';
import { seedUser } from '@documenso/prisma/seed/users'; import { seedUser } from '@documenso/prisma/seed/users';
import { signSignaturePad } from '../fixtures/signature'; import { signDirectSignaturePad, signSignaturePad } from '../fixtures/signature';
test('[NEXT_RECIPIENT_DICTATION]: should allow updating next recipient when dictation is enabled', async ({ test('[NEXT_RECIPIENT_DICTATION]: should allow updating next recipient when dictation is enabled', async ({
page, page,
@@ -322,7 +322,7 @@ test('[NEXT_RECIPIENT_DICTATION]: should allow assistant to dictate next signer'
await page.locator(`#field-${field.id}`).getByRole('button').click(); await page.locator(`#field-${field.id}`).getByRole('button').click();
if (field.type === FieldType.SIGNATURE) { if (field.type === FieldType.SIGNATURE) {
await signSignaturePad(page); await signDirectSignaturePad(page);
await page.getByRole('button', { name: 'Sign', exact: true }).click(); await page.getByRole('button', { name: 'Sign', exact: true }).click();
} }

View File

@@ -39,11 +39,11 @@ test.describe('[EE_ONLY]', () => {
await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();
// Add 2 signers. // Add 2 signers.
await page.getByPlaceholder('Email').fill('recipient1@documenso.com'); await page.getByPlaceholder('Email').fill('recipient1@sign.bls.media');
await page.getByPlaceholder('Name').fill('Recipient 1'); await page.getByPlaceholder('Name').fill('Recipient 1');
await page.getByRole('button', { name: 'Add Signer' }).click(); await page.getByRole('button', { name: 'Add Signer' }).click();
await page.getByLabel('Email').nth(1).fill('recipient2@documenso.com'); await page.getByLabel('Email').nth(1).fill('recipient2@sign.bls.media');
await page.getByLabel('Name').nth(1).fill('Recipient 2'); await page.getByLabel('Name').nth(1).fill('Recipient 2');
// Display advanced settings. // Display advanced settings.
@@ -74,12 +74,12 @@ test('[DOCUMENT_FLOW]: add signers', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Add Signers' })).toBeVisible();
// Add 2 signers. // Add 2 signers.
await page.getByPlaceholder('Email').fill('recipient1@documenso.com'); await page.getByPlaceholder('Email').fill('recipient1@sign.bls.media');
await page.getByPlaceholder('Name').fill('Recipient 1'); await page.getByPlaceholder('Name').fill('Recipient 1');
await page.getByRole('button', { name: 'Add Signer' }).click(); await page.getByRole('button', { name: 'Add Signer' }).click();
await page.getByLabel('Email').nth(1).fill('recipient2@documenso.com'); await page.getByLabel('Email').nth(1).fill('recipient2@sign.bls.media');
await page.getByLabel('Name').nth(1).fill('Recipient 2'); await page.getByLabel('Name').nth(1).fill('Recipient 2');
// Advanced settings should not be visible for non EE users. // Advanced settings should not be visible for non EE users.

View File

@@ -343,14 +343,14 @@ test('[DOCUMENT_FLOW]: should be able to approve a document', async ({ page }) =
const { recipients } = await seedPendingDocumentWithFullFields({ const { recipients } = await seedPendingDocumentWithFullFields({
owner: user, owner: user,
recipients: ['user@documenso.com', 'approver@documenso.com'], recipients: ['user@sign.bls.media', 'approver@sign.bls.media'],
recipientsCreateOptions: [ recipientsCreateOptions: [
{ {
email: 'user@documenso.com', email: 'user@sign.bls.media',
role: RecipientRole.SIGNER, role: RecipientRole.SIGNER,
}, },
{ {
email: 'approver@documenso.com', email: 'approver@sign.bls.media',
role: RecipientRole.APPROVER, role: RecipientRole.APPROVER,
}, },
], ],

View File

@@ -222,7 +222,10 @@ test.describe('Signing Certificate Tests', () => {
// Toggle signing certificate setting // Toggle signing certificate setting
await page.getByLabel('Include the Signing Certificate in the Document').click(); await page.getByLabel('Include the Signing Certificate in the Document').click();
await page.getByRole('button', { name: /Save/ }).first().click(); await page
.getByRole('button', { name: /Update/ })
.first()
.click();
await page.waitForTimeout(1000); await page.waitForTimeout(1000);
@@ -236,7 +239,10 @@ test.describe('Signing Certificate Tests', () => {
// Toggle the setting back to true // Toggle the setting back to true
await page.getByLabel('Include the Signing Certificate in the Document').click(); await page.getByLabel('Include the Signing Certificate in the Document').click();
await page.getByRole('button', { name: /Save/ }).first().click(); await page
.getByRole('button', { name: /Update/ })
.first()
.click();
await page.waitForTimeout(1000); await page.waitForTimeout(1000);

View File

@@ -15,7 +15,7 @@ type LoginOptions = {
export const apiSignin = async ({ export const apiSignin = async ({
page, page,
email = 'example@documenso.com', email = 'example@sign.bls.media',
password = 'password', password = 'password',
redirectPath = '/documents', redirectPath = '/documents',
}: LoginOptions) => { }: LoginOptions) => {

View File

@@ -1,40 +1,28 @@
import type { Page } from '@playwright/test'; import type { Page } from '@playwright/test';
/**
* Will open the signature pad dialog and sign it.
*/
export const signSignaturePad = async (page: Page) => { export const signSignaturePad = async (page: Page) => {
await page.waitForTimeout(200); await page.waitForTimeout(200);
const canvas = page.getByTestId('signature-pad'); await page.getByTestId('signature-pad-dialog-button').click();
const box = await canvas.boundingBox(); // Click type tab
await page.getByRole('tab', { name: 'Type' }).click();
await page.getByTestId('signature-pad-type-input').fill('Signature');
if (!box) { // Click Next button
throw new Error('Signature pad not found'); await page.getByRole('button', { name: 'Next' }).click();
} };
// Calculate center point /**
const centerX = box.x + box.width / 2; * For when the signature pad is already open.
const centerY = box.y + box.height / 2; */
export const signDirectSignaturePad = async (page: Page) => {
// Calculate square size (making it slightly smaller than the canvas) await page.waitForTimeout(200);
const squareSize = Math.min(box.width, box.height) * 0.4; // 40% of the smallest dimension
// Click type tab
// Move to center await page.getByRole('tab', { name: 'Type' }).click();
await page.mouse.move(centerX, centerY); await page.getByTestId('signature-pad-type-input').fill('Signature');
await page.mouse.down();
// Draw square clockwise from center
// Move right
await page.mouse.move(centerX + squareSize, centerY, { steps: 10 });
// Move down
await page.mouse.move(centerX + squareSize, centerY + squareSize, { steps: 10 });
// Move left
await page.mouse.move(centerX - squareSize, centerY + squareSize, { steps: 10 });
// Move up
await page.mouse.move(centerX - squareSize, centerY - squareSize, { steps: 10 });
// Move right
await page.mouse.move(centerX + squareSize, centerY - squareSize, { steps: 10 });
// Move down to close the square
await page.mouse.move(centerX + squareSize, centerY, { steps: 10 });
await page.mouse.up();
}; };

View File

@@ -23,7 +23,7 @@ test('[TEAMS]: update the default document visibility in the team global setting
// !: Brittle selector // !: Brittle selector
await page.getByRole('combobox').first().click(); await page.getByRole('combobox').first().click();
await page.getByRole('option', { name: 'Admin' }).click(); await page.getByRole('option', { name: 'Admin' }).click();
await page.getByRole('button', { name: 'Save' }).first().click(); await page.getByRole('button', { name: 'Update' }).first().click();
const toast = page.locator('li[role="status"][data-state="open"]').first(); const toast = page.locator('li[role="status"][data-state="open"]').first();
await expect(toast).toBeVisible(); await expect(toast).toBeVisible();
@@ -47,7 +47,7 @@ test('[TEAMS]: update the sender details in the team global settings', async ({
await expect(checkbox).toBeChecked(); await expect(checkbox).toBeChecked();
await page.getByRole('button', { name: 'Save' }).first().click(); await page.getByRole('button', { name: 'Update' }).first().click();
const toast = page.locator('li[role="status"][data-state="open"]').first(); const toast = page.locator('li[role="status"][data-state="open"]').first();
await expect(toast).toBeVisible(); await expect(toast).toBeVisible();

View File

@@ -0,0 +1,182 @@
import { expect, test } from '@playwright/test';
import { prisma } from '@documenso/prisma';
import {
seedTeamDocumentWithMeta,
seedTeamDocuments,
seedTeamTemplateWithMeta,
} from '@documenso/prisma/seed/documents';
import { apiSignin } from '../fixtures/authentication';
test.describe.configure({ mode: 'parallel' });
test('[TEAMS]: check that default team signature settings are all enabled', async ({ page }) => {
const { team } = await seedTeamDocuments();
await apiSignin({
page,
email: team.owner.email,
password: 'password',
redirectPath: `/t/${team.url}/settings/preferences`,
});
// Verify that the default created team settings has all signatures enabled
await expect(page.getByRole('combobox').filter({ hasText: 'Type' })).toBeVisible();
await expect(page.getByRole('combobox').filter({ hasText: 'Upload' })).toBeVisible();
await expect(page.getByRole('combobox').filter({ hasText: 'Draw' })).toBeVisible();
const document = await seedTeamDocumentWithMeta(team);
// Create a document and check the settings
await page.goto(`/t/${team.url}/documents/${document.id}/edit`);
// Verify that the settings match
await page.getByRole('button', { name: 'Advanced Options' }).click();
await expect(page.getByRole('combobox').filter({ hasText: 'Type' })).toBeVisible();
await expect(page.getByRole('combobox').filter({ hasText: 'Upload' })).toBeVisible();
await expect(page.getByRole('combobox').filter({ hasText: 'Draw' })).toBeVisible();
// Go to document and check that the signatured tabs are correct.
await page.goto(`/sign/${document.recipients[0].token}`);
await page.getByTestId('signature-pad-dialog-button').click();
// Check the tab values
await expect(page.getByRole('tab', { name: 'Type' })).toBeVisible();
await expect(page.getByRole('tab', { name: 'Upload' })).toBeVisible();
await expect(page.getByRole('tab', { name: 'Draw' })).toBeVisible();
});
test('[TEAMS]: check signature modes can be disabled', async ({ page }) => {
const { team } = await seedTeamDocuments();
await apiSignin({
page,
email: team.owner.email,
password: 'password',
redirectPath: `/t/${team.url}/settings/preferences`,
});
const allTabs = ['Type', 'Upload', 'Draw'];
const tabTest = [['Type', 'Upload', 'Draw'], ['Type', 'Upload'], ['Type']];
for (const tabs of tabTest) {
await page.goto(`/t/${team.url}/settings/preferences`);
// Update combobox to have the correct tabs
await page.getByTestId('signature-types-combobox').click();
await expect(page.getByRole('option', { name: 'Type' })).toBeVisible();
await expect(page.getByRole('option', { name: 'Upload' })).toBeVisible();
await expect(page.getByRole('option', { name: 'Draw' })).toBeVisible();
// Clear all selected items.
for (const tab of allTabs) {
const item = page.getByRole('option', { name: tab });
const isSelected = (await item.innerHTML()).includes('opacity-100');
if (isSelected) {
await item.click();
}
}
// Selected wanted items.
for (const tab of tabs) {
const item = page.getByRole('option', { name: tab });
await item.click();
}
await page.getByRole('button', { name: 'Update' }).first().click();
const document = await seedTeamDocumentWithMeta(team);
// Go to document and check that the signatured tabs are correct.
await page.goto(`/sign/${document.recipients[0].token}`);
await page.getByTestId('signature-pad-dialog-button').click();
// Check the tab values
for (const tab of allTabs) {
if (tabs.includes(tab)) {
await expect(page.getByRole('tab', { name: tab })).toBeVisible();
} else {
await expect(page.getByRole('tab', { name: tab })).not.toBeVisible();
}
}
}
});
test('[TEAMS]: check signature modes work for templates', async ({ page }) => {
const { team } = await seedTeamDocuments();
await apiSignin({
page,
email: team.owner.email,
password: 'password',
redirectPath: `/t/${team.url}/settings/preferences`,
});
const allTabs = ['Type', 'Upload', 'Draw'];
const tabTest = [['Type', 'Upload', 'Draw'], ['Type', 'Upload'], ['Type']];
for (const tabs of tabTest) {
await page.goto(`/t/${team.url}/settings/preferences`);
// Update combobox to have the correct tabs
await page.getByTestId('signature-types-combobox').click();
await expect(page.getByRole('option', { name: 'Type' })).toBeVisible();
await expect(page.getByRole('option', { name: 'Upload' })).toBeVisible();
await expect(page.getByRole('option', { name: 'Draw' })).toBeVisible();
// Clear all selected items.
for (const tab of allTabs) {
const item = page.getByRole('option', { name: tab });
const isSelected = (await item.innerHTML()).includes('opacity-100');
if (isSelected) {
await item.click();
}
}
// Selected wanted items.
for (const tab of tabs) {
const item = page.getByRole('option', { name: tab });
await item.click();
}
await page.getByRole('button', { name: 'Update' }).first().click();
const template = await seedTeamTemplateWithMeta(team);
await page.goto(`/t/${team.url}/templates/${template.id}`);
await page.getByRole('button', { name: 'Use' }).click();
// Check the send document checkbox to true
await page.getByLabel('Send document').click();
await page.getByRole('button', { name: 'Create and send' }).click();
await page.waitForTimeout(1000);
const document = await prisma.document.findFirst({
where: {
templateId: template.id,
},
include: {
documentMeta: true,
},
});
// Test kinda flaky, debug here.
// console.log({
// tabs,
// typedSignatureEnabled: document?.documentMeta?.typedSignatureEnabled,
// uploadSignatureEnabled: document?.documentMeta?.uploadSignatureEnabled,
// drawSignatureEnabled: document?.documentMeta?.drawSignatureEnabled,
// });
expect(document?.documentMeta?.typedSignatureEnabled).toEqual(tabs.includes('Type'));
expect(document?.documentMeta?.uploadSignatureEnabled).toEqual(tabs.includes('Upload'));
expect(document?.documentMeta?.drawSignatureEnabled).toEqual(tabs.includes('Draw'));
}
});

View File

@@ -39,10 +39,10 @@ test.describe('[EE_ONLY]', () => {
await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible();
// Add 2 signers. // Add 2 signers.
await page.getByPlaceholder('Email').fill('recipient1@documenso.com'); await page.getByPlaceholder('Email').fill('recipient1@sign.bls.media');
await page.getByPlaceholder('Name').fill('Recipient 1'); await page.getByPlaceholder('Name').fill('Recipient 1');
await page.getByRole('button', { name: 'Add Placeholder Recipient' }).click(); await page.getByRole('button', { name: 'Add Placeholder Recipient' }).click();
await page.getByPlaceholder('Email').nth(1).fill('recipient2@documenso.com'); await page.getByPlaceholder('Email').nth(1).fill('recipient2@sign.bls.media');
await page.getByPlaceholder('Name').nth(1).fill('Recipient 2'); await page.getByPlaceholder('Name').nth(1).fill('Recipient 2');
// Display advanced settings. // Display advanced settings.
@@ -89,10 +89,10 @@ test('[TEMPLATE_FLOW]: add placeholder', async ({ page }) => {
await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible();
// Add 2 signers. // Add 2 signers.
await page.getByPlaceholder('Email').fill('recipient1@documenso.com'); await page.getByPlaceholder('Email').fill('recipient1@sign.bls.media');
await page.getByPlaceholder('Name').fill('Recipient 1'); await page.getByPlaceholder('Name').fill('Recipient 1');
await page.getByRole('button', { name: 'Add Placeholder Recipient' }).click(); await page.getByRole('button', { name: 'Add Placeholder Recipient' }).click();
await page.getByPlaceholder('Email').nth(1).fill('recipient2@documenso.com'); await page.getByPlaceholder('Email').nth(1).fill('recipient2@sign.bls.media');
await page.getByPlaceholder('Name').nth(1).fill('Recipient 2'); await page.getByPlaceholder('Name').nth(1).fill('Recipient 2');
// Advanced settings should not be visible for non EE users. // Advanced settings should not be visible for non EE users.

View File

@@ -91,10 +91,10 @@ test('[TEMPLATE]: should create a document from a template', async ({ page }) =>
await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible();
// Add 2 signers. // Add 2 signers.
await page.getByPlaceholder('Email').fill('recipient1@documenso.com'); await page.getByPlaceholder('Email').fill('recipient1@sign.bls.media');
await page.getByPlaceholder('Name').fill('Recipient 1'); await page.getByPlaceholder('Name').fill('Recipient 1');
await page.getByRole('button', { name: 'Add Placeholder Recipient' }).click(); await page.getByRole('button', { name: 'Add Placeholder Recipient' }).click();
await page.getByPlaceholder('Email').nth(1).fill('recipient2@documenso.com'); await page.getByPlaceholder('Email').nth(1).fill('recipient2@sign.bls.media');
await page.getByPlaceholder('Name').nth(1).fill('Recipient 2'); await page.getByPlaceholder('Name').nth(1).fill('Recipient 2');
// Apply require passkey for Recipient 1. // Apply require passkey for Recipient 1.
@@ -226,10 +226,10 @@ test('[TEMPLATE]: should create a team document from a team template', async ({
await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible();
// Add 2 signers. // Add 2 signers.
await page.getByPlaceholder('Email').fill('recipient1@documenso.com'); await page.getByPlaceholder('Email').fill('recipient1@sign.bls.media');
await page.getByPlaceholder('Name').fill('Recipient 1'); await page.getByPlaceholder('Name').fill('Recipient 1');
await page.getByRole('button', { name: 'Add Placeholder Recipient' }).click(); await page.getByRole('button', { name: 'Add Placeholder Recipient' }).click();
await page.getByPlaceholder('Email').nth(1).fill('recipient2@documenso.com'); await page.getByPlaceholder('Email').nth(1).fill('recipient2@sign.bls.media');
await page.getByPlaceholder('Name').nth(1).fill('Recipient 2'); await page.getByPlaceholder('Name').nth(1).fill('Recipient 2');
// Apply require passkey for Recipient 1. // Apply require passkey for Recipient 1.
@@ -330,7 +330,7 @@ test('[TEMPLATE]: should create a document from a template with custom document'
await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible();
// Add a signer // Add a signer
await page.getByPlaceholder('Email').fill('recipient@documenso.com'); await page.getByPlaceholder('Email').fill('recipient@sign.bls.media');
await page.getByPlaceholder('Name').fill('Recipient'); await page.getByPlaceholder('Name').fill('Recipient');
await page.getByRole('button', { name: 'Continue' }).click(); await page.getByRole('button', { name: 'Continue' }).click();
@@ -411,7 +411,7 @@ test('[TEMPLATE]: should create a team document from a template with custom docu
await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible();
// Add a signer // Add a signer
await page.getByPlaceholder('Email').fill('recipient@documenso.com'); await page.getByPlaceholder('Email').fill('recipient@sign.bls.media');
await page.getByPlaceholder('Name').fill('Recipient'); await page.getByPlaceholder('Name').fill('Recipient');
await page.getByRole('button', { name: 'Continue' }).click(); await page.getByRole('button', { name: 'Continue' }).click();
@@ -481,7 +481,7 @@ test('[TEMPLATE]: should create a document from a template using template docume
await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible();
// Add a signer // Add a signer
await page.getByPlaceholder('Email').fill('recipient@documenso.com'); await page.getByPlaceholder('Email').fill('recipient@sign.bls.media');
await page.getByPlaceholder('Name').fill('Recipient'); await page.getByPlaceholder('Name').fill('Recipient');
await page.getByRole('button', { name: 'Continue' }).click(); await page.getByRole('button', { name: 'Continue' }).click();
@@ -561,7 +561,7 @@ test('[TEMPLATE]: should persist document visibility when creating from template
await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'Add Placeholder' })).toBeVisible();
// Add a signer // Add a signer
await page.getByPlaceholder('Email').fill('recipient@documenso.com'); await page.getByPlaceholder('Email').fill('recipient@sign.bls.media');
await page.getByPlaceholder('Name').fill('Recipient'); await page.getByPlaceholder('Name').fill('Recipient');
await page.getByRole('button', { name: 'Continue' }).click(); await page.getByRole('button', { name: 'Continue' }).click();

View File

@@ -225,7 +225,7 @@ test('[DIRECT_TEMPLATES]: use direct template link with 1 recipient', async ({ p
await page.goto(formatDirectTemplatePath(template.directLink?.token || '')); await page.goto(formatDirectTemplatePath(template.directLink?.token || ''));
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
await page.getByPlaceholder('recipient@documenso.com').fill(seedTestEmail()); await page.getByPlaceholder('recipient@sign.bls.media').fill(seedTestEmail());
await page.getByRole('button', { name: 'Continue' }).click(); await page.getByRole('button', { name: 'Continue' }).click();
await page.getByRole('button', { name: 'Complete' }).click(); await page.getByRole('button', { name: 'Complete' }).click();
@@ -298,7 +298,8 @@ test('[DIRECT_TEMPLATES]: use direct template link with 2 recipients', async ({
await page.goto(formatDirectTemplatePath(template.directLink?.token || '')); await page.goto(formatDirectTemplatePath(template.directLink?.token || ''));
await expect(page.getByRole('heading', { name: 'General' })).toBeVisible(); await expect(page.getByRole('heading', { name: 'General' })).toBeVisible();
await page.getByPlaceholder('recipient@documenso.com').fill(seedTestEmail()); await page.waitForTimeout(1000);
await page.getByPlaceholder('recipient@sign.bls.media').fill(seedTestEmail());
await page.getByRole('button', { name: 'Continue' }).click(); await page.getByRole('button', { name: 'Continue' }).click();
await page.getByRole('button', { name: 'Complete' }).click(); await page.getByRole('button', { name: 'Complete' }).click();

View File

@@ -184,8 +184,8 @@ test('[TEMPLATES]: use template', async ({ page }) => {
await page.getByRole('button', { name: 'Use Template' }).click(); await page.getByRole('button', { name: 'Use Template' }).click();
// Enter template values. // Enter template values.
await page.getByPlaceholder('recipient.1@documenso.com').click(); await page.getByPlaceholder('recipient.1@sign.bls.media').click();
await page.getByPlaceholder('recipient.1@documenso.com').fill(teamMemberUser.email); await page.getByPlaceholder('recipient.1@sign.bls.media').fill(teamMemberUser.email);
await page.getByPlaceholder('Recipient 1').click(); await page.getByPlaceholder('Recipient 1').click();
await page.getByPlaceholder('Recipient 1').fill('name'); await page.getByPlaceholder('Recipient 1').fill('name');
@@ -202,8 +202,8 @@ test('[TEMPLATES]: use template', async ({ page }) => {
await page.getByRole('button', { name: 'Use Template' }).click(); await page.getByRole('button', { name: 'Use Template' }).click();
// Enter template values. // Enter template values.
await page.getByPlaceholder('recipient.1@documenso.com').click(); await page.getByPlaceholder('recipient.1@sign.bls.media').click();
await page.getByPlaceholder('recipient.1@documenso.com').fill(teamMemberUser.email); await page.getByPlaceholder('recipient.1@sign.bls.media').fill(teamMemberUser.email);
await page.getByPlaceholder('Recipient 1').click(); await page.getByPlaceholder('Recipient 1').click();
await page.getByPlaceholder('Recipient 1').fill('name'); await page.getByPlaceholder('Recipient 1').fill('name');

View File

@@ -4,6 +4,7 @@ import { getUserByEmail } from '@documenso/lib/server-only/user/get-user-by-emai
import { seedUser } from '@documenso/prisma/seed/users'; import { seedUser } from '@documenso/prisma/seed/users';
import { apiSignin } from '../fixtures/authentication'; import { apiSignin } from '../fixtures/authentication';
import { signSignaturePad } from '../fixtures/signature';
test('[USER] update full name', async ({ page }) => { test('[USER] update full name', async ({ page }) => {
const user = await seedUser(); const user = await seedUser();
@@ -12,7 +13,7 @@ test('[USER] update full name', async ({ page }) => {
await page.getByLabel('Full Name').fill('John Doe'); await page.getByLabel('Full Name').fill('John Doe');
await page.getByPlaceholder('Type your signature').fill('John Doe'); await signSignaturePad(page);
await page.getByRole('button', { name: 'Update profile' }).click(); await page.getByRole('button', { name: 'Update profile' }).click();

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +1,6 @@
{ {
"name": "Documenso", "name": "BLS sign",
"short_name": "Documenso", "short_name": "BLS sign",
"icons": [ "icons": [
{ {
"src": "/android-chrome-192x192.png", "src": "/android-chrome-192x192.png",
@@ -13,7 +13,7 @@
"type": "image/png" "type": "image/png"
} }
], ],
"theme_color": "#A2E771", //"theme_color": "#A2E771",
"background_color": "#FFFFFF", "background_color": "#F8F8F8",
"display": "standalone" "display": "standalone"
} }

View File

@@ -27,7 +27,7 @@ export const TemplateDocumentCompleted = ({
<Section> <Section>
<Section className="mb-4"> <Section className="mb-4">
<Column align="center"> <Column align="center">
<Text className="text-base font-semibold text-[#7AC455]"> <Text className="text-base font-semibold text-[#FF6B3D]">
<Img <Img
src={getAssetUrl('/static/completed.png')} src={getAssetUrl('/static/completed.png')}
className="-mt-0.5 mr-2 inline h-7 w-7 align-middle" className="-mt-0.5 mr-2 inline h-7 w-7 align-middle"

View File

@@ -16,7 +16,7 @@ export const TemplateDocumentImage = ({ assetBaseUrl, className }: TemplateDocum
<Column /> <Column />
<Column> <Column>
<Img className="h-42 mx-auto" src={getAssetUrl('/static/document.png')} alt="Documenso" /> <Img className="h-42 mx-auto" src={getAssetUrl('/static/document.png')} alt="BLS sign" />
</Column> </Column>
<Column /> <Column />

View File

@@ -29,7 +29,7 @@ export const TemplateDocumentRecipientSigned = ({
<Section> <Section>
<Section className="mb-4"> <Section className="mb-4">
<Column align="center"> <Column align="center">
<Text className="text-base font-semibold text-[#7AC455]"> <Text className="text-base font-semibold text-[#FF6B3D]">
<Img <Img
src={getAssetUrl('/static/completed.png')} src={getAssetUrl('/static/completed.png')}
className="-mt-0.5 mr-2 inline h-7 w-7 align-middle" className="-mt-0.5 mr-2 inline h-7 w-7 align-middle"

View File

@@ -29,7 +29,7 @@ export const TemplateDocumentSelfSigned = ({
<Section className="flex-row items-center justify-center"> <Section className="flex-row items-center justify-center">
<Section> <Section>
<Column align="center"> <Column align="center">
<Text className="text-base font-semibold text-[#7AC455]"> <Text className="text-base font-semibold text-[#FF6B3D]">
<Img <Img
src={getAssetUrl('/static/completed.png')} src={getAssetUrl('/static/completed.png')}
className="-mt-0.5 mr-2 inline h-7 w-7 align-middle" className="-mt-0.5 mr-2 inline h-7 w-7 align-middle"

View File

@@ -16,8 +16,8 @@ export const TemplateFooter = ({ isDocument = true }: TemplateFooterProps) => {
<Text className="my-4 text-base text-slate-400"> <Text className="my-4 text-base text-slate-400">
<Trans> <Trans>
This document was sent using{' '} This document was sent using{' '}
<Link className="text-[#7AC455]" href="https://documen.so/mail-footer"> <Link className="text-[#FF6B3D]" href="https://bls.media/sign/">
Documenso. BLS sign.
</Link> </Link>
</Trans> </Trans>
</Text> </Text>
@@ -36,9 +36,7 @@ export const TemplateFooter = ({ isDocument = true }: TemplateFooterProps) => {
</Text> </Text>
) : ( ) : (
<Text className="my-8 text-sm text-slate-400"> <Text className="my-8 text-sm text-slate-400">
Documenso, Inc. Made by BLS media
<br />
2261 Market Street, #5211, San Francisco, CA 94114, USA
</Text> </Text>
)} )}
</Section> </Section>

View File

@@ -33,7 +33,7 @@ export const ConfirmEmailTemplate = ({
) : ( ) : (
<Img <Img
src={getAssetUrl('/static/logo.png')} src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo" alt="BLS sign Logo"
className="mb-4 h-6" className="mb-4 h-6"
/> />
)} )}

View File

@@ -11,7 +11,7 @@ export type DocumentCancelEmailTemplateProps = Partial<TemplateDocumentCancelPro
export const DocumentCancelTemplate = ({ export const DocumentCancelTemplate = ({
inviterName = 'Lucas Smith', inviterName = 'Lucas Smith',
inviterEmail = 'lucas@documenso.com', inviterEmail = 'lucas@sign.bls.media',
documentName = 'Open Source Pledge.pdf', documentName = 'Open Source Pledge.pdf',
assetBaseUrl = 'http://localhost:3002', assetBaseUrl = 'http://localhost:3002',
cancellationReason, cancellationReason,
@@ -39,7 +39,7 @@ export const DocumentCancelTemplate = ({
) : ( ) : (
<Img <Img
src={getAssetUrl('/static/logo.png')} src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo" alt="BLS sign Logo"
className="mb-4 h-6" className="mb-4 h-6"
/> />
)} )}

View File

@@ -40,7 +40,7 @@ export const DocumentCompletedEmailTemplate = ({
) : ( ) : (
<Img <Img
src={getAssetUrl('/static/logo.png')} src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo" alt="BLS sign Logo"
className="mb-4 h-6" className="mb-4 h-6"
/> />
)} )}

View File

@@ -50,7 +50,7 @@ export const DocumentCreatedFromDirectTemplateEmailTemplate = ({
) : ( ) : (
<Img <Img
src={getAssetUrl('/static/logo.png')} src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo" alt="BLS sign Logo"
className="mb-4 h-6" className="mb-4 h-6"
/> />
)} )}

View File

@@ -23,7 +23,7 @@ export type DocumentInviteEmailTemplateProps = Partial<TemplateDocumentInvitePro
export const DocumentInviteEmailTemplate = ({ export const DocumentInviteEmailTemplate = ({
inviterName = 'Lucas Smith', inviterName = 'Lucas Smith',
inviterEmail = 'lucas@documenso.com', inviterEmail = 'lucas@sign.bls.media',
documentName = 'Open Source Pledge.pdf', documentName = 'Open Source Pledge.pdf',
signDocumentLink = 'https://documenso.com', signDocumentLink = 'https://documenso.com',
assetBaseUrl = 'http://localhost:3002', assetBaseUrl = 'http://localhost:3002',
@@ -69,7 +69,7 @@ export const DocumentInviteEmailTemplate = ({
) : ( ) : (
<Img <Img
src={getAssetUrl('/static/logo.png')} src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo" alt="BLS sign Logo"
className="mb-4 h-6" className="mb-4 h-6"
/> />
)} )}

View File

@@ -36,7 +36,7 @@ export const DocumentPendingEmailTemplate = ({
) : ( ) : (
<Img <Img
src={getAssetUrl('/static/logo.png')} src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo" alt="BLS sign Logo"
className="mb-4 h-6" className="mb-4 h-6"
/> />
)} )}

View File

@@ -16,7 +16,7 @@ export interface DocumentRecipientSignedEmailTemplateProps {
export const DocumentRecipientSignedEmailTemplate = ({ export const DocumentRecipientSignedEmailTemplate = ({
documentName = 'Open Source Pledge.pdf', documentName = 'Open Source Pledge.pdf',
recipientName = 'John Doe', recipientName = 'John Doe',
recipientEmail = 'lucas@documenso.com', recipientEmail = 'lucas@sign.bls.media',
assetBaseUrl = 'http://localhost:3002', assetBaseUrl = 'http://localhost:3002',
}: DocumentRecipientSignedEmailTemplateProps) => { }: DocumentRecipientSignedEmailTemplateProps) => {
const { _ } = useLingui(); const { _ } = useLingui();
@@ -44,7 +44,7 @@ export const DocumentRecipientSignedEmailTemplate = ({
) : ( ) : (
<Img <Img
src={getAssetUrl('/static/logo.png')} src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo" alt="BLS sign Logo"
className="mb-4 h-6" className="mb-4 h-6"
/> />
)} )}

View File

@@ -44,7 +44,7 @@ export function DocumentRejectedEmail({
) : ( ) : (
<Img <Img
src={getAssetUrl('/static/logo.png')} src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo" alt="BLS sign Logo"
className="mb-4 h-6" className="mb-4 h-6"
/> />
)} )}

View File

@@ -44,7 +44,7 @@ export function DocumentRejectionConfirmedEmail({
) : ( ) : (
<Img <Img
src={getAssetUrl('/static/logo.png')} src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo" alt="BLS sign Logo"
className="mb-4 h-6" className="mb-4 h-6"
/> />
)} )}

View File

@@ -36,7 +36,7 @@ export const DocumentSelfSignedEmailTemplate = ({
) : ( ) : (
<Img <Img
src={getAssetUrl('/static/logo.png')} src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo" alt="BLS sign Logo"
className="mb-4 h-6" className="mb-4 h-6"
/> />
)} )}

View File

@@ -39,7 +39,7 @@ export const DocumentSuperDeleteEmailTemplate = ({
) : ( ) : (
<Img <Img
src={getAssetUrl('/static/logo.png')} src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo" alt="BLS sign Logo"
className="mb-4 h-6" className="mb-4 h-6"
/> />
)} )}

View File

@@ -36,7 +36,7 @@ export const ForgotPasswordTemplate = ({
) : ( ) : (
<Img <Img
src={getAssetUrl('/static/logo.png')} src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo" alt="BLS sign Logo"
className="mb-4 h-6" className="mb-4 h-6"
/> />
)} )}

View File

@@ -38,7 +38,7 @@ export const RecipientRemovedFromDocumentTemplate = ({
) : ( ) : (
<Img <Img
src={getAssetUrl('/static/logo.png')} src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo" alt="BLS sign Logo"
className="mb-4 h-6" className="mb-4 h-6"
/> />
)} )}

View File

@@ -12,7 +12,7 @@ export type ResetPasswordTemplateProps = Partial<TemplateResetPasswordProps>;
export const ResetPasswordTemplate = ({ export const ResetPasswordTemplate = ({
userName = 'Lucas Smith', userName = 'Lucas Smith',
userEmail = 'lucas@documenso.com', userEmail = 'lucas@sign.bls.media',
assetBaseUrl = 'http://localhost:3002', assetBaseUrl = 'http://localhost:3002',
}: ResetPasswordTemplateProps) => { }: ResetPasswordTemplateProps) => {
const { _ } = useLingui(); const { _ } = useLingui();
@@ -38,7 +38,7 @@ export const ResetPasswordTemplate = ({
) : ( ) : (
<Img <Img
src={getAssetUrl('/static/logo.png')} src={getAssetUrl('/static/logo.png')}
alt="Documenso Logo" alt="BLS sign Logo"
className="mb-4 h-6" className="mb-4 h-6"
/> />
)} )}
@@ -72,7 +72,7 @@ export const ResetPasswordTemplate = ({
<Trans> <Trans>
Didn't request a password change? We are here to help you secure your account, Didn't request a password change? We are here to help you secure your account,
just{' '} just{' '}
<Link className="text-documenso-700 font-normal" href="mailto:hi@documenso.com"> <Link className="text-documenso-700 font-normal" href="mailto:hi@sign.bls.media">
contact us. contact us.
</Link> </Link>
</Trans> </Trans>

View File

@@ -20,7 +20,7 @@ export type TeamEmailRemovedTemplateProps = {
export const TeamEmailRemovedTemplate = ({ export const TeamEmailRemovedTemplate = ({
assetBaseUrl = 'http://localhost:3002', assetBaseUrl = 'http://localhost:3002',
baseUrl = 'https://documenso.com', baseUrl = 'https://documenso.com',
teamEmail = 'example@documenso.com', teamEmail = 'example@sign.bls.media',
teamName = 'Team Name', teamName = 'Team Name',
teamUrl = 'demo', teamUrl = 'demo',
}: TeamEmailRemovedTemplateProps) => { }: TeamEmailRemovedTemplateProps) => {

View File

@@ -22,7 +22,7 @@ export const TeamJoinEmailTemplate = ({
assetBaseUrl = 'http://localhost:3002', assetBaseUrl = 'http://localhost:3002',
baseUrl = 'https://documenso.com', baseUrl = 'https://documenso.com',
memberName = 'John Doe', memberName = 'John Doe',
memberEmail = 'johndoe@documenso.com', memberEmail = 'johndoe@sign.bls.media',
teamName = 'Team Name', teamName = 'Team Name',
teamUrl = 'demo', teamUrl = 'demo',
}: TeamJoinEmailProps) => { }: TeamJoinEmailProps) => {

Some files were not shown because too many files have changed in this diff Show More