fix: resolve follow-up issues from oxlint migration

This commit is contained in:
ephraimduncan 2026-03-06 05:45:16 +00:00
parent ae02169e97
commit 835c55e2ca
No known key found for this signature in database
GPG key ID: EA98563B876B2CD8
68 changed files with 433 additions and 430 deletions

View file

@ -12,11 +12,14 @@
"dependencies": {
"@documenso/prisma": "*",
"luxon": "^3.7.2",
"next": "15.5.12"
"next": "15.5.12",
"react": "^18",
"react-dom": "^18"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "18.3.27",
"@types/react-dom": "^18",
"typescript": "5.6.2"
}
}

166
package-lock.json generated
View file

@ -39,6 +39,8 @@
"@ts-rest/core": "^3.52.1",
"@ts-rest/open-api": "^3.52.1",
"@ts-rest/serverless": "^3.52.1",
"@types/react": "18.3.27",
"@types/react-dom": "^18",
"dotenv": "^17.2.3",
"dotenv-cli": "^11.0.0",
"husky": "^9.1.7",
@ -57,6 +59,7 @@
"prisma-extension-kysely": "^3.0.0",
"prisma-json-types-generator": "^3.6.2",
"prisma-kysely": "^2.3.0",
"react-dom": "^18",
"rimraf": "^6.1.2",
"superjson": "^2.2.5",
"syncpack": "^14.0.0-alpha.27",
@ -100,6 +103,26 @@
"typescript": "^5.9.3"
}
},
"apps/docs/node_modules/@types/react": {
"version": "19.2.14",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
"dev": true,
"license": "MIT",
"dependencies": {
"csstype": "^3.2.2"
}
},
"apps/docs/node_modules/@types/react-dom": {
"version": "19.2.3",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"@types/react": "^19.2.0"
}
},
"apps/docs/node_modules/react": {
"version": "19.2.4",
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
@ -109,31 +132,38 @@
"node": ">=0.10.0"
}
},
"apps/docs/node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
"apps/docs/node_modules/react-dom": {
"version": "19.2.4",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
"license": "MIT",
"dependencies": {
"scheduler": "^0.27.0"
},
"engines": {
"node": ">=14.17"
"peerDependencies": {
"react": "^19.2.4"
}
},
"apps/docs/node_modules/scheduler": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
"license": "MIT"
},
"apps/openpage-api": {
"name": "@documenso/openpage-api",
"version": "1.0.0",
"dependencies": {
"@documenso/prisma": "*",
"luxon": "^3.7.2",
"next": "15.5.12"
"next": "15.5.12",
"react": "^18",
"react-dom": "^18"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "18.3.27",
"@types/react-dom": "^18",
"typescript": "5.6.2"
}
},
@ -147,17 +177,6 @@
"undici-types": "~6.21.0"
}
},
"apps/openpage-api/node_modules/@types/react": {
"version": "18.3.27",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz",
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.2.2"
}
},
"apps/openpage-api/node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
@ -362,27 +381,6 @@
"undici-types": "~6.21.0"
}
},
"apps/remix/node_modules/@types/react": {
"version": "18.3.27",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz",
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.2.2"
}
},
"apps/remix/node_modules/@types/react-dom": {
"version": "18.3.7",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
"integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"@types/react": "^18.0.0"
}
},
"apps/remix/node_modules/lucide-react": {
"version": "0.554.0",
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.554.0.tgz",
@ -392,19 +390,6 @@
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"apps/remix/node_modules/react-dom": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.2"
},
"peerDependencies": {
"react": "^18.3.1"
}
},
"apps/remix/node_modules/tailwindcss": {
"version": "3.4.19",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",
@ -13465,26 +13450,26 @@
"version": "15.7.15",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
"integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/react": {
"version": "19.2.14",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
"integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
"version": "18.3.27",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz",
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.2.2"
}
},
"node_modules/@types/react-dom": {
"version": "19.2.3",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"version": "18.3.7",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
"integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"@types/react": "^19.2.0"
"@types/react": "^18.0.0"
}
},
"node_modules/@types/resolve": {
@ -24659,23 +24644,18 @@
}
},
"node_modules/react-dom": {
"version": "19.2.4",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz",
"integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==",
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
"license": "MIT",
"dependencies": {
"scheduler": "^0.27.0"
"loose-envify": "^1.1.0",
"scheduler": "^0.23.2"
},
"peerDependencies": {
"react": "^19.2.4"
"react": "^18.3.1"
}
},
"node_modules/react-dom/node_modules/scheduler": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
"license": "MIT"
},
"node_modules/react-draggable": {
"version": "4.4.6",
"resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.6.tgz",
@ -29762,27 +29742,6 @@
"typescript": "5.6.2"
}
},
"packages/ui/node_modules/@types/react": {
"version": "18.3.27",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz",
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.2.2"
}
},
"packages/ui/node_modules/@types/react-dom": {
"version": "18.3.7",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
"integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"@types/react": "^18.0.0"
}
},
"packages/ui/node_modules/lucide-react": {
"version": "0.554.0",
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.554.0.tgz",
@ -29792,19 +29751,6 @@
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"packages/ui/node_modules/react-dom": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.2"
},
"peerDependencies": {
"react": "^18.3.1"
}
},
"packages/ui/node_modules/tailwind-merge": {
"version": "1.14.0",
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.14.0.tgz",

View file

@ -76,12 +76,15 @@
"prisma-extension-kysely": "^3.0.0",
"prisma-json-types-generator": "^3.6.2",
"prisma-kysely": "^2.3.0",
"react-dom": "^18",
"rimraf": "^6.1.2",
"superjson": "^2.2.5",
"syncpack": "^14.0.0-alpha.27",
"turbo": "^1.13.4",
"vite": "^7.2.4",
"vite-plugin-static-copy": "^3.1.4",
"@types/react": "18.3.27",
"@types/react-dom": "^18",
"zod-openapi": "^4.2.4",
"zod-prisma-types": "3.3.5"
},

View file

