Compare commits

...

10 Commits

Author SHA1 Message Date
Ephraim Atta-Duncan
c68eb4f198 fix: ensure duplicated fields do not retain the original fields id 2025-03-04 11:57:09 +00:00
Ephraim Atta-Duncan
33c2cbe01d fix: correctly set field as last active field 2025-03-04 04:30:13 +00:00
David Nguyen
db326cb4a9 fix: posthog reverse proxy 2025-03-04 10:48:19 +11:00
David Nguyen
d664f571d6 fix: posthog reverse proxy 2025-03-04 10:46:59 +11:00
David Nguyen
7c38970ee8 fix: update error logging 2025-03-04 01:41:39 +11:00
David Nguyen
e08d62c844 fix: remove invalid prisma zod schemas 2025-03-04 01:20:13 +11:00
David Nguyen
25bb6ffe77 fix: imports 2025-03-03 14:49:28 +11:00
Catalin Pit
e79d762710 chore: add label for checkbox and radio fields (#1607) 2025-03-03 13:46:29 +11:00
Mythie
d970976299 fix: remove auto-expand in embeddding 2025-02-28 14:46:15 +11:00
David Nguyen
3dce814ab2 fix: stripe price fetch (#1677)
Currently Stripe prices search is omitting a price for an unknown
reason.

Changed our fetch logic to use `list` instead of `search` allows us to
work around the issue.

It's unknown on the performance impact of using `list` vs `search`
2025-02-28 14:44:06 +11:00
57 changed files with 228 additions and 93 deletions

View File

@@ -1,7 +1,7 @@
import { DocumentStatus } from '@prisma/client';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { kyselyPrisma, sql } from '@documenso/prisma'; import { kyselyPrisma, sql } from '@documenso/prisma';
import { DocumentStatus } from '@documenso/prisma/client';
export const getCompletedDocumentsMonthly = async (type: 'count' | 'cumulative' = 'count') => { export const getCompletedDocumentsMonthly = async (type: 'count' | 'cumulative' = 'count') => {
const qb = kyselyPrisma.$kysely const qb = kyselyPrisma.$kysely

View File

@@ -347,7 +347,7 @@ export const EmbedDirectTemplateClientPage = ({
{/* Widget */} {/* Widget */}
<div <div
key={isExpanded ? 'expanded' : 'collapsed'} key={isExpanded ? 'expanded' : 'collapsed'}
className="group/document-widget fixed bottom-8 left-0 z-50 h-fit w-full flex-shrink-0 px-6 md:sticky md:top-4 md:z-auto md:w-[350px] md:px-0" className="group/document-widget fixed bottom-8 left-0 z-50 h-fit max-h-[calc(100dvh-2rem)] w-full flex-shrink-0 px-6 md:sticky md:top-4 md:z-auto md:w-[350px] md:px-0"
data-expanded={isExpanded || undefined} data-expanded={isExpanded || undefined}
> >
<div className="border-border bg-widget flex h-fit w-full flex-col rounded-xl border px-4 py-4 md:min-h-[min(calc(100dvh-2rem),48rem)] md:py-6"> <div className="border-border bg-widget flex h-fit w-full flex-col rounded-xl border px-4 py-4 md:min-h-[min(calc(100dvh-2rem),48rem)] md:py-6">

View File

@@ -287,7 +287,7 @@ export const EmbedSignDocumentClientPage = ({
{/* Widget */} {/* Widget */}
<div <div
key={isExpanded ? 'expanded' : 'collapsed'} key={isExpanded ? 'expanded' : 'collapsed'}
className="embed--DocumentWidgetContainer group/document-widget fixed bottom-8 left-0 z-50 h-fit w-full flex-shrink-0 px-6 md:sticky md:top-4 md:z-auto md:w-[350px] md:px-0" className="embed--DocumentWidgetContainer group/document-widget fixed bottom-8 left-0 z-50 h-fit max-h-[calc(100dvh-2rem)] w-full flex-shrink-0 px-6 md:sticky md:top-4 md:z-auto md:w-[350px] md:px-0"
data-expanded={isExpanded || undefined} data-expanded={isExpanded || undefined}
> >
<div className="embed--DocumentWidget border-border bg-widget flex w-full flex-col rounded-xl border px-4 py-4 md:py-6"> <div className="embed--DocumentWidget border-border bg-widget flex w-full flex-col rounded-xl border px-4 py-4 md:py-6">

View File

@@ -181,6 +181,23 @@ export const DocumentSigningFieldContainer = ({
</button> </button>
)} )}
{(field.type === FieldType.RADIO || field.type === FieldType.CHECKBOX) &&
field.fieldMeta?.label && (
<div
className={cn(
'absolute -top-16 left-0 right-0 rounded-md p-2 text-center text-xs text-gray-700',
{
'bg-foreground/5 border-border border': !field.inserted,
},
{
'bg-documenso-200 border-primary border': field.inserted,
},
)}
>
{field.fieldMeta.label}
</div>
)}
{children} {children}
</FieldRootContainer> </FieldRootContainer>
</div> </div>

View File

@@ -62,7 +62,7 @@ export const GenericErrorLayout = ({
const team = useOptionalCurrentTeam(); const team = useOptionalCurrentTeam();
const { subHeading, heading, message } = const { subHeading, heading, message } =
errorCodeMap[errorCode || 404] ?? defaultErrorCodeMap[500]; errorCodeMap[errorCode || 500] ?? defaultErrorCodeMap[500];
return ( return (
<div className="fixed inset-0 z-0 flex h-screen w-screen items-center justify-center"> <div className="fixed inset-0 z-0 flex h-screen w-screen items-center justify-center">

View File

@@ -1,7 +1,6 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import Plausible from 'plausible-tracker'; import Plausible from 'plausible-tracker';
import posthog from 'posthog-js';
import { import {
Links, Links,
Meta, Meta,
@@ -181,7 +180,6 @@ export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
if (errorCode !== 404) { if (errorCode !== 404) {
console.error('[RootErrorBoundary]', error); console.error('[RootErrorBoundary]', error);
posthog.captureException(error);
} }
return <GenericErrorLayout errorCode={errorCode} />; return <GenericErrorLayout errorCode={errorCode} />;

View File

@@ -19,18 +19,29 @@ const posthogProxy = async (request: Request) => {
const headers = new Headers(request.headers); const headers = new Headers(request.headers);
headers.set('host', hostname); headers.set('host', hostname);
const response = await fetch(newUrl, { const fetchOptions: RequestInit = {
method: request.method, method: request.method,
headers, headers,
body: request.body, redirect: 'follow',
// @ts-expect-error - Not really sure about this };
duplex: 'half',
}); if (!['GET', 'HEAD'].includes(request.method)) {
fetchOptions.body = request.body;
fetchOptions.duplex = 'half';
}
const response = await fetch(newUrl, fetchOptions);
const responseHeaders = new Headers(response.headers);
responseHeaders.delete('content-encoding');
responseHeaders.delete('content-length');
responseHeaders.delete('transfer-encoding');
responseHeaders.delete('cookie');
return new Response(response.body, { return new Response(response.body, {
status: response.status, status: response.status,
statusText: response.statusText, statusText: response.statusText,
headers: response.headers, headers: responseHeaders,
}); });
}; };

View File

@@ -145,7 +145,9 @@ export default function EmbedDirectTemplatePage() {
recipient={recipient} recipient={recipient}
fields={fields} fields={fields}
metadata={template.templateMeta} metadata={template.templateMeta}
hidePoweredBy={isPlatformDocument || isEnterpriseDocument || hidePoweredBy} hidePoweredBy={
isCommunityPlan || isPlatformDocument || isEnterpriseDocument || hidePoweredBy
}
allowWhiteLabelling={isCommunityPlan || isPlatformDocument || isEnterpriseDocument} allowWhiteLabelling={isCommunityPlan || isPlatformDocument || isEnterpriseDocument}
/> />
</DocumentSigningRecipientProvider> </DocumentSigningRecipientProvider>

View File

@@ -169,7 +169,9 @@ export default function EmbedSignDocumentPage() {
fields={fields} fields={fields}
metadata={document.documentMeta} metadata={document.documentMeta}
isCompleted={document.status === DocumentStatus.COMPLETED} isCompleted={document.status === DocumentStatus.COMPLETED}
hidePoweredBy={isPlatformDocument || isEnterpriseDocument || hidePoweredBy} hidePoweredBy={
isCommunityPlan || isPlatformDocument || isEnterpriseDocument || hidePoweredBy
}
allowWhitelabelling={isCommunityPlan || isPlatformDocument || isEnterpriseDocument} allowWhitelabelling={isCommunityPlan || isPlatformDocument || isEnterpriseDocument}
allRecipients={allRecipients} allRecipients={allRecipients}
/> />

View File

@@ -1,4 +1,4 @@
import { fetchRequestHandler } from '@ts-rest/serverless/fetch'; import { TsRestHttpError, fetchRequestHandler } from '@ts-rest/serverless/fetch';
import { Hono } from 'hono'; import { Hono } from 'hono';
import { ApiContractV1 } from '@documenso/api/v1/contract'; import { ApiContractV1 } from '@documenso/api/v1/contract';
@@ -29,6 +29,12 @@ tsRestHonoApp.mount('/', async (request) => {
request, request,
contract: ApiContractV1, contract: ApiContractV1,
router: ApiContractV1Implementation, router: ApiContractV1Implementation,
options: {}, options: {
errorHandler: (err) => {
if (err instanceof TsRestHttpError && err.statusCode === 500) {
console.error(err);
}
},
},
}); });
}); });

View File

@@ -1,3 +1,5 @@
import type { Prisma } from '@prisma/client';
import { DocumentDataType, DocumentStatus, SigningStatus, TeamMemberRole } from '@prisma/client';
import { tsr } from '@ts-rest/serverless/fetch'; import { tsr } from '@ts-rest/serverless/fetch';
import { match } from 'ts-pattern'; import { match } from 'ts-pattern';
@@ -50,13 +52,6 @@ import {
} from '@documenso/lib/universal/upload/server-actions'; } from '@documenso/lib/universal/upload/server-actions';
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs'; import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import type { Prisma } from '@documenso/prisma/client';
import {
DocumentDataType,
DocumentStatus,
SigningStatus,
TeamMemberRole,
} from '@documenso/prisma/client';
import { ApiContractV1 } from './contract'; import { ApiContractV1 } from './contract';
import { authenticatedMiddleware } from './middleware/authenticated'; import { authenticatedMiddleware } from './middleware/authenticated';

View File

@@ -1,10 +1,10 @@
import type { Team, User } from '@prisma/client';
import type { TsRestRequest } from '@ts-rest/serverless'; import type { TsRestRequest } from '@ts-rest/serverless';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { getApiTokenByToken } from '@documenso/lib/server-only/public-api/get-api-token-by-token'; import { getApiTokenByToken } from '@documenso/lib/server-only/public-api/get-api-token-by-token';
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { extractRequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; import { extractRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import type { Team, User } from '@documenso/prisma/client';
type B = { type B = {
// appRoute: any; // appRoute: any;

View File

@@ -1,4 +1,16 @@
import { extendZodWithOpenApi } from '@anatine/zod-openapi'; import { extendZodWithOpenApi } from '@anatine/zod-openapi';
import {
DocumentDataType,
DocumentDistributionMethod,
DocumentSigningOrder,
FieldType,
ReadStatus,
RecipientRole,
SendStatus,
SigningStatus,
TeamMemberRole,
TemplateType,
} from '@prisma/client';
import { z } from 'zod'; import { z } from 'zod';
import { DATE_FORMATS, DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats'; import { DATE_FORMATS, DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
@@ -12,18 +24,6 @@ import {
} from '@documenso/lib/types/document-auth'; } from '@documenso/lib/types/document-auth';
import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email'; import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';
import { ZFieldMetaSchema } from '@documenso/lib/types/field-meta'; import { ZFieldMetaSchema } from '@documenso/lib/types/field-meta';
import {
DocumentDataType,
DocumentDistributionMethod,
DocumentSigningOrder,
FieldType,
ReadStatus,
RecipientRole,
SendStatus,
SigningStatus,
TeamMemberRole,
TemplateType,
} from '@documenso/prisma/client';
extendZodWithOpenApi(z); extendZodWithOpenApi(z);

View File

@@ -1,4 +1,5 @@
import { expect, test } from '@playwright/test'; import { expect, test } from '@playwright/test';
import { TeamMemberRole } from '@prisma/client';
import { import {
ZFindTeamMembersResponseSchema, ZFindTeamMembersResponseSchema,
@@ -10,7 +11,6 @@ import {
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app'; import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
import { createApiToken } from '@documenso/lib/server-only/public-api/create-api-token'; import { createApiToken } from '@documenso/lib/server-only/public-api/create-api-token';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import { TeamMemberRole } from '@documenso/prisma/client';
import { seedTeam } from '@documenso/prisma/seed/teams'; import { seedTeam } from '@documenso/prisma/seed/teams';
import { seedUser } from '@documenso/prisma/seed/users'; import { seedUser } from '@documenso/prisma/seed/users';

View File

@@ -1,11 +1,11 @@
import { expect, test } from '@playwright/test'; import { expect, test } from '@playwright/test';
import { FieldType } from '@prisma/client';
import { ZRecipientAuthOptionsSchema } from '@documenso/lib/types/document-auth'; import { ZRecipientAuthOptionsSchema } from '@documenso/lib/types/document-auth';
import { import {
createDocumentAuthOptions, createDocumentAuthOptions,
createRecipientAuthOptions, createRecipientAuthOptions,
} from '@documenso/lib/utils/document-auth'; } from '@documenso/lib/utils/document-auth';
import { FieldType } from '@documenso/prisma/client';
import { import {
seedPendingDocumentNoFields, seedPendingDocumentNoFields,
seedPendingDocumentWithFullFields, seedPendingDocumentWithFullFields,

View File

@@ -1,16 +1,16 @@
import { expect, test } from '@playwright/test'; import { expect, test } from '@playwright/test';
import { DateTime } from 'luxon';
import path from 'node:path';
import { getRecipientByEmail } from '@documenso/lib/server-only/recipient/get-recipient-by-email';
import { prisma } from '@documenso/prisma';
import { import {
DocumentSigningOrder, DocumentSigningOrder,
DocumentStatus, DocumentStatus,
FieldType, FieldType,
RecipientRole, RecipientRole,
SigningStatus, SigningStatus,
} from '@documenso/prisma/client'; } from '@prisma/client';
import { DateTime } from 'luxon';
import path from 'node:path';
import { getRecipientByEmail } from '@documenso/lib/server-only/recipient/get-recipient-by-email';
import { prisma } from '@documenso/prisma';
import { import {
seedBlankDocument, seedBlankDocument,
seedPendingDocumentWithFullFields, seedPendingDocumentWithFullFields,

View File

@@ -1,10 +1,10 @@
import { expect, test } from '@playwright/test'; import { expect, test } from '@playwright/test';
import { DocumentStatus, FieldType } from '@prisma/client';
import { PDFDocument } from 'pdf-lib'; import { PDFDocument } from 'pdf-lib';
import { getDocumentByToken } from '@documenso/lib/server-only/document/get-document-by-token'; import { getDocumentByToken } from '@documenso/lib/server-only/document/get-document-by-token';
import { getFile } from '@documenso/lib/universal/upload/get-file'; import { getFile } from '@documenso/lib/universal/upload/get-file';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import { DocumentStatus, FieldType } from '@documenso/prisma/client';
import { seedPendingDocumentWithFullFields } from '@documenso/prisma/seed/documents'; import { seedPendingDocumentWithFullFields } from '@documenso/prisma/seed/documents';
import { seedTeam } from '@documenso/prisma/seed/teams'; import { seedTeam } from '@documenso/prisma/seed/teams';
import { seedUser } from '@documenso/prisma/seed/users'; import { seedUser } from '@documenso/prisma/seed/users';

View File

@@ -1,6 +1,6 @@
import { expect, test } from '@playwright/test'; import { expect, test } from '@playwright/test';
import { DocumentStatus, TeamMemberRole } from '@prisma/client';
import { DocumentStatus, TeamMemberRole } from '@documenso/prisma/client';
import { seedDocuments, seedTeamDocuments } from '@documenso/prisma/seed/documents'; import { seedDocuments, seedTeamDocuments } from '@documenso/prisma/seed/documents';
import { seedTeam, seedTeamMember } from '@documenso/prisma/seed/teams'; import { seedTeam, seedTeamMember } from '@documenso/prisma/seed/teams';
import { seedUser } from '@documenso/prisma/seed/users'; import { seedUser } from '@documenso/prisma/seed/users';

View File

@@ -1,6 +1,6 @@
import { expect, test } from '@playwright/test'; import { expect, test } from '@playwright/test';
import { DocumentStatus, DocumentVisibility, TeamMemberRole } from '@prisma/client';
import { DocumentStatus, DocumentVisibility, TeamMemberRole } from '@documenso/prisma/client';
import { seedBlankDocument } from '@documenso/prisma/seed/documents'; import { seedBlankDocument } from '@documenso/prisma/seed/documents';
import { seedDocuments, seedTeamDocuments } from '@documenso/prisma/seed/documents'; import { seedDocuments, seedTeamDocuments } from '@documenso/prisma/seed/documents';
import { seedTeam, seedTeamEmail, seedTeamMember } from '@documenso/prisma/seed/teams'; import { seedTeam, seedTeamEmail, seedTeamMember } from '@documenso/prisma/seed/teams';

View File

@@ -1,7 +1,7 @@
import { expect, test } from '@playwright/test'; import { expect, test } from '@playwright/test';
import { TeamMemberRole } from '@prisma/client';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import { TeamMemberRole } from '@documenso/prisma/client';
import { seedUserSubscription } from '@documenso/prisma/seed/subscriptions'; import { seedUserSubscription } from '@documenso/prisma/seed/subscriptions';
import { seedTeam } from '@documenso/prisma/seed/teams'; import { seedTeam } from '@documenso/prisma/seed/teams';
import { seedBlankTemplate } from '@documenso/prisma/seed/templates'; import { seedBlankTemplate } from '@documenso/prisma/seed/templates';
@@ -12,7 +12,7 @@ import { apiSignin } from '../fixtures/authentication';
test.describe.configure({ mode: 'parallel' }); test.describe.configure({ mode: 'parallel' });
test.describe('[EE_ONLY]', () => { test.describe('[EE_ONLY]', () => {
const enterprisePriceId = process.env.NEXT_PUBLIC_STRIPE_ENTERPRISE_PLAN_MONTHLY_PRICE_ID || ''; const enterprisePriceId = '';
test.beforeEach(() => { test.beforeEach(() => {
test.skip( test.skip(

View File

@@ -1,11 +1,11 @@
import { expect, test } from '@playwright/test'; import { expect, test } from '@playwright/test';
import { DocumentDataType, TeamMemberRole } from '@prisma/client';
import fs from 'fs'; import fs from 'fs';
import os from 'os'; import os from 'os';
import path from 'path'; import path from 'path';
import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth'; import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import { DocumentDataType, TeamMemberRole } from '@documenso/prisma/client';
import { seedUserSubscription } from '@documenso/prisma/seed/subscriptions'; import { seedUserSubscription } from '@documenso/prisma/seed/subscriptions';
import { seedTeam } from '@documenso/prisma/seed/teams'; import { seedTeam } from '@documenso/prisma/seed/teams';
import { seedBlankTemplate } from '@documenso/prisma/seed/templates'; import { seedBlankTemplate } from '@documenso/prisma/seed/templates';
@@ -15,7 +15,7 @@ import { apiSignin } from '../fixtures/authentication';
test.describe.configure({ mode: 'parallel' }); test.describe.configure({ mode: 'parallel' });
const enterprisePriceId = process.env.NEXT_PUBLIC_STRIPE_ENTERPRISE_PLAN_MONTHLY_PRICE_ID || ''; const enterprisePriceId = '';
// Create a temporary PDF file for testing // Create a temporary PDF file for testing
function createTempPdfFile() { function createTempPdfFile() {

View File

@@ -1,3 +1,4 @@
import { UserSecurityAuditLogType } from '@prisma/client';
import { OAuth2Client, decodeIdToken } from 'arctic'; import { OAuth2Client, decodeIdToken } from 'arctic';
import type { Context } from 'hono'; import type { Context } from 'hono';
import { deleteCookie } from 'hono/cookie'; import { deleteCookie } from 'hono/cookie';
@@ -6,7 +7,6 @@ import { nanoid } from 'nanoid';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { onCreateUserHook } from '@documenso/lib/server-only/user/create-user'; import { onCreateUserHook } from '@documenso/lib/server-only/user/create-user';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import { UserSecurityAuditLogType } from '@documenso/prisma/client';
import type { OAuthClientOptions } from '../../config'; import type { OAuthClientOptions } from '../../config';
import { AuthenticationErrorCode } from '../errors/error-codes'; import { AuthenticationErrorCode } from '../errors/error-codes';

View File

@@ -1,5 +1,6 @@
import { sValidator } from '@hono/standard-validator'; import { sValidator } from '@hono/standard-validator';
import { compare } from '@node-rs/bcrypt'; import { compare } from '@node-rs/bcrypt';
import { UserSecurityAuditLogType } from '@prisma/client';
import { Hono } from 'hono'; import { Hono } from 'hono';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { z } from 'zod'; import { z } from 'zod';
@@ -22,7 +23,6 @@ import { updatePassword } from '@documenso/lib/server-only/user/update-password'
import { verifyEmail } from '@documenso/lib/server-only/user/verify-email'; import { verifyEmail } from '@documenso/lib/server-only/user/verify-email';
import { env } from '@documenso/lib/utils/env'; import { env } from '@documenso/lib/utils/env';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import { UserSecurityAuditLogType } from '@documenso/prisma/client';
import { AuthenticationErrorCode } from '../lib/errors/error-codes'; import { AuthenticationErrorCode } from '../lib/errors/error-codes';
import { getCsrfCookie } from '../lib/session/session-cookies'; import { getCsrfCookie } from '../lib/session/session-cookies';

View File

@@ -1,8 +1,8 @@
import { DocumentSource, SubscriptionStatus } from '@prisma/client';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app'; import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import { DocumentSource, SubscriptionStatus } from '@documenso/prisma/client';
import { getDocumentRelatedPrices } from '../stripe/get-document-related-prices.ts'; import { getDocumentRelatedPrices } from '../stripe/get-document-related-prices.ts';
import { FREE_PLAN_LIMITS, SELFHOSTED_PLAN_LIMITS, TEAM_PLAN_LIMITS } from './constants'; import { FREE_PLAN_LIMITS, SELFHOSTED_PLAN_LIMITS, TEAM_PLAN_LIMITS } from './constants';

View File

@@ -1,7 +1,8 @@
import type { User } from '@prisma/client';
import { STRIPE_CUSTOMER_TYPE } from '@documenso/lib/constants/billing'; import { STRIPE_CUSTOMER_TYPE } from '@documenso/lib/constants/billing';
import { stripe } from '@documenso/lib/server-only/stripe'; import { stripe } from '@documenso/lib/server-only/stripe';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import type { User } from '@documenso/prisma/client';
import { onSubscriptionUpdated } from './webhook/on-subscription-updated'; import { onSubscriptionUpdated } from './webhook/on-subscription-updated';

View File

@@ -4,15 +4,14 @@ import { stripe } from '@documenso/lib/server-only/stripe';
type PlanType = (typeof STRIPE_PLAN_TYPE)[keyof typeof STRIPE_PLAN_TYPE]; type PlanType = (typeof STRIPE_PLAN_TYPE)[keyof typeof STRIPE_PLAN_TYPE];
export const getPricesByPlan = async (plan: PlanType | PlanType[]) => { export const getPricesByPlan = async (plan: PlanType | PlanType[]) => {
const planTypes = typeof plan === 'string' ? [plan] : plan; const planTypes: string[] = typeof plan === 'string' ? [plan] : plan;
const query = planTypes.map((planType) => `metadata['plan']:'${planType}'`).join(' OR '); const prices = await stripe.prices.list({
const { data: prices } = await stripe.prices.search({
query,
expand: ['data.product'], expand: ['data.product'],
limit: 100, limit: 100,
}); });
return prices.filter((price) => price.type === 'recurring'); return prices.data.filter(
(price) => price.type === 'recurring' && planTypes.includes(price.metadata.plan),
);
}; };

View File

@@ -1,10 +1,10 @@
import { type Subscription, type Team, type User } from '@prisma/client';
import type Stripe from 'stripe'; import type Stripe from 'stripe';
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error'; import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
import { stripe } from '@documenso/lib/server-only/stripe'; import { stripe } from '@documenso/lib/server-only/stripe';
import { subscriptionsContainsActivePlan } from '@documenso/lib/utils/billing'; import { subscriptionsContainsActivePlan } from '@documenso/lib/utils/billing';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import { type Subscription, type Team, type User } from '@documenso/prisma/client';
import { deleteCustomerPaymentMethods } from './delete-customer-payment-methods'; import { deleteCustomerPaymentMethods } from './delete-customer-payment-methods';
import { getTeamPrices } from './get-team-prices'; import { getTeamPrices } from './get-team-prices';

View File

@@ -1,6 +1,7 @@
import { SubscriptionStatus } from '@prisma/client';
import type { Stripe } from '@documenso/lib/server-only/stripe'; import type { Stripe } from '@documenso/lib/server-only/stripe';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import { SubscriptionStatus } from '@documenso/prisma/client';
export type OnSubscriptionDeletedOptions = { export type OnSubscriptionDeletedOptions = {
subscription: Stripe.Subscription; subscription: Stripe.Subscription;

View File

@@ -1,9 +1,9 @@
import type { Prisma } from '@prisma/client';
import { SubscriptionStatus } from '@prisma/client';
import { match } from 'ts-pattern'; import { match } from 'ts-pattern';
import type { Stripe } from '@documenso/lib/server-only/stripe'; import type { Stripe } from '@documenso/lib/server-only/stripe';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import type { Prisma } from '@documenso/prisma/client';
import { SubscriptionStatus } from '@documenso/prisma/client';
export type OnSubscriptionUpdatedOptions = { export type OnSubscriptionUpdatedOptions = {
userId?: number; userId?: number;

View File

@@ -1,6 +1,7 @@
import type { Subscription } from '@prisma/client';
import { subscriptionsContainsActivePlan } from '@documenso/lib/utils/billing'; import { subscriptionsContainsActivePlan } from '@documenso/lib/utils/billing';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import type { Subscription } from '@documenso/prisma/client';
import { getCommunityPlanPriceIds } from '../stripe/get-community-plan-prices'; import { getCommunityPlanPriceIds } from '../stripe/get-community-plan-prices';

View File

@@ -1,7 +1,8 @@
import type { Subscription } from '@prisma/client';
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app'; import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
import { subscriptionsContainsActivePlan } from '@documenso/lib/utils/billing'; import { subscriptionsContainsActivePlan } from '@documenso/lib/utils/billing';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import type { Subscription } from '@documenso/prisma/client';
import { getEnterprisePlanPriceIds } from '../stripe/get-enterprise-plan-prices'; import { getEnterprisePlanPriceIds } from '../stripe/get-enterprise-plan-prices';

View File

@@ -1,7 +1,8 @@
import type { Document, Subscription } from '@prisma/client';
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app'; import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
import { subscriptionsContainsActivePlan } from '@documenso/lib/utils/billing'; import { subscriptionsContainsActivePlan } from '@documenso/lib/utils/billing';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import type { Document, Subscription } from '@documenso/prisma/client';
import { getPlatformPlanPriceIds } from '../stripe/get-platform-plan-prices'; import { getPlatformPlanPriceIds } from '../stripe/get-platform-plan-prices';

View File

@@ -2,10 +2,10 @@ import { useMemo } from 'react';
import { useLingui } from '@lingui/react'; import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro';
import { RecipientRole } from '@prisma/client';
import { P, match } from 'ts-pattern'; import { P, match } from 'ts-pattern';
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles'; import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
import { RecipientRole } from '@documenso/prisma/client';
import { Button, Section, Text } from '../components'; import { Button, Section, Text } from '../components';
import { TemplateDocumentImage } from './template-document-image'; import { TemplateDocumentImage } from './template-document-image';

View File

@@ -1,9 +1,9 @@
import { msg } from '@lingui/core/macro'; import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react'; import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro';
import { RecipientRole } from '@prisma/client';
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles'; import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
import { RecipientRole } from '@documenso/prisma/client';
import { Body, Button, Container, Head, Html, Img, Preview, Section, Text } from '../components'; import { Body, Button, Container, Head, Html, Img, Preview, Section, Text } from '../components';
import { useBranding } from '../providers/branding'; import { useBranding } from '../providers/branding';

View File

@@ -1,9 +1,9 @@
import { msg } from '@lingui/core/macro'; import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react'; import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro';
import type { RecipientRole } from '@prisma/client';
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles'; import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
import type { RecipientRole } from '@documenso/prisma/client';
import { Body, Container, Head, Hr, Html, Img, Link, Preview, Section, Text } from '../components'; import { Body, Container, Head, Hr, Html, Img, Link, Preview, Section, Text } from '../components';
import { useBranding } from '../providers/branding'; import { useBranding } from '../providers/branding';

View File

@@ -1,12 +1,12 @@
import { createContext, useCallback, useContext, useEffect, useState } from 'react'; import { createContext, useCallback, useContext, useEffect, useState } from 'react';
import React from 'react'; import React from 'react';
import type { Session } from '@prisma/client';
import { useLocation } from 'react-router'; import { useLocation } from 'react-router';
import { authClient } from '@documenso/auth/client'; import { authClient } from '@documenso/auth/client';
import type { SessionUser } from '@documenso/auth/server/lib/session/session'; import type { SessionUser } from '@documenso/auth/server/lib/session/session';
import { type TGetTeamsResponse } from '@documenso/lib/server-only/team/get-teams'; import { type TGetTeamsResponse } from '@documenso/lib/server-only/team/get-teams';
import type { Session } from '@documenso/prisma/client';
import { trpc } from '@documenso/trpc/client'; import { trpc } from '@documenso/trpc/client';
export type AppSession = { export type AppSession = {

View File

@@ -1,6 +1,7 @@
import { createElement } from 'react'; import { createElement } from 'react';
import { msg } from '@lingui/macro'; import { msg } from '@lingui/macro';
import type { TeamGlobalSettings } from '@prisma/client';
import { parse } from 'csv-parse/sync'; import { parse } from 'csv-parse/sync';
import { z } from 'zod'; import { z } from 'zod';
@@ -10,7 +11,6 @@ import { sendDocument } from '@documenso/lib/server-only/document/send-document'
import { createDocumentFromTemplate } from '@documenso/lib/server-only/template/create-document-from-template'; import { createDocumentFromTemplate } from '@documenso/lib/server-only/template/create-document-from-template';
import { getTemplateById } from '@documenso/lib/server-only/template/get-template-by-id'; import { getTemplateById } from '@documenso/lib/server-only/template/get-template-by-id';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import type { TeamGlobalSettings } from '@documenso/prisma/client';
import { getI18nInstance } from '../../../client-only/providers/i18n-server'; import { getI18nInstance } from '../../../client-only/providers/i18n-server';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app'; import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';

View File

@@ -1,5 +1,6 @@
import { FieldType, RecipientRole, SigningStatus } from '@prisma/client';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import { FieldType, RecipientRole, SigningStatus } from '@documenso/prisma/client';
export type GetFieldsForTokenOptions = { export type GetFieldsForTokenOptions = {
token: string; token: string;

View File

@@ -1,5 +1,6 @@
import { TeamMemberRole } from '@prisma/client';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import { TeamMemberRole } from '@documenso/prisma/client';
export type GetApiTokensOptions = { export type GetApiTokensOptions = {
userId: number; userId: number;

View File

@@ -1,5 +1,6 @@
import { FieldType } from '@prisma/client';
import { prisma } from '@documenso/prisma'; import { prisma } from '@documenso/prisma';
import { FieldType } from '@documenso/prisma/client';
import { AppError, AppErrorCode } from '../../errors/app-error'; import { AppError, AppErrorCode } from '../../errors/app-error';

View File

@@ -2804,6 +2804,8 @@ msgid "Field format"
msgstr "Feldformat" msgstr "Feldformat"
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx
msgid "Field label" msgid "Field label"
msgstr "Feldbeschriftung" msgstr "Feldbeschriftung"
@@ -3171,8 +3173,10 @@ msgid "Join {teamName} on Documenso"
msgstr "Tritt {teamName} auf Documenso bei" msgstr "Tritt {teamName} auf Documenso bei"
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx
msgid "Label" msgid "Label"
msgstr "Beschriftung" msgstr "Beschriftung"

View File

@@ -2799,6 +2799,8 @@ msgid "Field format"
msgstr "Field format" msgstr "Field format"
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx
msgid "Field label" msgid "Field label"
msgstr "Field label" msgstr "Field label"
@@ -3166,8 +3168,10 @@ msgid "Join {teamName} on Documenso"
msgstr "Join {teamName} on Documenso" msgstr "Join {teamName} on Documenso"
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx
msgid "Label" msgid "Label"
msgstr "Label" msgstr "Label"

View File

@@ -2804,6 +2804,8 @@ msgid "Field format"
msgstr "Formato de campo" msgstr "Formato de campo"
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx
msgid "Field label" msgid "Field label"
msgstr "Etiqueta de campo" msgstr "Etiqueta de campo"
@@ -3171,8 +3173,10 @@ msgid "Join {teamName} on Documenso"
msgstr "Únete a {teamName} en Documenso" msgstr "Únete a {teamName} en Documenso"
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx
msgid "Label" msgid "Label"
msgstr "Etiqueta" msgstr "Etiqueta"

View File

@@ -2804,6 +2804,8 @@ msgid "Field format"
msgstr "Format du champ" msgstr "Format du champ"
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx
msgid "Field label" msgid "Field label"
msgstr "Étiquette du champ" msgstr "Étiquette du champ"
@@ -3171,8 +3173,10 @@ msgid "Join {teamName} on Documenso"
msgstr "Rejoindre {teamName} sur Documenso" msgstr "Rejoindre {teamName} sur Documenso"
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx
msgid "Label" msgid "Label"
msgstr "Étiquette" msgstr "Étiquette"

View File

@@ -2804,6 +2804,8 @@ msgid "Field format"
msgstr "Formato del campo" msgstr "Formato del campo"
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx
msgid "Field label" msgid "Field label"
msgstr "Etichetta del campo" msgstr "Etichetta del campo"
@@ -3171,8 +3173,10 @@ msgid "Join {teamName} on Documenso"
msgstr "Unisci a {teamName} su Documenso" msgstr "Unisci a {teamName} su Documenso"
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx
msgid "Label" msgid "Label"
msgstr "Etichetta" msgstr "Etichetta"

View File

@@ -2804,6 +2804,8 @@ msgid "Field format"
msgstr "Format pola" msgstr "Format pola"
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx
msgid "Field label" msgid "Field label"
msgstr "Etykieta pola" msgstr "Etykieta pola"
@@ -3171,8 +3173,10 @@ msgid "Join {teamName} on Documenso"
msgstr "Dołącz do {teamName} na Documenso" msgstr "Dołącz do {teamName} na Documenso"
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/radio-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx #: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
#: packages/ui/primitives/document-flow/field-items-advanced-settings/checkbox-field.tsx
msgid "Label" msgid "Label"
msgstr "Etykieta" msgstr "Etykieta"

View File

@@ -11,10 +11,11 @@ generator json {
} }
generator zod { generator zod {
provider = "zod-prisma-types" provider = "zod-prisma-types"
createInputTypes = false createInputTypes = false
writeBarrelFiles = false writeBarrelFiles = false
useMultipleFiles = true useMultipleFiles = true
useDefaultValidators = false
} }
datasource db { datasource db {

View File

@@ -1,4 +1,4 @@
import type { Document, DocumentData, Recipient } from '@documenso/prisma/client'; import type { Document, DocumentData, Recipient } from '@prisma/client';
export type DocumentWithRecipients = Document & { export type DocumentWithRecipients = Document & {
recipients: Recipient[]; recipients: Recipient[];

View File

@@ -1,5 +1,6 @@
import type { Field, Signature } from '@prisma/client';
import { type TFieldMetaSchema as FieldMeta } from '@documenso/lib/types/field-meta'; import { type TFieldMetaSchema as FieldMeta } from '@documenso/lib/types/field-meta';
import type { Field, Signature } from '@documenso/prisma/client';
export type FieldWithSignatureAndFieldMeta = Field & { export type FieldWithSignatureAndFieldMeta = Field & {
signature?: Signature | null; signature?: Signature | null;

View File

@@ -1,4 +1,4 @@
import type { Field, Signature } from '@documenso/prisma/client'; import type { Field, Signature } from '@prisma/client';
export type FieldWithSignature = Field & { export type FieldWithSignature = Field & {
signature?: Signature | null; signature?: Signature | null;

View File

@@ -1,4 +1,4 @@
import type { Field, Recipient } from '@documenso/prisma/client'; import type { Field, Recipient } from '@prisma/client';
export type RecipientWithFields = Recipient & { export type RecipientWithFields = Recipient & {
fields: Field[]; fields: Field[];

View File

@@ -1,3 +1,4 @@
import type { Session } from '@prisma/client';
import type { Context } from 'hono'; import type { Context } from 'hono';
import { z } from 'zod'; import { z } from 'zod';
@@ -5,7 +6,6 @@ import type { SessionUser } from '@documenso/auth/server/lib/session/session';
import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-session'; import { getOptionalSession } from '@documenso/auth/server/lib/utils/get-session';
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { extractRequestMetadata } from '@documenso/lib/universal/extract-request-metadata'; import { extractRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import type { Session } from '@documenso/prisma/client';
type CreateTrpcContextOptions = { type CreateTrpcContextOptions = {
c: Context; c: Context;

View File

@@ -3,9 +3,8 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { msg } from '@lingui/core/macro'; import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react'; import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro';
import { Prisma } from '@prisma/client';
import type { Field, Recipient } from '@prisma/client'; import type { Field, Recipient } from '@prisma/client';
import { FieldType, RecipientRole, SendStatus } from '@prisma/client'; import { FieldType, Prisma, RecipientRole, SendStatus } from '@prisma/client';
import { import {
CalendarDays, CalendarDays,
Check, Check,
@@ -430,6 +429,7 @@ export const AddFieldsFormPartial = ({
const newField: TAddFieldsFormSchema['fields'][0] = { const newField: TAddFieldsFormSchema['fields'][0] = {
...structuredClone(lastActiveField), ...structuredClone(lastActiveField),
nativeId: undefined,
formId: nanoid(12), formId: nanoid(12),
signerEmail: selectedSigner?.email ?? lastActiveField.signerEmail, signerEmail: selectedSigner?.email ?? lastActiveField.signerEmail,
pageX: lastActiveField.pageX + 3, pageX: lastActiveField.pageX + 3,
@@ -451,6 +451,7 @@ export const AddFieldsFormPartial = ({
append({ append({
...copiedField, ...copiedField,
nativeId: undefined,
formId: nanoid(12), formId: nanoid(12),
signerEmail: selectedSigner?.email ?? copiedField.signerEmail, signerEmail: selectedSigner?.email ?? copiedField.signerEmail,
pageX: copiedField.pageX + 3, pageX: copiedField.pageX + 3,
@@ -649,6 +650,8 @@ export const AddFieldsFormPartial = ({
passive={isFieldWithinBounds && !!selectedField} passive={isFieldWithinBounds && !!selectedField}
onFocus={() => setLastActiveField(field)} onFocus={() => setLastActiveField(field)}
onBlur={() => setLastActiveField(null)} onBlur={() => setLastActiveField(null)}
onMouseEnter={() => setLastActiveField(field)}
onMouseLeave={() => setLastActiveField(null)}
onResize={(options) => onFieldResize(options, index)} onResize={(options) => onFieldResize(options, index)}
onMove={(options) => onFieldMove(options, index)} onMove={(options) => onFieldMove(options, index)}
onRemove={() => remove(index)} onRemove={() => remove(index)}

View File

@@ -1,5 +1,6 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { FieldType } from '@prisma/client';
import { CopyPlus, Settings2, Trash } from 'lucide-react'; import { CopyPlus, Settings2, Trash } from 'lucide-react';
import { createPortal } from 'react-dom'; import { createPortal } from 'react-dom';
import { Rnd } from 'react-rnd'; import { Rnd } from 'react-rnd';
@@ -33,6 +34,8 @@ export type FieldItemProps = {
onAdvancedSettings?: () => void; onAdvancedSettings?: () => void;
onFocus?: () => void; onFocus?: () => void;
onBlur?: () => void; onBlur?: () => void;
onMouseEnter?: () => void;
onMouseLeave?: () => void;
recipientIndex?: number; recipientIndex?: number;
hideRecipients?: boolean; hideRecipients?: boolean;
hasErrors?: boolean; hasErrors?: boolean;
@@ -174,11 +177,35 @@ export const FieldItem = ({
() => hasFieldMetaValues('CHECKBOX', field.fieldMeta, ZCheckboxFieldMeta), () => hasFieldMetaValues('CHECKBOX', field.fieldMeta, ZCheckboxFieldMeta),
[field.fieldMeta], [field.fieldMeta],
); );
const radioHasValues = useMemo( const radioHasValues = useMemo(
() => hasFieldMetaValues('RADIO', field.fieldMeta, ZRadioFieldMeta), () => hasFieldMetaValues('RADIO', field.fieldMeta, ZRadioFieldMeta),
[field.fieldMeta], [field.fieldMeta],
); );
const hasCheckedValues = (fieldMeta: TFieldMetaSchema, type: FieldType) => {
if (!fieldMeta || (type !== FieldType.RADIO && type !== FieldType.CHECKBOX)) {
return false;
}
if (type === FieldType.RADIO) {
const parsed = ZRadioFieldMeta.parse(fieldMeta);
return parsed.values?.some((value) => value.checked) ?? false;
}
if (type === FieldType.CHECKBOX) {
const parsed = ZCheckboxFieldMeta.parse(fieldMeta);
return parsed.values?.some((value) => value.checked) ?? false;
}
return false;
};
const fieldHasCheckedValues = useMemo(
() => hasCheckedValues(field.fieldMeta, field.type),
[field.fieldMeta, field.type],
);
const fixedSize = checkBoxHasValues || radioHasValues; const fixedSize = checkBoxHasValues || radioHasValues;
return createPortal( return createPortal(
@@ -202,6 +229,8 @@ export const FieldItem = ({
bounds={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.pageNumber}"]`} bounds={`${PDF_VIEWER_PAGE_SELECTOR}[data-page-number="${field.pageNumber}"]`}
onDragStart={() => onFieldActivate?.()} onDragStart={() => onFieldActivate?.()}
onResizeStart={() => onFieldActivate?.()} onResizeStart={() => onFieldActivate?.()}
onMouseEnter={() => onFocus?.()}
onMouseLeave={() => onBlur?.()}
enableResizing={!fixedSize} enableResizing={!fixedSize}
resizeHandleStyles={{ resizeHandleStyles={{
bottom: { bottom: -8, cursor: 'ns-resize' }, bottom: { bottom: -8, cursor: 'ns-resize' },
@@ -218,6 +247,21 @@ export const FieldItem = ({
onMove?.(d.node); onMove?.(d.node);
}} }}
> >
{(field.type === FieldType.RADIO || field.type === FieldType.CHECKBOX) &&
field.fieldMeta?.label && (
<div
className={cn(
'absolute -top-16 left-0 right-0 rounded-md p-2 text-center text-xs text-gray-700',
{
'bg-foreground/5 border-primary border': !fieldHasCheckedValues,
'bg-documenso-200 border-primary border': fieldHasCheckedValues,
},
)}
>
{field.fieldMeta.label}
</div>
)}
<div <div
className={cn( className={cn(
'relative flex h-full w-full items-center justify-center bg-white', 'relative flex h-full w-full items-center justify-center bg-white',

View File

@@ -125,6 +125,18 @@ export const CheckboxFieldAdvancedSettings = ({
return ( return (
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<div className="mb-2">
<Label>
<Trans>Label</Trans>
</Label>
<Input
id="label"
className="bg-background mt-2"
placeholder={_(msg`Field label`)}
value={fieldState.label}
onChange={(e) => handleFieldChange('label', e.target.value)}
/>
</div>
<div className="flex flex-row items-center gap-x-4"> <div className="flex flex-row items-center gap-x-4">
<div className="flex w-2/3 flex-col"> <div className="flex w-2/3 flex-col">
<Label> <Label>

View File

@@ -1,5 +1,7 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro';
import { ChevronDown, ChevronUp, Trash } from 'lucide-react'; import { ChevronDown, ChevronUp, Trash } from 'lucide-react';
@@ -25,6 +27,8 @@ export const RadioFieldAdvancedSettings = ({
handleFieldChange, handleFieldChange,
handleErrors, handleErrors,
}: RadioFieldAdvancedSettingsProps) => { }: RadioFieldAdvancedSettingsProps) => {
const { _ } = useLingui();
const [showValidation, setShowValidation] = useState(false); const [showValidation, setShowValidation] = useState(false);
const [values, setValues] = useState( const [values, setValues] = useState(
fieldState.values ?? [{ id: 1, checked: false, value: 'Default value' }], fieldState.values ?? [{ id: 1, checked: false, value: 'Default value' }],
@@ -100,6 +104,18 @@ export const RadioFieldAdvancedSettings = ({
return ( return (
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<div>
<Label>
<Trans>Label</Trans>
</Label>
<Input
id="label"
className="bg-background mt-2"
placeholder={_(msg`Field label`)}
value={fieldState.label}
onChange={(e) => handleFieldChange('label', e.target.value)}
/>
</div>
<div className="flex flex-row items-center gap-2"> <div className="flex flex-row items-center gap-2">
<Switch <Switch
className="bg-background" className="bg-background"

View File

@@ -165,6 +165,7 @@ export const AddTemplateFieldsFormPartial = ({
const newField: TAddTemplateFieldsFormSchema['fields'][0] = { const newField: TAddTemplateFieldsFormSchema['fields'][0] = {
...structuredClone(lastActiveField), ...structuredClone(lastActiveField),
formId: nanoid(12), formId: nanoid(12),
nativeId: undefined,
signerEmail: selectedSigner?.email ?? lastActiveField.signerEmail, signerEmail: selectedSigner?.email ?? lastActiveField.signerEmail,
signerId: selectedSigner?.id ?? lastActiveField.signerId, signerId: selectedSigner?.id ?? lastActiveField.signerId,
signerToken: selectedSigner?.token ?? lastActiveField.signerToken, signerToken: selectedSigner?.token ?? lastActiveField.signerToken,
@@ -195,6 +196,7 @@ export const AddTemplateFieldsFormPartial = ({
append({ append({
...copiedField, ...copiedField,
formId: nanoid(12), formId: nanoid(12),
nativeId: undefined,
signerEmail: selectedSigner?.email ?? copiedField.signerEmail, signerEmail: selectedSigner?.email ?? copiedField.signerEmail,
signerId: selectedSigner?.id ?? copiedField.signerId, signerId: selectedSigner?.id ?? copiedField.signerId,
signerToken: selectedSigner?.token ?? copiedField.signerToken, signerToken: selectedSigner?.token ?? copiedField.signerToken,
@@ -483,12 +485,6 @@ export const AddTemplateFieldsFormPartial = ({
form.setValue('fields', updatedFields); form.setValue('fields', updatedFields);
}; };
const isTypedSignatureEnabled = form.watch('typedSignatureEnabled');
const handleTypedSignatureChange = (value: boolean) => {
form.setValue('typedSignatureEnabled', value, { shouldDirty: true });
};
return ( return (
<> <>
{showAdvancedSettings && currentField ? ( {showAdvancedSettings && currentField ? (