Compare commits

...

12 Commits

13 changed files with 98 additions and 104 deletions

View File

@ -2,8 +2,8 @@ declare namespace NodeJS {
export interface ProcessEnv {
NEXT_PUBLIC_WEBAPP_URL?: string;
NEXT_PUBLIC_MARKETING_URL?: string;
NEXT_PRIVATE_INTERNAL_WEBAPP_URL?:string;
NEXT_PRIVATE_INTERNAL_WEBAPP_URL?: string;
NEXT_PRIVATE_DATABASE_URL: string;
NEXT_PUBLIC_STRIPE_COMMUNITY_PLAN_MONTHLY_PRICE_ID: string;

View File

@ -2,7 +2,7 @@ declare namespace NodeJS {
export interface ProcessEnv {
NEXT_PUBLIC_WEBAPP_URL?: string;
NEXT_PUBLIC_MARKETING_URL?: string;
NEXT_PRIVATE_INTERNAL_WEBAPP_URL?:string;
NEXT_PRIVATE_INTERNAL_WEBAPP_URL?: string;
NEXT_PRIVATE_DATABASE_URL: string;

View File

@ -99,7 +99,9 @@ export const DocumentLogsDataTable = ({ documentId }: DocumentLogsDataTableProps
{
header: _(msg`Action`),
accessorKey: 'type',
cell: ({ row }) => <span>{formatDocumentAuditLogAction(_, row.original).description}</span>,
cell: ({ row }) => (
<span>{uppercaseFistLetter(formatDocumentAuditLogAction(row.original).description)}</span>
),
},
{
header: 'IP Address',

View File

@ -193,7 +193,6 @@ export const SignDirectTemplateForm = ({
field={field}
recipient={directRecipient}
onSignField={onSignField}
onUnsignField={onUnsignField}
/>
))
.with(FieldType.NAME, () => (
@ -202,7 +201,6 @@ export const SignDirectTemplateForm = ({
field={field}
recipient={directRecipient}
onSignField={onSignField}
onUnsignField={onUnsignField}
/>
))
.with(FieldType.DATE, () => (
@ -213,7 +211,6 @@ export const SignDirectTemplateForm = ({
dateFormat={template.templateMeta?.dateFormat ?? DEFAULT_DOCUMENT_DATE_FORMAT}
timezone={template.templateMeta?.timezone ?? DEFAULT_DOCUMENT_TIME_ZONE}
onSignField={onSignField}
onUnsignField={onUnsignField}
/>
))
.with(FieldType.EMAIL, () => (
@ -222,7 +219,6 @@ export const SignDirectTemplateForm = ({
field={field}
recipient={directRecipient}
onSignField={onSignField}
onUnsignField={onUnsignField}
/>
))
.with(FieldType.TEXT, () => {

View File

@ -1,6 +1,6 @@
'use client';
import { useTransition } from 'react';
import { useEffect, useTransition } from 'react';
import { useRouter } from 'next/navigation';
@ -25,6 +25,7 @@ import type {
} from '@documenso/trpc/server/field-router/schema';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { useRequiredDocumentAuthContext } from './document-auth-provider';
import { SigningFieldContainer } from './signing-field-container';
export type DateFieldProps = {
@ -42,7 +43,6 @@ export const DateField = ({
dateFormat = DEFAULT_DOCUMENT_DATE_FORMAT,
timezone = DEFAULT_DOCUMENT_TIME_ZONE,
onSignField,
onUnsignField,
}: DateFieldProps) => {
const router = useRouter();
@ -54,13 +54,13 @@ export const DateField = ({
const { mutateAsync: signFieldWithToken, isLoading: isSignFieldWithTokenLoading } =
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
const {
mutateAsync: removeSignedFieldWithToken,
isLoading: isRemoveSignedFieldWithTokenLoading,
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
const { isLoading: isRemoveSignedFieldWithTokenLoading } =
trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
const { executeActionAuthProcedure } = useRequiredDocumentAuthContext();
const localDateString = convertToLocalSystemFormat(field.customText, dateFormat, timezone);
const isDifferentTime = field.inserted && localDateString !== field.customText;
@ -103,37 +103,18 @@ export const DateField = ({
}
};
const onRemove = async () => {
try {
const payload: TRemovedSignedFieldWithTokenMutationSchema = {
token: recipient.token,
fieldId: field.id,
};
if (onUnsignField) {
await onUnsignField(payload);
return;
}
await removeSignedFieldWithToken(payload);
startTransition(() => router.refresh());
} catch (err) {
console.error(err);
toast({
title: _(msg`Error`),
description: _(msg`An error occurred while removing the signature.`),
variant: 'destructive',
useEffect(() => {
if (!field.inserted) {
void executeActionAuthProcedure({
onReauthFormSubmit: async (authOptions) => await onSign(authOptions),
actionTarget: field.type,
});
}
};
}, [field]);
return (
<SigningFieldContainer
field={field}
onSign={onSign}
onRemove={onRemove}
type="Date"
tooltipText={isDifferentTime ? tooltipText : undefined}
>

View File

@ -1,6 +1,6 @@
'use client';
import { useTransition } from 'react';
import { useEffect, useTransition } from 'react';
import { useRouter } from 'next/navigation';
@ -14,12 +14,10 @@ import type { TRecipientActionAuth } from '@documenso/lib/types/document-auth';
import type { Recipient } from '@documenso/prisma/client';
import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
import { trpc } from '@documenso/trpc/react';
import type {
TRemovedSignedFieldWithTokenMutationSchema,
TSignFieldWithTokenMutationSchema,
} from '@documenso/trpc/server/field-router/schema';
import type { TSignFieldWithTokenMutationSchema } from '@documenso/trpc/server/field-router/schema';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { useRequiredDocumentAuthContext } from './document-auth-provider';
import { useRequiredSigningContext } from './provider';
import { SigningFieldContainer } from './signing-field-container';
@ -27,10 +25,9 @@ export type EmailFieldProps = {
field: FieldWithSignature;
recipient: Recipient;
onSignField?: (value: TSignFieldWithTokenMutationSchema) => Promise<void> | void;
onUnsignField?: (value: TRemovedSignedFieldWithTokenMutationSchema) => Promise<void> | void;
};
export const EmailField = ({ field, recipient, onSignField, onUnsignField }: EmailFieldProps) => {
export const EmailField = ({ field, recipient, onSignField }: EmailFieldProps) => {
const router = useRouter();
const { _ } = useLingui();
@ -43,13 +40,13 @@ export const EmailField = ({ field, recipient, onSignField, onUnsignField }: Ema
const { mutateAsync: signFieldWithToken, isLoading: isSignFieldWithTokenLoading } =
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
const {
mutateAsync: removeSignedFieldWithToken,
isLoading: isRemoveSignedFieldWithTokenLoading,
} = trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
const { isLoading: isRemoveSignedFieldWithTokenLoading } =
trpc.field.removeSignedFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
const isLoading = isSignFieldWithTokenLoading || isRemoveSignedFieldWithTokenLoading || isPending;
const { executeActionAuthProcedure } = useRequiredDocumentAuthContext();
const onSign = async (authOptions?: TRecipientActionAuth) => {
try {
const value = providedEmail ?? '';
@ -87,34 +84,17 @@ export const EmailField = ({ field, recipient, onSignField, onUnsignField }: Ema
}
};
const onRemove = async () => {
try {
const payload: TRemovedSignedFieldWithTokenMutationSchema = {
token: recipient.token,
fieldId: field.id,
};
if (onUnsignField) {
await onUnsignField(payload);
return;
}
await removeSignedFieldWithToken(payload);
startTransition(() => router.refresh());
} catch (err) {
console.error(err);
toast({
title: _(msg`Error`),
description: _(msg`An error occurred while removing the signature.`),
variant: 'destructive',
useEffect(() => {
if (!field.inserted) {
void executeActionAuthProcedure({
onReauthFormSubmit: async (authOptions) => await onSign(authOptions),
actionTarget: field.type,
});
}
};
}, [field]);
return (
<SigningFieldContainer field={field} onSign={onSign} onRemove={onRemove} type="Email">
<SigningFieldContainer field={field} type="Email">
{isLoading && (
<div className="bg-background absolute inset-0 flex items-center justify-center rounded-md">
<Loader className="text-primary h-5 w-5 animate-spin md:h-8 md:w-8" />

View File

@ -1,6 +1,6 @@
'use client';
import { useTransition } from 'react';
import { useEffect, useTransition } from 'react';
import { useRouter } from 'next/navigation';
@ -8,6 +8,7 @@ import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { Loader } from 'lucide-react';
import { useDebouncedValue } from '@documenso/lib/client-only/hooks/use-debounced-value';
import { DO_NOT_INVALIDATE_QUERY_ON_MUTATION } from '@documenso/lib/constants/trpc';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import type { TRecipientActionAuth } from '@documenso/lib/types/document-auth';
@ -21,6 +22,7 @@ import type {
} from '@documenso/trpc/server/field-router/schema';
import { useToast } from '@documenso/ui/primitives/use-toast';
import { useRequiredDocumentAuthContext } from './document-auth-provider';
import { useRequiredSigningContext } from './provider';
import { SigningFieldContainer } from './signing-field-container';
@ -44,8 +46,12 @@ export const InitialsField = ({
const { fullName } = useRequiredSigningContext();
const initials = extractInitials(fullName);
const debouncedInitials = useDebouncedValue(initials, 2000);
const [isPending, startTransition] = useTransition();
const { executeActionAuthProcedure } = useRequiredDocumentAuthContext();
const { mutateAsync: signFieldWithToken, isLoading: isSignFieldWithTokenLoading } =
trpc.field.signFieldWithToken.useMutation(DO_NOT_INVALIDATE_QUERY_ON_MUTATION);
@ -58,7 +64,7 @@ export const InitialsField = ({
const onSign = async (authOptions?: TRecipientActionAuth) => {
try {
const value = initials ?? '';
const value = debouncedInitials ?? '';
const payload: TSignFieldWithTokenMutationSchema = {
token: recipient.token,
@ -119,6 +125,15 @@ export const InitialsField = ({
}
};
useEffect(() => {
if (!field.inserted && debouncedInitials) {
void executeActionAuthProcedure({
onReauthFormSubmit: async (authOptions) => await onSign(authOptions),
actionTarget: field.type,
});
}
}, [field, debouncedInitials, field.inserted]);
return (
<SigningFieldContainer field={field} onSign={onSign} onRemove={onRemove} type="Initials">
{isLoading && (

View File

@ -1,6 +1,6 @@
'use client';
import { useState, useTransition } from 'react';
import { useEffect, useState, useTransition } from 'react';
import { useRouter } from 'next/navigation';
@ -8,6 +8,7 @@ import { Trans, msg } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { Loader } from 'lucide-react';
import { useDebouncedValue } from '@documenso/lib/client-only/hooks/use-debounced-value';
import { DO_NOT_INVALIDATE_QUERY_ON_MUTATION } from '@documenso/lib/constants/trpc';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import type { TRecipientActionAuth } from '@documenso/lib/types/document-auth';
@ -44,6 +45,8 @@ export const NameField = ({ field, recipient, onSignField, onUnsignField }: Name
const { fullName: providedFullName, setFullName: setProvidedFullName } =
useRequiredSigningContext();
const debouncedProvidedFullName = useDebouncedValue(providedFullName, 2000);
const { executeActionAuthProcedure } = useRequiredDocumentAuthContext();
const [isPending, startTransition] = useTransition();
@ -151,6 +154,15 @@ export const NameField = ({ field, recipient, onSignField, onUnsignField }: Name
}
};
useEffect(() => {
if (!field.inserted && providedFullName) {
void executeActionAuthProcedure({
onReauthFormSubmit: async (authOptions) => await onSign(authOptions),
actionTarget: field.type,
});
}
}, [field, debouncedProvidedFullName]);
return (
<SigningFieldContainer
field={field}

View File

@ -68,6 +68,8 @@ export const SigningFieldContainer = ({
const parsedFieldMeta = field.fieldMeta ? ZFieldMetaSchema.parse(field.fieldMeta) : undefined;
const readOnlyField = parsedFieldMeta?.readOnly || false;
const automatedFields = ['Email', 'Date'].includes(type ?? '');
const handleInsertField = async () => {
if (field.inserted || !onSign) {
return;
@ -146,7 +148,7 @@ export const SigningFieldContainer = ({
</button>
)}
{type === 'Date' && field.inserted && !loading && !readOnlyField && (
{field.inserted && !automatedFields && !loading && !readOnlyField && (
<Tooltip delayDuration={0}>
<TooltipTrigger asChild>
<button
@ -172,14 +174,18 @@ export const SigningFieldContainer = ({
</button>
)}
{type !== 'Date' && type !== 'Checkbox' && field.inserted && !loading && !readOnlyField && (
<button
className="text-destructive bg-background/50 absolute inset-0 z-10 flex h-full w-full items-center justify-center rounded-md text-sm opacity-0 duration-200 group-hover:opacity-100"
onClick={onRemoveSignedFieldClick}
>
<Trans>Remove</Trans>
</button>
)}
{type !== 'Checkbox' &&
!automatedFields &&
field.inserted &&
!loading &&
!readOnlyField && (
<button
className="text-destructive bg-background/50 absolute inset-0 z-10 flex h-full w-full items-center justify-center rounded-md text-sm opacity-0 duration-200 group-hover:opacity-100"
onClick={onRemoveSignedFieldClick}
>
Remove
</button>
)}
{children}
</FieldRootContainer>

View File

@ -5,7 +5,11 @@ import { getToken } from 'next-auth/jwt';
import { LOCAL_FEATURE_FLAGS } from '@documenso/lib/constants/feature-flags';
import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-post-hog-server-client';
import { NEXT_PUBLIC_MARKETING_URL, NEXT_PUBLIC_WEBAPP_URL, NEXT_PRIVATE_INTERNAL_WEBAPP_URL } from '../../constants/app';
import {
NEXT_PRIVATE_INTERNAL_WEBAPP_URL,
NEXT_PUBLIC_MARKETING_URL,
NEXT_PUBLIC_WEBAPP_URL,
} from '../../constants/app';
import { extractDistinctUserId, mapJwtToFlagProperties } from './get';
/**

View File

@ -7,7 +7,11 @@ import { getToken } from 'next-auth/jwt';
import { LOCAL_FEATURE_FLAGS, extractPostHogConfig } from '@documenso/lib/constants/feature-flags';
import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-post-hog-server-client';
import { NEXT_PUBLIC_MARKETING_URL, NEXT_PUBLIC_WEBAPP_URL, NEXT_PRIVATE_INTERNAL_WEBAPP_URL } from '../../constants/app';
import {
NEXT_PRIVATE_INTERNAL_WEBAPP_URL,
NEXT_PUBLIC_MARKETING_URL,
NEXT_PUBLIC_WEBAPP_URL,
} from '../../constants/app';
/**
* Evaluate a single feature flag based on the current user if possible.
@ -67,7 +71,7 @@ export default async function handleFeatureFlagGet(req: Request) {
if (origin.startsWith(NEXT_PUBLIC_MARKETING_URL() ?? 'http://localhost:3001')) {
res.headers.set('Access-Control-Allow-Origin', origin);
}
if (origin.startsWith(NEXT_PRIVATE_INTERNAL_WEBAPP_URL ?? 'http://localhost:3000')) {
res.headers.set('Access-Control-Allow-Origin', origin);
}

View File

@ -518,10 +518,8 @@ msgid "An error occurred while removing the field."
msgstr "Ein Fehler ist beim Entfernen des Feldes aufgetreten."
#: apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx:152
#: apps/web/src/app/(signing)/sign/[token]/date-field.tsx:126
#: apps/web/src/app/(signing)/sign/[token]/dropdown-field.tsx:137
#: apps/web/src/app/(signing)/sign/[token]/email-field.tsx:110
#: apps/web/src/app/(signing)/sign/[token]/name-field.tsx:148
#: apps/web/src/app/(signing)/sign/[token]/name-field.tsx:151
#: apps/web/src/app/(signing)/sign/[token]/number-field.tsx:195
#: apps/web/src/app/(signing)/sign/[token]/radio-field.tsx:129
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:173
@ -717,7 +715,7 @@ msgstr "Avatar aktualisiert"
msgid "Awaiting email confirmation"
msgstr "Warte auf E-Mail-Bestätigung"
#: apps/web/src/app/(recipient)/d/[token]/sign-direct-template.tsx:369
#: apps/web/src/app/(recipient)/d/[token]/sign-direct-template.tsx:365
#: apps/web/src/components/(dashboard)/settings/layout/activity-back.tsx:20
#: apps/web/src/components/forms/v2/signup.tsx:509
msgid "Back"
@ -1803,7 +1801,6 @@ msgstr "Geben Sie hier Ihren Text ein"
#: apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx:151
#: apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx:212
#: apps/web/src/app/(signing)/sign/[token]/date-field.tsx:99
#: apps/web/src/app/(signing)/sign/[token]/date-field.tsx:125
#: apps/web/src/app/(signing)/sign/[token]/dropdown-field.tsx:105
#: apps/web/src/app/(signing)/sign/[token]/dropdown-field.tsx:136
#: apps/web/src/app/(signing)/sign/[token]/email-field.tsx:83
@ -2361,7 +2358,7 @@ msgstr "Meine Vorlagen"
#: apps/web/src/app/(dashboard)/templates/use-template-dialog.tsx:287
#: apps/web/src/app/(dashboard)/templates/use-template-dialog.tsx:294
#: apps/web/src/app/(signing)/sign/[token]/complete/claim-account.tsx:119
#: apps/web/src/app/(signing)/sign/[token]/name-field.tsx:170
#: apps/web/src/app/(signing)/sign/[token]/name-field.tsx:182
#: apps/web/src/components/(teams)/dialogs/add-team-email-dialog.tsx:153
#: apps/web/src/components/(teams)/dialogs/update-team-email-dialog.tsx:141
#: apps/web/src/components/forms/signup.tsx:164
@ -2809,7 +2806,7 @@ msgstr "Öffentlicher Profil-Benutzername"
msgid "Public templates are connected to your public profile. Any modifications to public templates will also appear in your public profile."
msgstr "Öffentliche Vorlagen sind mit Ihrem öffentlichen Profil verbunden. Änderungen an öffentlichen Vorlagen erscheinen auch in Ihrem öffentlichen Profil."
#: apps/web/src/app/(signing)/sign/[token]/signing-field-container.tsx:144
#: apps/web/src/app/(signing)/sign/[token]/signing-field-container.tsx:146
msgid "Read only field"
msgstr "Nur-Lese-Feld"
@ -3188,7 +3185,7 @@ msgstr "Unterzeichnen"
msgid "Sign as {0} <0>({1})</0>"
msgstr "Unterzeichnen als {0} <0>({1})</0>"
#: apps/web/src/app/(signing)/sign/[token]/name-field.tsx:183
#: apps/web/src/app/(signing)/sign/[token]/name-field.tsx:195
msgid "Sign as<0>{0} <1>({1})</1></0>"
msgstr "Unterzeichnen als<0>{0} <1>({1})</1></0>"

View File

@ -513,10 +513,8 @@ msgid "An error occurred while removing the field."
msgstr "An error occurred while removing the field."
#: apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx:152
#: apps/web/src/app/(signing)/sign/[token]/date-field.tsx:126
#: apps/web/src/app/(signing)/sign/[token]/dropdown-field.tsx:137
#: apps/web/src/app/(signing)/sign/[token]/email-field.tsx:110
#: apps/web/src/app/(signing)/sign/[token]/name-field.tsx:148
#: apps/web/src/app/(signing)/sign/[token]/name-field.tsx:151
#: apps/web/src/app/(signing)/sign/[token]/number-field.tsx:195
#: apps/web/src/app/(signing)/sign/[token]/radio-field.tsx:129
#: apps/web/src/app/(signing)/sign/[token]/signature-field.tsx:173
@ -712,7 +710,7 @@ msgstr "Avatar Updated"
msgid "Awaiting email confirmation"
msgstr "Awaiting email confirmation"
#: apps/web/src/app/(recipient)/d/[token]/sign-direct-template.tsx:369
#: apps/web/src/app/(recipient)/d/[token]/sign-direct-template.tsx:365
#: apps/web/src/components/(dashboard)/settings/layout/activity-back.tsx:20
#: apps/web/src/components/forms/v2/signup.tsx:509
msgid "Back"
@ -1798,7 +1796,6 @@ msgstr "Enter your text here"
#: apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx:151
#: apps/web/src/app/(signing)/sign/[token]/checkbox-field.tsx:212
#: apps/web/src/app/(signing)/sign/[token]/date-field.tsx:99
#: apps/web/src/app/(signing)/sign/[token]/date-field.tsx:125
#: apps/web/src/app/(signing)/sign/[token]/dropdown-field.tsx:105
#: apps/web/src/app/(signing)/sign/[token]/dropdown-field.tsx:136
#: apps/web/src/app/(signing)/sign/[token]/email-field.tsx:83
@ -2356,7 +2353,7 @@ msgstr "My templates"
#: apps/web/src/app/(dashboard)/templates/use-template-dialog.tsx:287
#: apps/web/src/app/(dashboard)/templates/use-template-dialog.tsx:294
#: apps/web/src/app/(signing)/sign/[token]/complete/claim-account.tsx:119
#: apps/web/src/app/(signing)/sign/[token]/name-field.tsx:170
#: apps/web/src/app/(signing)/sign/[token]/name-field.tsx:182
#: apps/web/src/components/(teams)/dialogs/add-team-email-dialog.tsx:153
#: apps/web/src/components/(teams)/dialogs/update-team-email-dialog.tsx:141
#: apps/web/src/components/forms/signup.tsx:164
@ -2804,7 +2801,7 @@ msgstr "Public profile username"
msgid "Public templates are connected to your public profile. Any modifications to public templates will also appear in your public profile."
msgstr "Public templates are connected to your public profile. Any modifications to public templates will also appear in your public profile."
#: apps/web/src/app/(signing)/sign/[token]/signing-field-container.tsx:144
#: apps/web/src/app/(signing)/sign/[token]/signing-field-container.tsx:146
msgid "Read only field"
msgstr "Read only field"