@ -189,7 +189,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
fields: parsedMetaFields,
},
};
} catch (err) {
} catch {
return {
status: 404,
body: {
@ -276,7 +276,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
status: 200,
body: { downloadUrl: url },
};
} catch (err) {
} catch {
return {
status: 500,
body: {
@ -341,7 +341,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
completedAt: deletedDocument.completedAt,
},
};
} catch (err) {
} catch {
return {
status: 404,
body: {
@ -478,7 +478,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
})),
},
};
} catch (err) {
} catch {
return {
status: 404,
body: {
@ -593,7 +593,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
template: fullTemplate,
},
};
} catch (err) {
} catch {
return {
status: 404,
body: {
@ -637,7 +637,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
updatedAt: deletedTemplate.updatedAt,
},
};
} catch (err) {
} catch {
return {
status: 404,
body: {
@ -1077,7 +1077,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
message: 'Document resend successfully initiated',
},
};
} catch (err) {
} catch {
return {
status: 500,
body: {
@ -1185,7 +1185,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
signingUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${newRecipient.token}`,
},
};
} catch (err) {
} catch {
return {
status: 500,
body: {

View file

@ -54,13 +54,11 @@ export class MailChannelsTransport implements Transport<SentMessageInfo> {
const mailCc = this.toMailChannelsAddresses(mail.data.cc);
const mailBcc = this.toMailChannelsAddresses(mail.data.bcc);
const from: MailChannelsAddress =
typeof mail.data.from === 'string'
? { email: mail.data.from }
: {
email: mail.data.from?.address,
name: mail.data.from?.name,
};
const [from] = this.toMailChannelsAddresses(mail.data.from);
if (!from) {
return callback(new Error('Missing required field "from"'), null);
}
const requestHeaders: Record<string, string> = {
'Content-Type': 'application/json',
@ -70,56 +68,15 @@ export class MailChannelsTransport implements Transport<SentMessageInfo> {
requestHeaders['X-Auth-Token'] = this._options.apiKey;
}
fetch(this._options.endpoint, {
method: 'POST',
headers: requestHeaders,
body: JSON.stringify({
from: from,
subject: mail.data.subject,
personalizations: [
{
to: mailTo,
cc: mailCc.length > 0 ? mailCc : undefined,
bcc: mailBcc.length > 0 ? mailBcc : undefined,
dkim_domain: env('NEXT_PRIVATE_MAILCHANNELS_DKIM_DOMAIN') || undefined,
dkim_selector: env('NEXT_PRIVATE_MAILCHANNELS_DKIM_SELECTOR') || undefined,
dkim_private_key: env('NEXT_PRIVATE_MAILCHANNELS_DKIM_PRIVATE_KEY') || undefined,
},
],
content: [
{
type: 'text/plain',
value: mail.data.text?.toString('utf-8') ?? '',
},
{
type: 'text/html',
value: mail.data.html?.toString('utf-8') ?? '',
},
],
}),
})
.then((res) => {
if (res.status >= 200 && res.status <= 299) {
return callback(null, {
messageId: '',
envelope: {
from: mail.data.from,
to: mail.data.to,
},
accepted: mail.data.to,
rejected: [],
pending: [],
});
}
res
.json()
.then((data) => callback(new Error(`MailChannels error: ${data.message}`), null))
.catch((err) => callback(err, null));
})
.catch((err) => {
return callback(err, null);
});
void this.sendMailRequest({
callback,
from,
mail,
mailBcc,
mailCc,
mailTo,
requestHeaders,
});
}
/**
@ -154,4 +111,72 @@ export class MailChannelsTransport implements Transport<SentMessageInfo> {
},
];
}
private async sendMailRequest({
callback,
from,
mail,
mailBcc,
mailCc,
mailTo,
requestHeaders,
}: {
callback: (_err: Error | null, _info: SentMessageInfo) => void;
from: MailChannelsAddress;
mail: MailMessage;
mailBcc: Array<MailChannelsAddress>;
mailCc: Array<MailChannelsAddress>;
mailTo: Array<MailChannelsAddress>;
requestHeaders: Record<string, string>;
}) {
try {
const response = await fetch(this._options.endpoint, {
method: 'POST',
headers: requestHeaders,
body: JSON.stringify({
from,
subject: mail.data.subject,
personalizations: [
{
to: mailTo,
cc: mailCc.length > 0 ? mailCc : undefined,
bcc: mailBcc.length > 0 ? mailBcc : undefined,
dkim_domain: env('NEXT_PRIVATE_MAILCHANNELS_DKIM_DOMAIN') || undefined,
dkim_selector: env('NEXT_PRIVATE_MAILCHANNELS_DKIM_SELECTOR') || undefined,
dkim_private_key: env('NEXT_PRIVATE_MAILCHANNELS_DKIM_PRIVATE_KEY') || undefined,
},
],
content: [
{
type: 'text/plain',
value: mail.data.text?.toString('utf-8') ?? '',
},
{
type: 'text/html',
value: mail.data.html?.toString('utf-8') ?? '',
},
],
}),
});
if (response.status >= 200 && response.status <= 299) {
return callback(null, {
messageId: '',
envelope: {
from: mail.data.from,
to: mail.data.to,
},
accepted: mail.data.to,
rejected: [],
pending: [],
});
}
const data = await response.json();
return callback(new Error(`MailChannels error: ${data.message}`), null);
} catch (error) {
return callback(error instanceof Error ? error : new Error('Failed to send email'), null);
}
}
}

View file

@ -39,7 +39,7 @@ export function useAnalytics() {
*
* @param eventFlag The event to check against feature flags to determine whether tracking is enabled.
*/
const startSessionRecording = (eventFlag?: string) => {
const startSessionRecording = (_eventFlag?: string) => {
return;
// const isSessionRecordingEnabled = featureFlags.getFlag(FEATURE_FLAG_GLOBAL_SESSION_RECORDING);
// const isSessionRecordingEnabledForEvent = Boolean(eventFlag && featureFlags.getFlag(eventFlag));

View file

@ -40,7 +40,7 @@ export function useCopyShareLink({ onSuccess, onError }: UseCopyShareLinkOptions
}
onSuccess?.();
} catch (e) {
} catch {
onError?.();
}
};

View file

@ -14,13 +14,16 @@ export function useCopyToClipboard(): [CopiedValue, CopyFn] {
return false;
}
const isClipboardApiSupported = Boolean(typeof ClipboardItem && navigator.clipboard.write);
const isClipboardApiSupported =
typeof ClipboardItem !== 'undefined' && typeof navigator.clipboard.write === 'function';
// Try to save to clipboard then save it in the state if worked
try {
isClipboardApiSupported
? await handleClipboardApiCopy(text, blobType)
: await handleWriteTextCopy(text);
if (isClipboardApiSupported) {
await handleClipboardApiCopy(text, blobType);
} else {
await handleWriteTextCopy(text);
}
setCopiedText(await text);
return true;
@ -41,7 +44,7 @@ export function useCopyToClipboard(): [CopiedValue, CopyFn] {
const handleClipboardApiCopy = async (value: CopyValue, blobType = 'text/plain') => {
try {
await navigator.clipboard.write([new ClipboardItem({ [blobType]: value })]);
} catch (e) {
} catch {
// Fallback attempt.
await handleWriteTextCopy(value);
}

View file

@ -251,7 +251,7 @@ export const useEditorFields = ({
const getFieldByFormId = useCallback(
(formId: string): TLocalField | undefined => {
return localFields.find((field) => field.formId === formId) as TLocalField | undefined;
return localFields.find((field) => field.formId === formId);
},
[localFields],
);

View file

@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/consistent-type-assertions */
import { RefObject, useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
/**
* Calculate the width and height of a text element.

View file

@ -276,7 +276,7 @@ export const EnvelopeEditorProvider = ({
[envelope.recipients],
);
const { refetch: reloadEnvelope, isLoading: isReloadingEnvelope } = trpc.envelope.get.useQuery(
const { refetch: reloadEnvelope, isLoading: _isReloadingEnvelope } = trpc.envelope.get.useQuery(
{
envelopeId: envelope.id,
},

View file

@ -189,7 +189,7 @@ export const EnvelopeRenderProvider = ({
}, [envelope.envelopeItems]);
const recipientIds = useMemo(
() => recipients.map((recipient) => recipient.id).sort(),
() => recipients.map((recipient) => recipient.id).sort((left, right) => left - right),
[recipients],
);

View file

@ -170,5 +170,8 @@ export const convertToLocalSystemFormat = (
};
export const isValidDateFormat = (dateFormat: unknown): dateFormat is ValidDateFormat => {
return VALID_DATE_FORMAT_VALUES.includes(dateFormat as ValidDateFormat);
return (
typeof dateFormat === 'string' &&
VALID_DATE_FORMAT_VALUES.some((validDateFormat) => validDateFormat === dateFormat)
);
};

View file

@ -73,4 +73,5 @@ export const SUPPORTED_LANGUAGES: Record<string, SupportedLanguage> = {
} satisfies Record<SupportedLanguageCodes, SupportedLanguage>;
export const isValidLanguageCode = (code: unknown): code is SupportedLanguageCodes =>
SUPPORTED_LANGUAGE_CODES.includes(code as SupportedLanguageCodes);
typeof code === 'string' &&
SUPPORTED_LANGUAGE_CODES.some((languageCode) => languageCode === code);

View file

@ -91,11 +91,11 @@ export class InngestJobProvider extends BaseJobProvider {
return {
wait: step.sleep,
logger: {
info: ctx.logger.info,
debug: ctx.logger.debug,
error: ctx.logger.error,
warn: ctx.logger.warn,
log: ctx.logger.info,
info: (...args) => ctx.logger.info(...args),
debug: (...args) => ctx.logger.debug(...args),
error: (...args) => ctx.logger.error(...args),
warn: (...args) => ctx.logger.warn(...args),
log: (...args) => ctx.logger.info(...args),
},
runTask: async (cacheKey, callback) => {
const result = await step.run(cacheKey, callback);

View file

@ -7,7 +7,7 @@ import type { TExecuteWebhookJobDefinition } from './execute-webhook';
export const run = async ({
payload,
io,
io: _io,
}: {
payload: TExecuteWebhookJobDefinition;
io: JobRunIO;
@ -28,10 +28,11 @@ export const run = async ({
createdAt: new Date().toISOString(),
webhookEndpoint: url,
};
const requestBody: Prisma.InputJsonValue = JSON.parse(JSON.stringify(payloadData));
const response = await fetch(url, {
method: 'POST',
body: JSON.stringify(payloadData),
body: JSON.stringify(requestBody),
headers: {
'Content-Type': 'application/json',
'X-Documenso-Secret': secret ?? '',
@ -44,7 +45,7 @@ export const run = async ({
try {
responseBody = JSON.parse(body);
} catch (err) {
} catch {
responseBody = body;
}
@ -53,7 +54,7 @@ export const run = async ({
url,
event,
status: response.ok ? WebhookCallStatus.SUCCESS : WebhookCallStatus.FAILED,
requestBody: payloadData as Prisma.InputJsonValue,
requestBody,
responseCode: response.status,
responseBody,
responseHeaders: Object.fromEntries(response.headers.entries()),

View file

@ -91,9 +91,13 @@ export const adminFindUnsealedDocuments = async ({
]);
const count = Number(countResult[0]?.count ?? 0);
const formattedData: AdminUnsealedDocument[] = data.map((document) => ({
...document,
id: String(document.id),
}));
return {
data: data as unknown as AdminUnsealedDocument[],
data: formattedData,
count,
currentPage: Math.max(page, 1),
perPage,

View file

@ -32,7 +32,7 @@ export type TeamInsights = {
export type UserInsights = {
id: number;
name: string;
name: string | null;
email: string;
documentCount: number;
signedDocumentCount: number;
@ -98,7 +98,7 @@ export async function getOrganisationDetailedInsights({
case 'documents':
return await getDocumentInsights(organisationId, offset, perPage, createdAtFrom);
default:
throw new Error(`Invalid view: ${view}`);
throw new Error('Invalid view');
}
})();
@ -149,9 +149,14 @@ async function getTeamInsights(
const [teams, countResult] = await Promise.all([teamsQuery.execute(), countQuery.execute()]);
const count = Number(countResult[0]?.count || 0);
const teamInsights: TeamInsights[] = teams.map((team) => ({
...team,
memberCount: Number(team.memberCount),
documentCount: Number(team.documentCount),
}));
return {
teams: teams as TeamInsights[],
teams: teamInsights,
users: [],
documents: [],
totalPages: Math.ceil(Number(count) / perPage),
@ -208,10 +213,15 @@ async function getUserInsights(
const [users, countResult] = await Promise.all([usersQuery.execute(), countQuery.execute()]);
const count = Number(countResult[0]?.count || 0);
const userInsights: UserInsights[] = users.map((user) => ({
...user,
documentCount: Number(user.documentCount),
signedDocumentCount: Number(user.signedDocumentCount),
}));
return {
teams: [],
users: users as UserInsights[],
users: userInsights,
documents: [],
totalPages: Math.ceil(Number(count) / perPage),
};
@ -223,18 +233,13 @@ async function getDocumentInsights(
perPage: number,
createdAtFrom: Date | null,
): Promise<OrganisationDetailedInsights> {
let documentsQuery = kyselyPrisma.$kysely
const documentsQuery = kyselyPrisma.$kysely
.selectFrom('Envelope as e')
.innerJoin('Team as t', 'e.teamId', 't.id')
.where('t.organisationId', '=', organisationId)
.where('e.deletedAt', 'is', null)
.where(() => sql`e.type = ${EnvelopeType.DOCUMENT}::"EnvelopeType"`);
if (createdAtFrom) {
documentsQuery = documentsQuery.where('e.createdAt', '>=', createdAtFrom);
}
documentsQuery = documentsQuery
.where(() => sql`e.type = ${EnvelopeType.DOCUMENT}::"EnvelopeType"`)
.$if(!!createdAtFrom, (qb) => qb.where('e.createdAt', '>=', createdAtFrom!))
.select([
'e.id as id',
'e.title as title',
@ -247,33 +252,33 @@ async function getDocumentInsights(
.limit(perPage)
.offset(offset);
let countQuery = kyselyPrisma.$kysely
const countQuery = kyselyPrisma.$kysely
.selectFrom('Envelope as e')
.innerJoin('Team as t', 'e.teamId', 't.id')
.where('t.organisationId', '=', organisationId)
.where('e.deletedAt', 'is', null)
.where(() => sql`e.type = ${EnvelopeType.DOCUMENT}::"EnvelopeType"`);
if (createdAtFrom) {
countQuery = countQuery.where('e.createdAt', '>=', createdAtFrom);
}
countQuery = countQuery.select(({ fn }) => [fn.countAll().as('count')]);
.where(() => sql`e.type = ${EnvelopeType.DOCUMENT}::"EnvelopeType"`)
.$if(!!createdAtFrom, (qb) => qb.where('e.createdAt', '>=', createdAtFrom!))
.select(sql<number>`count(*)`.as('count'));
const [documents, countResult] = await Promise.all([
documentsQuery.execute(),
countQuery.execute(),
countQuery.executeTakeFirst(),
]);
const count = Number((countResult[0] as { count: number })?.count || 0);
const count = Number(countResult?.count || 0);
const documentInsights: DocumentInsights[] = documents.map((document) => ({
title: document.title,
status: document.status,
createdAt: document.createdAt,
completedAt: document.completedAt,
teamName: document.teamName,
id: String(document.id),
}));
return {
teams: [],
users: [],
documents: documents.map((doc) => ({
...doc,
id: String((doc as { id: number }).id),
})) as DocumentInsights[],
documents: documentInsights,
totalPages: Math.ceil(Number(count) / perPage),
};
}

View file

@ -47,7 +47,7 @@ export type PdfToImagesOptions = {
export const pdfToImages = async (pdfBytes: Uint8Array, options: PdfToImagesOptions = {}) => {
const { scale = 2 } = options;
const task = await pdfjsLib.getDocument({
const task = pdfjsLib.getDocument({
data: pdfBytes,
CanvasFactory: SkiaCanvasFactory,
});

View file

@ -4,6 +4,7 @@ import { omit } from 'remeda';
import { prisma } from '@documenso/prisma';
import { AppError, AppErrorCode } from '../../errors/app-error';
import { ZFieldMetaSchema } from '../../types/field-meta';
import {
ZWebhookDocumentSchema,
mapEnvelopeToWebhookDocumentPayload,
@ -158,7 +159,7 @@ export const duplicateEnvelope = async ({ id, userId, teamId }: DuplicateEnvelop
height: field.height,
customText: '',
inserted: false,
fieldMeta: field.fieldMeta as PrismaJson.FieldMeta,
fieldMeta: field.fieldMeta ? ZFieldMetaSchema.parse(field.fieldMeta) : undefined,
})),
},
},

View file

@ -25,7 +25,7 @@ export type GetRecipientEnvelopeByTokenOptions = {
export const getEnvelopeForDirectTemplateSigning = async ({
token,
userId,
accessAuth,
accessAuth: _accessAuth,
}: GetRecipientEnvelopeByTokenOptions): Promise<EnvelopeForSigningResponse> => {
if (!token) {
throw new AppError(AppErrorCode.NOT_FOUND, {

View file

@ -193,12 +193,12 @@ export const updateEnvelope = async ({
isDeepEqual(documentGlobalActionAuth, newGlobalActionAuth);
const isDocumentVisibilitySame =
data.visibility === undefined || data.visibility === envelope.visibility;
const isFolderSame = data.folderId === undefined || data.folderId === envelope.folderId;
const isTemplateTypeSame =
const _isFolderSame = data.folderId === undefined || data.folderId === envelope.folderId;
const _isTemplateTypeSame =
data.templateType === undefined || data.templateType === envelope.templateType;
const isPublicDescriptionSame =
const _isPublicDescriptionSame =
data.publicDescription === undefined || data.publicDescription === envelope.publicDescription;
const isPublicTitleSame =
const _isPublicTitleSame =
data.publicTitle === undefined || data.publicTitle === envelope.publicTitle;
const auditLogs: CreateDocumentAuditLogDataResponse[] = [];

View file

@ -27,6 +27,7 @@ Konva.Util['createCanvasElement'] = () => {
get: () => node,
});
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
return node as unknown as HTMLCanvasElement;
};
@ -34,6 +35,7 @@ Konva.Util.createImageElement = () => {
const node = new Image();
node.toString = () => '[object HTMLImageElement]';
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
return node as unknown as HTMLImageElement;
};

View file

@ -6,7 +6,7 @@ import { NEXT_PRIVATE_INTERNAL_WEBAPP_URL } from '../../constants/app';
* Adds a rejection stamp to each page of a PDF document.
* The stamp is placed in the center of the page.
*/
export async function addRejectionStampToPdf(pdf: PDF, reason: string): Promise<PDF> {
export async function addRejectionStampToPdf(pdf: PDF, _reason: string): Promise<PDF> {
const pages = pdf.getPages();
const fontBytes = await fetch(`${NEXT_PRIVATE_INTERNAL_WEBAPP_URL()}/fonts/noto-sans.ttf`).then(

View file

@ -694,10 +694,10 @@ const setTextFieldFontSize = (textField: PDFTextField, font: PDFFont, fontSize:
try {
textField.setFontSize(fontSize);
} catch (err) {
} catch {
let da = textField.acroField.getDefaultAppearance() ?? '';
da += `\n ${setFontAndSize(font.name, fontSize)}`;
da += `\n ${String(setFontAndSize(font.name, fontSize))}`;
textField.acroField.setDefaultAppearance(da);
}

View file

@ -51,7 +51,7 @@ const parser = new UAParser();
const textMutedForegroundLight = '#929DAE';
const textForeground = '#000';
const textMutedForeground = '#64748B';
const textBase = 10;
const _textBase = 10;
const textSm = 9;
const textXs = 8;
const fontMedium = '500';

View file

@ -75,13 +75,13 @@ const getDevice = (userAgent?: string | null): string => {
return `${result.os.name} - ${result.browser.name} ${result.browser.version}`;
};
const textMutedForegroundLight = '#929DAE';
const textForeground = '#000';
const _textMutedForegroundLight = '#929DAE';
const _textForeground = '#000';
const textMutedForeground = '#64748B';
const textRejectedRed = '#dc2626';
const textBase = 10;
const textSm = 9;
const textXs = 8;
const _textXs = 8;
const fontMedium = '500';
const columnWidthPercentages = [30, 30, 40];

View file

@ -33,7 +33,7 @@ export const setAvatarImage = async ({
userId,
target,
bytes,
requestMetadata,
requestMetadata: _requestMetadata,
}: SetAvatarImageOptions) => {
let oldAvatarImageId: string | null = null;

View file

@ -13,7 +13,7 @@ export const testCredentialsHandler = async (req: Request) => {
return Response.json({
name: result.team?.name ?? result.user.name,
});
} catch (err) {
} catch {
return Response.json(
{
message: 'Internal Server Error',

View file

@ -99,7 +99,7 @@ export const deleteTeamEmail = async ({ userId, userEmail, teamId }: DeleteTeamE
html,
text,
});
} catch (e) {
} catch {
// Todo: Teams - Alert us.
// We don't want to prevent a user from revoking access because an email could not be sent.
}

View file

@ -13,7 +13,7 @@ export const deletedServiceAccountEmail = () => {
const { hostname } = new URL(process.env.NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000');
return `deleted-account@${hostname}`;
} catch (error) {
} catch {
return LEGACY_DELETED_ACCOUNT_EMAIL;
}
};

View file

@ -13,7 +13,7 @@ export const legacyServiceAccountEmail = () => {
const { hostname } = new URL(process.env.NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000');
return `serviceaccount@${hostname}`;
} catch (error) {
} catch {
return LEGACY_SERVICE_ACCOUNT_EMAIL;
}
};

View file

@ -545,5 +545,5 @@ export const generateSampleWebhookPayload = (
};
}
throw new Error(`Unsupported event type: ${event}`);
throw new Error('Unsupported event type');
};

View file

@ -14,7 +14,7 @@ export const validateApiToken = async ({ authorization }: ValidateApiTokenOption
}
return await getApiTokenByToken({ token });
} catch (err) {
} catch {
throw new Error(`Failed to validate API token`);
}
};

View file

@ -418,5 +418,3 @@ export const ZEnvelopeFieldAndMetaSchema = z.discriminatedUnion('type', [
fieldMeta: ZDropdownFieldMeta.optional().default(FIELD_DROPDOWN_META_DEFAULT_VALUES),
}),
]);
type TEnvelopeFieldAndMeta = z.infer<typeof ZEnvelopeFieldAndMetaSchema>;

View file

@ -11,7 +11,7 @@ export const MIN_FIELD_WIDTH_PX = 36;
export type FieldToRender = Pick<
Field,
'envelopeItemId' | 'recipientId' | 'type' | 'page' | 'customText' | 'inserted' | 'recipientId'
'envelopeItemId' | 'recipientId' | 'type' | 'page' | 'customText' | 'inserted'
> & {
renderId: string; // A unique ID for the field in the render.
width: number;

View file

@ -30,7 +30,8 @@ export const renderCheckboxFieldElement = (
const { fieldWidth, fieldHeight } = calculateFieldPosition(field, pageWidth, pageHeight);
const checkboxMeta: TCheckboxFieldMeta | null = (field.fieldMeta as TCheckboxFieldMeta) || null;
const checkboxMeta: TCheckboxFieldMeta | null =
field.fieldMeta?.type === 'checkbox' ? field.fieldMeta : null;
const checkboxValues = checkboxMeta?.values || [];
const isFirstRender = !pageLayer.findOne(`#${field.renderId}`);
@ -131,6 +132,7 @@ export const renderCheckboxFieldElement = (
});
const checkedValues: number[] = field.customText ? parseCheckboxCustomText(field.customText) : [];
const isReadOnly = checkboxMeta?.readOnly ?? false;
checkboxValues.forEach(({ value, checked }, index) => {
const isCheckboxChecked = match(mode)
@ -138,7 +140,7 @@ export const renderCheckboxFieldElement = (
.with('sign', () => checkedValues.includes(index))
.with('export', () => {
// If it's read-only, check the originally checked state.
if (checkboxMeta.readOnly) {
if (isReadOnly) {
return checked;
}

View file

@ -54,7 +54,8 @@ export const renderDropdownFieldElement = (
const { fieldWidth, fieldHeight } = calculateFieldPosition(field, pageWidth, pageHeight);
const dropdownMeta: TDropdownFieldMeta | null = (field.fieldMeta as TDropdownFieldMeta) || null;
const dropdownMeta: TDropdownFieldMeta | null =
field.fieldMeta?.type === 'dropdown' ? field.fieldMeta : null;
let selectedValue = translations?.[FieldType.DROPDOWN] || 'Select Option';

View file

@ -28,7 +28,7 @@ export type FieldRenderMode = 'edit' | 'sign' | 'export';
export type FieldToRender = Pick<
Field,
'envelopeItemId' | 'recipientId' | 'type' | 'page' | 'customText' | 'inserted' | 'recipientId'
'envelopeItemId' | 'recipientId' | 'type' | 'page' | 'customText' | 'inserted'
> & {
renderId: string; // A unique ID for the field in the render.
width: number;

View file

@ -20,12 +20,29 @@ import { calculateFieldPosition } from './field-renderer';
const DEFAULT_TEXT_X_PADDING = 6;
const getGenericTextFieldMeta = (field: FieldToRender): GenericTextFieldTypeMetas | undefined => {
const fieldMeta = field.fieldMeta;
if (
fieldMeta?.type === 'initials' ||
fieldMeta?.type === 'name' ||
fieldMeta?.type === 'email' ||
fieldMeta?.type === 'date' ||
fieldMeta?.type === 'text' ||
fieldMeta?.type === 'number'
) {
return fieldMeta;
}
return undefined;
};
const upsertFieldText = (field: FieldToRender, options: RenderFieldElementOptions): Konva.Text => {
const { pageWidth, pageHeight, mode = 'edit', pageLayer, translations } = options;
const { fieldWidth, fieldHeight } = calculateFieldPosition(field, pageWidth, pageHeight);
const fieldMeta = field.fieldMeta as GenericTextFieldTypeMetas | undefined;
const fieldMeta = getGenericTextFieldMeta(field);
const fieldTypeName = translations?.[field.type] || field.type;

View file

@ -27,7 +27,8 @@ export const renderRadioFieldElement = (
) => {
const { pageWidth, pageHeight, pageLayer, mode, color } = options;
const radioMeta: TRadioFieldMeta | null = (field.fieldMeta as TRadioFieldMeta) || null;
const radioMeta: TRadioFieldMeta | null =
field.fieldMeta?.type === 'radio' ? field.fieldMeta : null;
const radioValues = radioMeta?.values || [];
const isFirstRender = !pageLayer.findOne(`#${field.renderId}`);
@ -122,6 +123,7 @@ export const renderRadioFieldElement = (
});
const { fieldWidth, fieldHeight } = calculateFieldPosition(field, pageWidth, pageHeight);
const isReadOnly = radioMeta?.readOnly ?? false;
radioValues.forEach(({ value, checked }, index) => {
const isRadioValueChecked = match(mode)
@ -129,7 +131,7 @@ export const renderRadioFieldElement = (
.with('sign', () => index.toString() === field.customText)
.with('export', () => {
// If it's read-only, check the originally checked state.
if (radioMeta.readOnly) {
if (isReadOnly) {
return checked;
}

View file

@ -13,7 +13,7 @@ export type GetFileOptions = {
*
* - Lucas, 2025-11-04
*/
const getFile = async ({ type, data }: GetFileOptions) => {
const _getFile = async ({ type, data }: GetFileOptions) => {
return await match(type)
.with(DocumentDataType.BYTES, () => getFileFromBytes(data))
.with(DocumentDataType.BYTES_64, () => getFileFromBytes64(data))

View file

@ -26,7 +26,7 @@ export const getEnvelopeItemPdfUrl = (options: EnvelopeItemPdfUrlOptions) => {
const version = options.version;
return token
? `${NEXT_PUBLIC_WEBAPP_URL()}/api/files/token/${token}/envelopeItem/${id}/download/${version}${presignToken ? `?presignToken=${presignToken}` : ''}`
? `${NEXT_PUBLIC_WEBAPP_URL()}/api/files/token/${token}/envelopeItem/${id}/download/${version}`
: `${NEXT_PUBLIC_WEBAPP_URL()}/api/files/envelope/${envelopeId}/envelopeItem/${id}/download/${version}`;
}

View file

@ -53,8 +53,8 @@ export const validateFieldsUninserted = (): boolean => {
const innerDiv = element.querySelector('div');
const hasError = innerDiv?.getAttribute('data-error') === 'true';
if (hasError) {
errorElements.push(element as HTMLElement);
if (hasError && element instanceof HTMLElement) {
errorElements.push(element);
} else {
element.removeAttribute('data-error');
}

View file

@ -151,7 +151,7 @@ export const updateOrganisationMemberRoleRoute = adminProcedure
return;
}
const targetRole = role as OrganisationMemberRole;
const targetRole: OrganisationMemberRole = role;
if (currentOrganisationRole === targetRole) {
throw new AppError(AppErrorCode.INVALID_REQUEST, {

View file

@ -30,8 +30,6 @@ export const accessAuthRequest2FAEmailRoute = procedure
assertRateLimit(rateLimitResult);
const user = ctx.user;
// Get document and recipient by token
const envelope = await prisma.envelope.findFirst({
where: {

View file

@ -174,7 +174,7 @@ export const folderRouter = router({
folderId: parentId,
type,
});
} catch (error) {
} catch {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Parent folder not found',
});

View file

@ -1,4 +1,4 @@
import { Prisma, WebhookCallStatus, WebhookTriggerEvents } from '@prisma/client';
import type { Prisma, WebhookCallStatus, WebhookTriggerEvents } from '@prisma/client';
import { TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '@documenso/lib/constants/teams';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';

View file

@ -1,8 +1,7 @@
import { Prisma, WebhookCallStatus, WebhookTriggerEvents } from '@prisma/client';
import { Prisma, WebhookCallStatus } from '@prisma/client';
import { TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '@documenso/lib/constants/teams';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import type { FindResultResponse } from '@documenso/lib/types/search-params';
import { buildTeamWhereQuery } from '@documenso/lib/utils/teams';
import { prisma } from '@documenso/prisma';
@ -62,7 +61,7 @@ export const resendWebhookCallRoute = authenticatedProcedure
try {
responseBody = JSON.parse(body);
} catch (err) {
} catch {
responseBody = body;
}

View file

@ -1,4 +1,3 @@
import { WebhookCallStatus, WebhookTriggerEvents } from '@prisma/client';
import { z } from 'zod';
import WebhookCallSchema from '@documenso/prisma/generated/zod/modelSchema/WebhookCallSchema';

View file

@ -39,17 +39,13 @@ export const DocumentGlobalAuthAccessSelect = ({
})),
];
// Convert string array to Option array for MultiSelect
const selectedOptions =
(value
?.map((val) => authOptions.find((option) => option.value === val))
.filter(Boolean) as Option[]) || [];
const getSelectedOptions = (selectedValues?: string[]) =>
selectedValues
?.map((selectedValue) => authOptions.find((option) => option.value === selectedValue))
.filter((option): option is Option => option !== undefined) ?? [];
// Convert default value to Option array
const defaultOptions =
(defaultValue
?.map((val) => authOptions.find((option) => option.value === val))
.filter(Boolean) as Option[]) || [];
const selectedOptions = getSelectedOptions(value);
const defaultOptions = getSelectedOptions(defaultValue);
const handleChange = (options: Option[]) => {
const values = options.map((option) => option.value);
@ -79,7 +75,7 @@ export const DocumentGlobalAuthAccessTooltip = () => (
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
<TooltipContent className="text-foreground max-w-md space-y-2 p-4">
<h2>
<strong>
<Trans>Document access</Trans>

View file

@ -39,17 +39,13 @@ export const DocumentGlobalAuthActionSelect = ({
})),
];
// Convert string array to Option array for MultiSelect
const selectedOptions =
(value
?.map((val) => authOptions.find((option) => option.value === val))
.filter(Boolean) as Option[]) || [];
const getSelectedOptions = (selectedValues?: string[]) =>
selectedValues
?.map((selectedValue) => authOptions.find((option) => option.value === selectedValue))
.filter((option): option is Option => option !== undefined) ?? [];
// Convert default value to Option array
const defaultOptions =
(defaultValue
?.map((val) => authOptions.find((option) => option.value === val))
.filter(Boolean) as Option[]) || [];
const selectedOptions = getSelectedOptions(value);
const defaultOptions = getSelectedOptions(defaultValue);
const handleChange = (options: Option[]) => {
const values = options.map((option) => option.value);
@ -79,7 +75,7 @@ export const DocumentGlobalAuthActionTooltip = () => (
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
<TooltipContent className="text-foreground max-w-md space-y-2 p-4">
<h2>
<Trans>Global recipient action authentication</Trans>
</h2>

View file

@ -56,7 +56,7 @@ export function EnvelopeRecipientFieldTooltip({
field,
showFieldStatus = false,
showRecipientTooltip = false,
showRecipientColors = false,
showRecipientColors: _showRecipientColors = false,
}: EnvelopeRecipientFieldTooltipProps) {
const { t } = useLingui();
@ -145,7 +145,7 @@ export function EnvelopeRecipientFieldTooltip({
>
<PopoverHover
trigger={
<Avatar className="absolute -left-3 -top-3 z-50 h-6 w-6 border-2 border-solid border-gray-200/50 transition-colors hover:border-gray-200">
<Avatar className="absolute -top-3 -left-3 z-50 h-6 w-6 border-2 border-solid border-gray-200/50 transition-colors hover:border-gray-200">
<AvatarFallback className="bg-neutral-50 text-xs text-gray-400">
{extractInitials(field.recipient.name || field.recipient.email)}
</AvatarFallback>
@ -191,12 +191,12 @@ export function EnvelopeRecipientFieldTooltip({
</span>
</p>
<p className="mt-1 text-center text-xs text-muted-foreground">
<p className="text-muted-foreground mt-1 text-center text-xs">
{getRecipientDisplayText(field.recipient)}
</p>
<button
className="absolute right-0 top-0 my-1 p-2 focus:outline-none focus-visible:ring-0"
className="absolute top-0 right-0 my-1 p-2 focus:outline-none focus-visible:ring-0"
onClick={() => setHideField(true)}
title="Hide field"
>

View file

@ -43,17 +43,13 @@ export const RecipientActionAuthSelect = ({
})),
];
// Convert string array to Option array for MultiSelect
const selectedOptions =
(value
?.map((val) => authOptions.find((option) => option.value === val))
.filter(Boolean) as Option[]) || [];
const getSelectedOptions = (selectedValues?: string[]) =>
selectedValues
?.map((selectedValue) => authOptions.find((option) => option.value === selectedValue))
.filter((option): option is Option => option !== undefined) ?? [];
// Convert default value to Option array
const defaultOptions =
(defaultValue
?.map((val) => authOptions.find((option) => option.value === val))
.filter(Boolean) as Option[]) || [];
const selectedOptions = getSelectedOptions(value);
const defaultOptions = getSelectedOptions(defaultValue);
const handleChange = (options: Option[]) => {
const values = options.map((option) => option.value);
@ -76,14 +72,14 @@ export const RecipientActionAuthSelect = ({
<Tooltip>
<TooltipTrigger
className={cn('absolute right-2 top-1/2 -translate-y-1/2', {
className={cn('absolute top-1/2 right-2 -translate-y-1/2', {
'right-8': selectedOptions.length > 0,
})}
>
<InfoIcon className="h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="max-w-md p-4 text-foreground">
<TooltipContent className="text-foreground max-w-md p-4">
<h2>
<strong>
<Trans>Recipient action authentication</Trans>

View file

@ -37,7 +37,7 @@ function getBaseFontSize(): number {
}
return parsed;
} catch (error) {
} catch {
// Fallback to browser default if anything goes wrong
return 16;
}

View file

@ -101,7 +101,7 @@ export const AddFieldsFormPartial = ({
onAutoSave,
canGoBack = false,
isDocumentPdfLoaded,
teamId,
teamId: _teamId,
}: AddFieldsFormProps) => {
const { toast } = useToast();
const { _ } = useLingui();
@ -535,22 +535,6 @@ export const AddFieldsFormPartial = ({
);
}, [recipients]);
const recipientsByRole = useMemo(() => {
const recipientsByRole: Record<RecipientRole, Recipient[]> = {
CC: [],
VIEWER: [],
SIGNER: [],
APPROVER: [],
ASSISTANT: [],
};
recipients.forEach((recipient) => {
recipientsByRole[recipient.role].push(recipient);
});
return recipientsByRole;
}, [recipients]);
const handleAdvancedSettings = () => {
setShowAdvancedSettings((prev) => !prev);
};
@ -623,10 +607,10 @@ export const AddFieldsFormPartial = ({
{selectedField && (
<div
className={cn(
'dark:text-muted-background pointer-events-none fixed z-50 flex cursor-pointer flex-col items-center justify-center rounded-[2px] bg-white text-muted-foreground ring-2 transition duration-200 [container-type:size]',
'dark:text-muted-background text-muted-foreground [container-type:size] pointer-events-none fixed z-50 flex cursor-pointer flex-col items-center justify-center rounded-[2px] bg-white ring-2 transition duration-200',
selectedSignerStyles?.base,
{
'-rotate-6 scale-90 opacity-50 dark:bg-black/20': !isFieldWithinBounds,
'scale-90 -rotate-6 opacity-50 dark:bg-black/20': !isFieldWithinBounds,
'dark:text-black/60': isFieldWithinBounds,
},
)}
@ -703,7 +687,7 @@ export const AddFieldsFormPartial = ({
selectedRecipient={selectedSigner}
onSelectedRecipientChange={setSelectedSigner}
recipients={recipients}
className="mb-12 mt-2"
className="mt-2 mb-12"
/>
)}
@ -725,7 +709,7 @@ export const AddFieldsFormPartial = ({
<CardContent className="flex flex-col items-center justify-center px-6 py-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 font-signature text-lg font-normal text-muted-foreground group-data-[selected]:text-foreground',
'font-signature text-muted-foreground group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-lg font-normal',
)}
>
<Trans>Signature</Trans>
@ -749,7 +733,7 @@ export const AddFieldsFormPartial = ({
<CardContent className="flex flex-col items-center justify-center px-6 py-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'text-muted-foreground group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-sm font-normal',
)}
>
<Contact className="h-4 w-4" />
@ -774,7 +758,7 @@ export const AddFieldsFormPartial = ({
<CardContent className="flex flex-col items-center justify-center px-6 py-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'text-muted-foreground group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-sm font-normal',
)}
>
<Mail className="h-4 w-4" />
@ -799,7 +783,7 @@ export const AddFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'text-muted-foreground group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-sm font-normal',
)}
>
<User className="h-4 w-4" />
@ -824,7 +808,7 @@ export const AddFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'text-muted-foreground group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-sm font-normal',
)}
>
<CalendarDays className="h-4 w-4" />
@ -849,7 +833,7 @@ export const AddFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'text-muted-foreground group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-sm font-normal',
)}
>
<Type className="h-4 w-4" />
@ -874,7 +858,7 @@ export const AddFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'text-muted-foreground group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-sm font-normal',
)}
>
<Hash className="h-4 w-4" />
@ -899,7 +883,7 @@ export const AddFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'text-muted-foreground group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-sm font-normal',
)}
>
<Disc className="h-4 w-4" />
@ -924,7 +908,7 @@ export const AddFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'text-muted-foreground group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-sm font-normal',
)}
>
<CheckSquare className="h-4 w-4" />
@ -949,7 +933,7 @@ export const AddFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'text-muted-foreground group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-sm font-normal',
)}
>
<ChevronDown className="h-4 w-4" />

View file

@ -242,7 +242,7 @@ export const AddSettingsFormPartial = ({
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
<TooltipContent className="text-foreground max-w-md space-y-2 p-4">
<Trans>
Controls the language for the document, including the language to be used
for email notifications, and the final certificate that is generated and
@ -361,11 +361,11 @@ export const AddSettingsFormPartial = ({
<Accordion type="multiple" className="mt-6">
<AccordionItem value="advanced-options" className="border-none">
<AccordionTrigger className="mb-2 rounded border px-3 py-2 text-left text-foreground hover:bg-neutral-200/30 hover:no-underline">
<AccordionTrigger className="text-foreground mb-2 rounded border px-3 py-2 text-left hover:bg-neutral-200/30 hover:no-underline">
<Trans>Advanced Options</Trans>
</AccordionTrigger>
<AccordionContent className="-mx-1 px-1 pt-2 text-sm leading-relaxed text-muted-foreground">
<AccordionContent className="text-muted-foreground -mx-1 px-1 pt-2 text-sm leading-relaxed">
<div className="flex flex-col space-y-6">
<FormField
control={form.control}
@ -379,7 +379,7 @@ export const AddSettingsFormPartial = ({
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="max-w-xs text-muted-foreground">
<TooltipContent className="text-muted-foreground max-w-xs">
<Trans>
Add an external ID to the document. This can be used to identify
the document in external systems.
@ -418,7 +418,7 @@ export const AddSettingsFormPartial = ({
field.onChange(value);
void handleAutoSave();
}}
className="w-full bg-background"
className="bg-background w-full"
emptySelectionPlaceholder={t`Select signature types`}
/>
</FormControl>
@ -481,7 +481,10 @@ export const AddSettingsFormPartial = ({
options={TIME_ZONES}
{...field}
onChange={(value) => {
value && field.onChange(value);
if (value) {
field.onChange(value);
}
void handleAutoSave();
}}
value={field.value}
@ -506,7 +509,7 @@ export const AddSettingsFormPartial = ({
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="max-w-xs text-muted-foreground">
<TooltipContent className="text-muted-foreground max-w-xs">
<Trans>
Add a URL to redirect the user to once the document is signed
</Trans>

View file

@ -69,8 +69,8 @@ export type AddSubjectFormProps = {
export const AddSubjectFormPartial = ({
documentFlow,
recipients: recipients,
fields: fields,
recipients,
fields,
document,
onSubmit,
onAutoSave,
@ -323,7 +323,7 @@ export const AddSubjectFormPartial = ({
<TooltipTrigger>
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="p-4 text-muted-foreground">
<TooltipContent className="text-muted-foreground p-4">
<DocumentSendEmailMessageHelper />
</TooltipContent>
</Tooltip>
@ -331,7 +331,7 @@ export const AddSubjectFormPartial = ({
<FormControl>
<Textarea
className="mt-2 h-16 resize-none bg-background"
className="bg-background mt-2 h-16 resize-none"
{...field}
maxLength={5000}
/>
@ -360,7 +360,7 @@ export const AddSubjectFormPartial = ({
className="rounded-lg border"
>
{document.status === DocumentStatus.DRAFT ? (
<div className="py-16 text-center text-sm text-muted-foreground">
<div className="text-muted-foreground py-16 text-center text-sm">
<p>
<Trans>We won't send anything to notify recipients.</Trans>
</p>
@ -373,7 +373,7 @@ export const AddSubjectFormPartial = ({
</p>
</div>
) : (
<ul className="divide-y text-muted-foreground">
<ul className="text-muted-foreground divide-y">
{recipients.length === 0 && (
<li className="flex flex-col items-center justify-center py-6 text-sm">
<Trans>No recipients</Trans>
@ -388,10 +388,10 @@ export const AddSubjectFormPartial = ({
<AvatarWithText
avatarFallback={recipient.email.slice(0, 1).toUpperCase()}
primaryText={
<p className="text-sm text-muted-foreground">{recipient.email}</p>
<p className="text-muted-foreground text-sm">{recipient.email}</p>
}
secondaryText={
<p className="text-xs text-muted-foreground/70">
<p className="text-muted-foreground/70 text-xs">
{_(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}
</p>
}

View file

@ -31,7 +31,7 @@ export type DocumentUploadButtonProps = {
};
export const DocumentUploadButton = ({
className,
className: _className,
loading,
onDrop,
onDropRejected,

View file

@ -23,7 +23,7 @@ export interface FieldSelectorProps {
}
export const FieldSelector = ({
className,
className: _className,
selectedField,
onSelectedFieldChange,
disabled = false,
@ -104,10 +104,10 @@ export const FieldSelector = ({
)}
>
<CardContent className="relative flex items-center justify-center gap-x-2 px-6 py-4">
{Icon && <Icon className="h-4 w-4 text-muted-foreground" />}
{Icon && <Icon className="text-muted-foreground h-4 w-4" />}
<span
className={cn(
'text-sm text-muted-foreground group-data-[selected]:text-foreground',
'text-muted-foreground group-data-[selected]:text-foreground text-sm',
field.type === FieldType.SIGNATURE && 'invisible',
)}
>
@ -115,7 +115,7 @@ export const FieldSelector = ({
</span>
{field.type === FieldType.SIGNATURE && (
<div className="absolute inset-0 flex items-center justify-center font-signature text-lg text-muted-foreground">
<div className="font-signature text-muted-foreground absolute inset-0 flex items-center justify-center text-lg">
<Trans>Signature</Trans>
</div>
)}

View file

@ -5,7 +5,7 @@ import { cn } from '../../lib/utils';
export type FormErrorMessageProps = {
className?: string;
error: { message?: string } | undefined | unknown;
error: unknown;
};
const isErrorWithMessage = (error: unknown): error is { message?: string } => {

View file

@ -99,17 +99,24 @@ function transToGroupOption(options: Option[], groupBy?: string) {
const groupOption: GroupOption = {};
options.forEach((option) => {
const key = (option[groupBy] as string) || '';
const groupValue = option[groupBy];
const key = typeof groupValue === 'string' ? groupValue : groupValue ? String(groupValue) : '';
if (!groupOption[key]) {
groupOption[key] = [];
}
groupOption[key].push(option);
});
return groupOption;
}
function removePickedOption(groupOption: GroupOption, picked: Option[]) {
const cloneOption = JSON.parse(JSON.stringify(groupOption)) as GroupOption;
const cloneOption: GroupOption = {};
for (const [key, value] of Object.entries(groupOption)) {
cloneOption[key] = [...value];
}
for (const [key, value] of Object.entries(cloneOption)) {
cloneOption[key] = value.filter((val) => !picked.find((p) => p.value === val.value));
@ -186,11 +193,17 @@ const MultiSelect = ({
const debouncedSearchTerm = useDebounce(inputValue, delay || 500);
const handleClickOutside = (event: MouseEvent | TouchEvent) => {
const target = event.target;
if (!(target instanceof Node)) {
return;
}
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node) &&
!dropdownRef.current.contains(target) &&
inputRef.current &&
!inputRef.current.contains(event.target as Node)
!inputRef.current.contains(target)
) {
setOpen(false);
inputRef.current.blur();
@ -408,7 +421,7 @@ const MultiSelect = ({
>
<div
className={cn(
'has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50 relative min-h-[38px] rounded-md border border-input text-sm outline-none transition-[color,box-shadow] focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50',
'has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive border-input focus-within:border-ring focus-within:ring-ring/50 relative min-h-[38px] rounded-md border text-sm transition-[color,box-shadow] outline-none focus-within:ring-[3px] has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50',
{
'p-1': selected.length !== 0,
'cursor-text': !disabled && selected.length !== 0,
@ -427,7 +440,7 @@ const MultiSelect = ({
<div
key={option.value}
className={cn(
'animate-fadeIn data-fixed:pe-2 relative inline-flex h-7 cursor-default items-center rounded-md border bg-background pe-7 pl-2 ps-2 text-xs font-medium text-secondary-foreground transition-all hover:bg-background disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
'animate-fadeIn bg-background text-secondary-foreground hover:bg-background relative inline-flex h-7 cursor-default items-center rounded-md border ps-2 pe-7 pl-2 text-xs font-medium transition-all disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 data-fixed:pe-2',
badgeClassName,
)}
data-fixed={option.fixed}
@ -435,7 +448,7 @@ const MultiSelect = ({
>
{option.label}
<button
className="absolute -inset-y-px -end-px flex size-7 items-center justify-center rounded-e-md border border-transparent p-0 text-muted-foreground/80 outline-none transition-[color,box-shadow] hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50"
className="text-muted-foreground/80 hover:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 absolute -inset-y-px -end-px flex size-7 items-center justify-center rounded-e-md border border-transparent p-0 transition-[color,box-shadow] outline-none focus-visible:ring-[3px]"
onKeyDown={(e) => {
if (e.key === 'Enter') {
handleUnselect(option);
@ -478,7 +491,7 @@ const MultiSelect = ({
}}
placeholder={hidePlaceholderWhenSelected && selected.length !== 0 ? '' : placeholder}
className={cn(
'flex-1 bg-transparent outline-none placeholder:text-muted-foreground/70 disabled:cursor-not-allowed',
'placeholder:text-muted-foreground/70 flex-1 bg-transparent outline-none disabled:cursor-not-allowed',
{
'w-full': hidePlaceholderWhenSelected,
'px-3 py-2': selected.length === 0,
@ -494,7 +507,7 @@ const MultiSelect = ({
onChange?.(selected.filter((s) => s.fixed));
}}
className={cn(
'absolute end-0 top-0 flex size-9 items-center justify-center rounded-md border border-transparent text-muted-foreground/80 outline-none transition-[color,box-shadow] hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50',
'text-muted-foreground/80 hover:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 absolute end-0 top-0 flex size-9 items-center justify-center rounded-md border border-transparent transition-[color,box-shadow] outline-none focus-visible:ring-[3px]',
(hideClearAllButton ||
disabled ||
selected.length < 1 ||
@ -510,7 +523,7 @@ const MultiSelect = ({
<div className="relative">
<div
className={cn(
'absolute top-2 z-10 w-full overflow-hidden rounded-md border border-input',
'border-input absolute top-2 z-10 w-full overflow-hidden rounded-md border',
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
!open && 'hidden',
)}

View file

@ -51,24 +51,18 @@ export const RecipientSelector = ({
}, [recipients]);
const recipientsByRoleToDisplay = useMemo(() => {
return Object.entries(recipientsByRole)
.filter(
([role]) =>
role !== RecipientRole.CC &&
role !== RecipientRole.VIEWER &&
role !== RecipientRole.ASSISTANT,
)
.map(
([role, roleRecipients]) =>
[
role,
sortBy(
roleRecipients,
[(r) => r.signingOrder || Number.MAX_SAFE_INTEGER, 'asc'],
[(r) => r.id, 'asc'],
),
] as [RecipientRole, Recipient[]],
);
return [RecipientRole.SIGNER, RecipientRole.APPROVER].map((role) => {
const roleRecipients = recipientsByRole[role];
return [
role,
sortBy(
roleRecipients,
[(r) => r.signingOrder || Number.MAX_SAFE_INTEGER, 'asc'],
[(r) => r.id, 'asc'],
),
] satisfies [RecipientRole, Recipient[]];
});
}, [recipientsByRole]);
const getRecipientLabel = useCallback(
@ -101,7 +95,7 @@ export const RecipientSelector = ({
variant="outline"
role="combobox"
className={cn(
'justify-between bg-background font-normal text-muted-foreground hover:text-foreground',
'bg-background text-muted-foreground hover:text-foreground justify-between font-normal',
getRecipientColorStyles(
Math.max(
recipients.findIndex((r) => r.id === selectedRecipient?.id),
@ -126,21 +120,21 @@ export const RecipientSelector = ({
<CommandInput />
<CommandEmpty>
<span className="inline-block px-4 text-muted-foreground">
<span className="text-muted-foreground inline-block px-4">
<Trans>No recipient matching this description was found.</Trans>
</span>
</CommandEmpty>
{recipientsByRoleToDisplay.map(([role, roleRecipients], roleIndex) => (
<CommandGroup key={roleIndex}>
<div className="mb-1 ml-2 mt-2 text-xs font-medium text-muted-foreground">
<div className="text-muted-foreground mt-2 mb-1 ml-2 text-xs font-medium">
{_(RECIPIENT_ROLES_DESCRIPTION[role].roleNamePlural)}
</div>
{roleRecipients.length === 0 && (
<div
key={`${role}-empty`}
className="px-4 pb-4 pt-2.5 text-center text-xs text-muted-foreground/80"
className="text-muted-foreground/80 px-4 pt-2.5 pb-4 text-center text-xs"
>
<Trans>No recipients with this role</Trans>
</div>
@ -168,7 +162,7 @@ export const RecipientSelector = ({
disabled={recipient.signingStatus !== SigningStatus.NOT_SIGNED}
>
<span
className={cn('truncate text-foreground/70', {
className={cn('text-foreground/70 truncate', {
'text-foreground/80': recipient.id === selectedRecipient?.id,
})}
>
@ -190,7 +184,7 @@ export const RecipientSelector = ({
<Info className="ml-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="max-w-xs text-muted-foreground">
<TooltipContent className="text-muted-foreground max-w-xs">
<Trans>
This document has already been sent to this recipient. You can no longer
edit this recipient.

View file

@ -172,7 +172,9 @@ export class Canvas {
event.preventDefault();
}
event.buttons === 1 && this.onMouseDown(event);
if (event.buttons === 1) {
this.onMouseDown(event);
}
}
private onMouseLeave(event: MouseEvent): void {

View file

@ -5,7 +5,6 @@ import type { MessageDescriptor } from '@lingui/core';
import { Trans, useLingui } from '@lingui/react/macro';
import { motion } from 'framer-motion';
import { parseMessageDescriptor } from '@documenso/lib/utils/i18n';
import { Dialog, DialogClose, DialogContent, DialogFooter } from '@documenso/ui/primitives/dialog';
import { cn } from '../../lib/utils';
@ -45,7 +44,7 @@ export const SignaturePadDialog = ({
return (
<div
className={cn(
'relative block aspect-signature-pad w-full select-none rounded-lg border bg-background',
'aspect-signature-pad bg-background relative block w-full rounded-lg border select-none',
className,
{
'pointer-events-none opacity-50': disabled,
@ -140,7 +139,11 @@ export const SignaturePadDialog = ({
}}
>
{dialogConfirmText ? (
parseMessageDescriptor(i18n._, dialogConfirmText)
typeof dialogConfirmText === 'string' ? (
dialogConfirmText
) : (
i18n._(dialogConfirmText)
)
) : (
<Trans>Next</Trans>
)}

View file

@ -64,7 +64,7 @@ export const Stepper: FC<StepperProps> = ({
const nextStep = () => {
if (currentStep < totalSteps) {
void handleStepChange(currentStep + 1);
handleStepChange(currentStep + 1);
} else {
void handleComplete();
}
@ -72,7 +72,7 @@ export const Stepper: FC<StepperProps> = ({
const previousStep = () => {
if (currentStep > 1) {
void handleStepChange(currentStep - 1);
handleStepChange(currentStep - 1);
}
};

View file

@ -88,7 +88,7 @@ export const AddTemplateFieldsFormPartial = ({
fields,
onSubmit,
onAutoSave,
teamId,
teamId: _teamId,
}: AddTemplateFieldsFormProps) => {
const { _ } = useLingui();
const { toast } = useToast();
@ -581,10 +581,10 @@ export const AddTemplateFieldsFormPartial = ({
{selectedField && (
<div
className={cn(
'dark:text-muted-background pointer-events-none fixed z-50 flex cursor-pointer flex-col items-center justify-center rounded-[2px] bg-white text-muted-foreground ring-2 transition duration-200 [container-type:size]',
'dark:text-muted-background text-muted-foreground [container-type:size] pointer-events-none fixed z-50 flex cursor-pointer flex-col items-center justify-center rounded-[2px] bg-white ring-2 transition duration-200',
selectedSignerStyles?.base,
{
'-rotate-6 scale-90 opacity-50 dark:bg-black/20': !isFieldWithinBounds,
'scale-90 -rotate-6 opacity-50 dark:bg-black/20': !isFieldWithinBounds,
'dark:text-black/60': isFieldWithinBounds,
},
)}
@ -650,7 +650,7 @@ export const AddTemplateFieldsFormPartial = ({
variant="outline"
role="combobox"
className={cn(
'mb-12 mt-2 justify-between bg-background font-normal text-muted-foreground hover:text-foreground',
'bg-background text-muted-foreground hover:text-foreground mt-2 mb-12 justify-between font-normal',
selectedSignerStyles?.comboxBoxTrigger,
)}
>
@ -681,7 +681,7 @@ export const AddTemplateFieldsFormPartial = ({
<CommandInput />
<CommandEmpty>
<span className="inline-block px-4 text-muted-foreground">
<span className="text-muted-foreground inline-block px-4">
<Trans>No recipient matching this description was found.</Trans>
</span>
</CommandEmpty>
@ -689,14 +689,14 @@ export const AddTemplateFieldsFormPartial = ({
{/* Note: This is duplicated in `add-fields.tsx` */}
{recipientsByRoleToDisplay.map(([role, roleRecipients], roleIndex) => (
<CommandGroup key={roleIndex}>
<div className="mb-1 ml-2 mt-2 text-xs font-medium text-muted-foreground">
<div className="text-muted-foreground mt-2 mb-1 ml-2 text-xs font-medium">
{_(RECIPIENT_ROLES_DESCRIPTION[role].roleNamePlural)}
</div>
{roleRecipients.length === 0 && (
<div
key={`${role}-empty`}
className="px-4 pb-4 pt-2.5 text-center text-xs text-muted-foreground/80"
className="text-muted-foreground/80 px-4 pt-2.5 pb-4 text-center text-xs"
>
<Trans>No recipients with this role</Trans>
</div>
@ -720,7 +720,7 @@ export const AddTemplateFieldsFormPartial = ({
}}
>
<span
className={cn('truncate text-foreground/70', {
className={cn('text-foreground/70 truncate', {
'text-foreground/80': recipient === selectedSigner,
})}
>
@ -768,7 +768,7 @@ export const AddTemplateFieldsFormPartial = ({
<CardContent className="flex flex-col items-center justify-center px-6 py-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 font-signature text-lg font-normal text-muted-foreground group-data-[selected]:text-foreground',
'font-signature text-muted-foreground group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-lg font-normal',
)}
>
<Trans>Signature</Trans>
@ -793,7 +793,7 @@ export const AddTemplateFieldsFormPartial = ({
<CardContent className="flex flex-col items-center justify-center px-6 py-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'text-muted-foreground group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-sm font-normal',
)}
>
<Contact className="h-4 w-4" />
@ -819,7 +819,7 @@ export const AddTemplateFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'text-muted-foreground group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-sm font-normal',
)}
>
<Mail className="h-4 w-4" />
@ -845,7 +845,7 @@ export const AddTemplateFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'text-muted-foreground group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-sm font-normal',
)}
>
<User className="h-4 w-4" />
@ -871,7 +871,7 @@ export const AddTemplateFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'text-muted-foreground group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-sm font-normal',
)}
>
<CalendarDays className="h-4 w-4" />
@ -897,7 +897,7 @@ export const AddTemplateFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'text-muted-foreground group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-sm font-normal',
)}
>
<Type className="h-4 w-4" />
@ -923,7 +923,7 @@ export const AddTemplateFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'text-muted-foreground group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-sm font-normal',
)}
>
<Hash className="h-4 w-4" />
@ -949,7 +949,7 @@ export const AddTemplateFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'text-muted-foreground group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-sm font-normal',
)}
>
<Disc className="h-4 w-4" />
@ -975,7 +975,7 @@ export const AddTemplateFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'text-muted-foreground group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-sm font-normal',
)}
>
<CheckSquare className="h-4 w-4" />
@ -1001,7 +1001,7 @@ export const AddTemplateFieldsFormPartial = ({
<CardContent className="p-4">
<p
className={cn(
'flex items-center justify-center gap-x-1.5 text-sm font-normal text-muted-foreground group-data-[selected]:text-foreground',
'text-muted-foreground group-data-[selected]:text-foreground flex items-center justify-center gap-x-1.5 text-sm font-normal',
)}
>
<ChevronDown className="h-4 w-4" />

View file

@ -239,7 +239,7 @@ export const AddTemplateSettingsFormPartial = ({
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
<TooltipContent className="text-foreground max-w-md space-y-2 p-4">
Controls the language for the document, including the language to be used
for email notifications, and the final certificate that is generated and
attached to the document.
@ -337,7 +337,7 @@ export const AddTemplateSettingsFormPartial = ({
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="max-w-md space-y-2 p-4 text-foreground">
<TooltipContent className="text-foreground max-w-md space-y-2 p-4">
<h2>
<strong>
<Trans>Document Distribution Method</Trans>
@ -423,7 +423,7 @@ export const AddTemplateSettingsFormPartial = ({
field.onChange(value);
void handleAutoSave();
}}
className="w-full bg-background"
className="bg-background w-full"
emptySelectionPlaceholder={t`Select signature types`}
/>
</FormControl>
@ -463,11 +463,11 @@ export const AddTemplateSettingsFormPartial = ({
{distributionMethod === DocumentDistributionMethod.EMAIL && (
<Accordion type="multiple">
<AccordionItem value="email-options" className="border-none">
<AccordionTrigger className="rounded border px-3 py-2 text-left text-foreground hover:bg-neutral-200/30 hover:no-underline">
<AccordionTrigger className="text-foreground rounded border px-3 py-2 text-left hover:bg-neutral-200/30 hover:no-underline">
<Trans>Email Options</Trans>
</AccordionTrigger>
<AccordionContent className="-mx-1 px-1 pt-4 text-sm leading-relaxed text-muted-foreground [&>div]:pb-0">
<AccordionContent className="text-muted-foreground -mx-1 px-1 pt-4 text-sm leading-relaxed [&>div]:pb-0">
<div className="flex flex-col space-y-6">
{organisation.organisationClaim.flags.emailDomains && (
<FormField
@ -566,7 +566,7 @@ export const AddTemplateSettingsFormPartial = ({
<TooltipTrigger>
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="p-4 text-muted-foreground">
<TooltipContent className="text-muted-foreground p-4">
<DocumentSendEmailMessageHelper />
</TooltipContent>
</Tooltip>
@ -574,7 +574,7 @@ export const AddTemplateSettingsFormPartial = ({
<FormControl>
<Textarea
className="h-16 resize-none bg-background"
className="bg-background h-16 resize-none"
{...field}
maxLength={5000}
onBlur={handleAutoSave}
@ -603,11 +603,11 @@ export const AddTemplateSettingsFormPartial = ({
<Accordion type="multiple">
<AccordionItem value="advanced-options" className="border-none">
<AccordionTrigger className="rounded border px-3 py-2 text-left text-foreground hover:bg-neutral-200/30 hover:no-underline">
<AccordionTrigger className="text-foreground rounded border px-3 py-2 text-left hover:bg-neutral-200/30 hover:no-underline">
<Trans>Advanced Options</Trans>
</AccordionTrigger>
<AccordionContent className="-mx-1 px-1 pt-4 text-sm leading-relaxed text-muted-foreground">
<AccordionContent className="text-muted-foreground -mx-1 px-1 pt-4 text-sm leading-relaxed">
<div className="flex flex-col space-y-6">
<FormField
control={form.control}
@ -621,7 +621,7 @@ export const AddTemplateSettingsFormPartial = ({
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="max-w-xs text-muted-foreground">
<TooltipContent className="text-muted-foreground max-w-xs">
<Trans>
Add an external ID to the template. This can be used to identify
in external systems.
@ -695,7 +695,10 @@ export const AddTemplateSettingsFormPartial = ({
options={TIME_ZONES}
{...field}
onChange={(value) => {
value && field.onChange(value);
if (value) {
field.onChange(value);
}
void handleAutoSave();
}}
/>
@ -718,7 +721,7 @@ export const AddTemplateSettingsFormPartial = ({
<InfoIcon className="mx-2 h-4 w-4" />
</TooltipTrigger>
<TooltipContent className="max-w-xs text-muted-foreground">
<TooltipContent className="text-muted-foreground max-w-xs">
<Trans>
Add a URL to redirect the user to once the document is signed
</Trans>