mirror of
https://github.com/documenso/documenso.git
synced 2025-11-10 04:22:32 +10:00
Compare commits
4 Commits
chore/angu
...
v1.7.2-rc.
| Author | SHA1 | Date | |
|---|---|---|---|
| faf2bd5384 | |||
| d40ed94b74 | |||
| cd3d9b701b | |||
| e40f47a73c |
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@documenso/marketing",
|
||||
"version": "1.7.1-rc.3",
|
||||
"version": "1.7.2-rc.0",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0",
|
||||
"scripts": {
|
||||
@ -56,4 +56,4 @@
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@documenso/web",
|
||||
"version": "1.7.1-rc.3",
|
||||
"version": "1.7.2-rc.0",
|
||||
"private": true,
|
||||
"license": "AGPL-3.0",
|
||||
"scripts": {
|
||||
@ -74,4 +74,4 @@
|
||||
"@types/ua-parser-js": "^0.7.39",
|
||||
"typescript": "5.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-documen
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@documenso/ui/primitives/avatar';
|
||||
import { Tabs, TabsList, TabsTrigger } from '@documenso/ui/primitives/tabs';
|
||||
|
||||
import { DocumentSearch } from '~/components/(dashboard)/document-search/document-search';
|
||||
import { PeriodSelector } from '~/components/(dashboard)/period-selector/period-selector';
|
||||
import { isPeriodSelectorValue } from '~/components/(dashboard)/period-selector/types';
|
||||
import { DocumentStatus } from '~/components/formatter/document-status';
|
||||
@ -25,16 +26,17 @@ import { DataTableSenderFilter } from './data-table-sender-filter';
|
||||
import { EmptyDocumentState } from './empty-state';
|
||||
import { UploadDocument } from './upload-document';
|
||||
|
||||
export type DocumentsPageViewProps = {
|
||||
export interface DocumentsPageViewProps {
|
||||
searchParams?: {
|
||||
status?: ExtendedDocumentStatus;
|
||||
period?: PeriodSelectorValue;
|
||||
page?: string;
|
||||
perPage?: string;
|
||||
senderIds?: string;
|
||||
search?: string;
|
||||
};
|
||||
team?: Team & { teamEmail?: TeamEmail | null } & { currentTeamMember?: { role: TeamMemberRole } };
|
||||
};
|
||||
}
|
||||
|
||||
export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPageViewProps) => {
|
||||
const { user } = await getRequiredServerComponentSession();
|
||||
@ -44,6 +46,7 @@ export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPa
|
||||
const page = Number(searchParams.page) || 1;
|
||||
const perPage = Number(searchParams.perPage) || 20;
|
||||
const senderIds = parseToIntegerArray(searchParams.senderIds ?? '');
|
||||
const search = searchParams.search || '';
|
||||
const currentTeam = team
|
||||
? { id: team.id, url: team.url, teamEmail: team.teamEmail?.email }
|
||||
: undefined;
|
||||
@ -52,6 +55,7 @@ export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPa
|
||||
const getStatOptions: GetStatsInput = {
|
||||
user,
|
||||
period,
|
||||
search,
|
||||
};
|
||||
|
||||
if (team) {
|
||||
@ -79,6 +83,7 @@ export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPa
|
||||
perPage,
|
||||
period,
|
||||
senderIds,
|
||||
search,
|
||||
});
|
||||
|
||||
const getTabHref = (value: typeof status) => {
|
||||
@ -148,6 +153,9 @@ export const DocumentsPageView = async ({ searchParams = {}, team }: DocumentsPa
|
||||
<div className="flex w-48 flex-wrap items-center justify-between gap-x-2 gap-y-4">
|
||||
<PeriodSelector />
|
||||
</div>
|
||||
<div className="flex w-48 flex-wrap items-center justify-between gap-x-2 gap-y-4">
|
||||
<DocumentSearch initialValue={search} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
'use client';
|
||||
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
|
||||
import { useDebouncedValue } from '@documenso/lib/client-only/hooks/use-debounced-value';
|
||||
import { Input } from '@documenso/ui/primitives/input';
|
||||
|
||||
export const DocumentSearch = ({ initialValue = '' }: { initialValue?: string }) => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const [searchTerm, setSearchTerm] = useState(initialValue);
|
||||
const debouncedSearchTerm = useDebouncedValue(searchTerm, 500);
|
||||
|
||||
const handleSearch = useCallback(
|
||||
(term: string) => {
|
||||
const params = new URLSearchParams(searchParams?.toString() ?? '');
|
||||
if (term) {
|
||||
params.set('search', term);
|
||||
} else {
|
||||
params.delete('search');
|
||||
}
|
||||
router.push(`?${params.toString()}`);
|
||||
},
|
||||
[router, searchParams],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
handleSearch(searchTerm);
|
||||
}, [debouncedSearchTerm]);
|
||||
|
||||
return (
|
||||
<Input
|
||||
type="search"
|
||||
placeholder="Search documents..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@documenso/root",
|
||||
"version": "1.7.1-rc.3",
|
||||
"version": "1.7.2-rc.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@documenso/root",
|
||||
"version": "1.7.1-rc.3",
|
||||
"version": "1.7.2-rc.0",
|
||||
"workspaces": [
|
||||
"apps/*",
|
||||
"packages/*"
|
||||
@ -80,7 +80,7 @@
|
||||
},
|
||||
"apps/marketing": {
|
||||
"name": "@documenso/marketing",
|
||||
"version": "1.7.1-rc.3",
|
||||
"version": "1.7.2-rc.0",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@documenso/assets": "*",
|
||||
@ -441,7 +441,7 @@
|
||||
},
|
||||
"apps/web": {
|
||||
"name": "@documenso/web",
|
||||
"version": "1.7.1-rc.3",
|
||||
"version": "1.7.2-rc.0",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@documenso/api": "*",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "1.7.1-rc.3",
|
||||
"version": "1.7.2-rc.0",
|
||||
"scripts": {
|
||||
"build": "turbo run build",
|
||||
"build:web": "turbo run build --filter=@documenso/web",
|
||||
|
||||
249
packages/app-tests/e2e/teams/search-documents.spec.ts
Normal file
249
packages/app-tests/e2e/teams/search-documents.spec.ts
Normal file
@ -0,0 +1,249 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { DocumentStatus, TeamMemberRole } from '@documenso/prisma/client';
|
||||
import { seedDocuments, seedTeamDocuments } from '@documenso/prisma/seed/documents';
|
||||
import { seedTeam, seedTeamMember } from '@documenso/prisma/seed/teams';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
import { apiSignin, apiSignout } from '../fixtures/authentication';
|
||||
import { checkDocumentTabCount } from '../fixtures/documents';
|
||||
|
||||
test('[TEAMS]: search respects team document visibility', async ({ page }) => {
|
||||
const team = await seedTeam();
|
||||
const adminUser = await seedTeamMember({ teamId: team.id, role: TeamMemberRole.ADMIN });
|
||||
const managerUser = await seedTeamMember({ teamId: team.id, role: TeamMemberRole.MANAGER });
|
||||
const memberUser = await seedTeamMember({ teamId: team.id, role: TeamMemberRole.MEMBER });
|
||||
|
||||
await seedDocuments([
|
||||
{
|
||||
sender: team.owner,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'EVERYONE',
|
||||
title: 'Searchable Document for Everyone',
|
||||
},
|
||||
},
|
||||
{
|
||||
sender: team.owner,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'MANAGER_AND_ABOVE',
|
||||
title: 'Searchable Document for Managers',
|
||||
},
|
||||
},
|
||||
{
|
||||
sender: team.owner,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'ADMIN',
|
||||
title: 'Searchable Document for Admins',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const testCases = [
|
||||
{ user: adminUser, visibleDocs: 3 },
|
||||
{ user: managerUser, visibleDocs: 2 },
|
||||
{ user: memberUser, visibleDocs: 1 },
|
||||
];
|
||||
|
||||
for (const { user, visibleDocs } of testCases) {
|
||||
await apiSignin({
|
||||
page,
|
||||
email: user.email,
|
||||
redirectPath: `/t/${team.url}/documents`,
|
||||
});
|
||||
|
||||
await page.getByPlaceholder('Search documents...').fill('Searchable');
|
||||
await page.waitForURL(/search=Searchable/);
|
||||
|
||||
await checkDocumentTabCount(page, 'All', visibleDocs);
|
||||
|
||||
await apiSignout({ page });
|
||||
}
|
||||
});
|
||||
|
||||
test('[TEAMS]: search does not reveal documents from other teams', async ({ page }) => {
|
||||
const { team: teamA, teamMember2: teamAMember } = await seedTeamDocuments();
|
||||
const { team: teamB } = await seedTeamDocuments();
|
||||
|
||||
await seedDocuments([
|
||||
{
|
||||
sender: teamA.owner,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
documentOptions: {
|
||||
teamId: teamA.id,
|
||||
visibility: 'EVERYONE',
|
||||
title: 'Unique Team A Document',
|
||||
},
|
||||
},
|
||||
{
|
||||
sender: teamB.owner,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
documentOptions: {
|
||||
teamId: teamB.id,
|
||||
visibility: 'EVERYONE',
|
||||
title: 'Unique Team B Document',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: teamAMember.email,
|
||||
redirectPath: `/t/${teamA.url}/documents`,
|
||||
});
|
||||
|
||||
await page.getByPlaceholder('Search documents...').fill('Unique');
|
||||
await page.waitForURL(/search=Unique/);
|
||||
|
||||
await checkDocumentTabCount(page, 'All', 1);
|
||||
await expect(page.getByRole('link', { name: 'Unique Team A Document' })).toBeVisible();
|
||||
await expect(page.getByRole('link', { name: 'Unique Team B Document' })).not.toBeVisible();
|
||||
|
||||
await apiSignout({ page });
|
||||
});
|
||||
|
||||
test('[PERSONAL]: search does not reveal team documents in personal account', async ({ page }) => {
|
||||
const { team, teamMember2 } = await seedTeamDocuments();
|
||||
|
||||
await seedDocuments([
|
||||
{
|
||||
sender: teamMember2,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
documentOptions: {
|
||||
teamId: null,
|
||||
title: 'Personal Unique Document',
|
||||
},
|
||||
},
|
||||
{
|
||||
sender: team.owner,
|
||||
recipients: [],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'EVERYONE',
|
||||
title: 'Team Unique Document',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: teamMember2.email,
|
||||
redirectPath: '/documents',
|
||||
});
|
||||
|
||||
await page.getByPlaceholder('Search documents...').fill('Unique');
|
||||
await page.waitForURL(/search=Unique/);
|
||||
|
||||
await checkDocumentTabCount(page, 'All', 1);
|
||||
await expect(page.getByRole('link', { name: 'Personal Unique Document' })).toBeVisible();
|
||||
await expect(page.getByRole('link', { name: 'Team Unique Document' })).not.toBeVisible();
|
||||
|
||||
await apiSignout({ page });
|
||||
});
|
||||
|
||||
test('[TEAMS]: search respects recipient visibility regardless of team visibility', async ({
|
||||
page,
|
||||
}) => {
|
||||
const team = await seedTeam();
|
||||
const memberUser = await seedTeamMember({ teamId: team.id, role: TeamMemberRole.MEMBER });
|
||||
|
||||
await seedDocuments([
|
||||
{
|
||||
sender: team.owner,
|
||||
recipients: [memberUser],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'ADMIN',
|
||||
title: 'Admin Document with Member Recipient',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: memberUser.email,
|
||||
redirectPath: `/t/${team.url}/documents`,
|
||||
});
|
||||
|
||||
await page.getByPlaceholder('Search documents...').fill('Admin Document');
|
||||
await page.waitForURL(/search=Admin(%20|\+|\s)Document/);
|
||||
|
||||
await checkDocumentTabCount(page, 'All', 1);
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Admin Document with Member Recipient' }),
|
||||
).toBeVisible();
|
||||
|
||||
await apiSignout({ page });
|
||||
});
|
||||
|
||||
test('[TEAMS]: search by recipient name respects visibility', async ({ page }) => {
|
||||
const team = await seedTeam();
|
||||
const adminUser = await seedTeamMember({ teamId: team.id, role: TeamMemberRole.ADMIN });
|
||||
const memberUser = await seedTeamMember({
|
||||
teamId: team.id,
|
||||
role: TeamMemberRole.MEMBER,
|
||||
name: 'Team Member',
|
||||
});
|
||||
|
||||
const uniqueRecipient = await seedUser();
|
||||
|
||||
await seedDocuments([
|
||||
{
|
||||
sender: team.owner,
|
||||
recipients: [uniqueRecipient],
|
||||
type: DocumentStatus.COMPLETED,
|
||||
documentOptions: {
|
||||
teamId: team.id,
|
||||
visibility: 'ADMIN',
|
||||
title: 'Admin Document for Unique Recipient',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
// Admin should see the document when searching by recipient name
|
||||
await apiSignin({
|
||||
page,
|
||||
email: adminUser.email,
|
||||
redirectPath: `/t/${team.url}/documents`,
|
||||
});
|
||||
|
||||
await page.getByPlaceholder('Search documents...').fill('Unique Recipient');
|
||||
await page.waitForURL(/search=Unique(%20|\+|\s)Recipient/);
|
||||
|
||||
await checkDocumentTabCount(page, 'All', 1);
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Admin Document for Unique Recipient' }),
|
||||
).toBeVisible();
|
||||
|
||||
await apiSignout({ page });
|
||||
|
||||
// Member should not see the document when searching by recipient name
|
||||
await apiSignin({
|
||||
page,
|
||||
email: memberUser.email,
|
||||
redirectPath: `/t/${team.url}/documents`,
|
||||
});
|
||||
|
||||
await page.getByPlaceholder('Search documents...').fill('Unique Recipient');
|
||||
await page.waitForURL(/search=Unique(%20|\+|\s)Recipient/);
|
||||
|
||||
await checkDocumentTabCount(page, 'All', 0);
|
||||
await expect(
|
||||
page.getByRole('link', { name: 'Admin Document for Unique Recipient' }),
|
||||
).not.toBeVisible();
|
||||
|
||||
await apiSignout({ page });
|
||||
});
|
||||
@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
import { getBoundingClientRect } from '@documenso/lib/client-only/get-bounding-client-rect';
|
||||
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
|
||||
import { Field } from '@documenso/prisma/client';
|
||||
import type { Field } from '@documenso/prisma/client';
|
||||
|
||||
export const useFieldPageCoords = (field: Field) => {
|
||||
const [coords, setCoords] = useState({
|
||||
|
||||
@ -25,6 +25,7 @@ export type FindDocumentsOptions = {
|
||||
};
|
||||
period?: PeriodSelectorValue;
|
||||
senderIds?: number[];
|
||||
search?: string;
|
||||
};
|
||||
|
||||
export const findDocuments = async ({
|
||||
@ -37,6 +38,7 @@ export const findDocuments = async ({
|
||||
orderBy,
|
||||
period,
|
||||
senderIds,
|
||||
search,
|
||||
}: FindDocumentsOptions) => {
|
||||
const { user, team } = await prisma.$transaction(async (tx) => {
|
||||
const user = await tx.user.findFirstOrThrow({
|
||||
@ -92,6 +94,14 @@ export const findDocuments = async ({
|
||||
})
|
||||
.otherwise(() => undefined);
|
||||
|
||||
const searchFilter: Prisma.DocumentWhereInput = {
|
||||
OR: [
|
||||
{ title: { contains: search, mode: 'insensitive' } },
|
||||
{ Recipient: { some: { name: { contains: search, mode: 'insensitive' } } } },
|
||||
{ Recipient: { some: { email: { contains: search, mode: 'insensitive' } } } },
|
||||
],
|
||||
};
|
||||
|
||||
const visibilityFilters = [
|
||||
match(teamMemberRole)
|
||||
.with(TeamMemberRole.ADMIN, () => ({
|
||||
@ -188,7 +198,7 @@ export const findDocuments = async ({
|
||||
}
|
||||
|
||||
const whereClause: Prisma.DocumentWhereInput = {
|
||||
AND: [{ ...termFilters }, { ...filters }, { ...deletedFilter }],
|
||||
AND: [{ ...termFilters }, { ...filters }, { ...deletedFilter }, { ...searchFilter }],
|
||||
};
|
||||
|
||||
if (period) {
|
||||
|
||||
@ -15,9 +15,10 @@ export type GetStatsInput = {
|
||||
user: User;
|
||||
team?: Omit<GetTeamCountsOption, 'createdAt'>;
|
||||
period?: PeriodSelectorValue;
|
||||
search?: string;
|
||||
};
|
||||
|
||||
export const getStats = async ({ user, period, ...options }: GetStatsInput) => {
|
||||
export const getStats = async ({ user, period, search, ...options }: GetStatsInput) => {
|
||||
let createdAt: Prisma.DocumentWhereInput['createdAt'];
|
||||
|
||||
if (period) {
|
||||
@ -31,8 +32,14 @@ export const getStats = async ({ user, period, ...options }: GetStatsInput) => {
|
||||
}
|
||||
|
||||
const [ownerCounts, notSignedCounts, hasSignedCounts] = await (options.team
|
||||
? getTeamCounts({ ...options.team, createdAt, currentUserEmail: user.email, userId: user.id })
|
||||
: getCounts({ user, createdAt }));
|
||||
? getTeamCounts({
|
||||
...options.team,
|
||||
createdAt,
|
||||
currentUserEmail: user.email,
|
||||
userId: user.id,
|
||||
search,
|
||||
})
|
||||
: getCounts({ user, createdAt, search }));
|
||||
|
||||
const stats: Record<ExtendedDocumentStatus, number> = {
|
||||
[ExtendedDocumentStatus.DRAFT]: 0,
|
||||
@ -72,9 +79,18 @@ export const getStats = async ({ user, period, ...options }: GetStatsInput) => {
|
||||
type GetCountsOption = {
|
||||
user: User;
|
||||
createdAt: Prisma.DocumentWhereInput['createdAt'];
|
||||
search?: string;
|
||||
};
|
||||
|
||||
const getCounts = async ({ user, createdAt }: GetCountsOption) => {
|
||||
const getCounts = async ({ user, createdAt, search }: GetCountsOption) => {
|
||||
const searchFilter: Prisma.DocumentWhereInput = {
|
||||
OR: [
|
||||
{ title: { contains: search, mode: 'insensitive' } },
|
||||
{ Recipient: { some: { name: { contains: search, mode: 'insensitive' } } } },
|
||||
{ Recipient: { some: { email: { contains: search, mode: 'insensitive' } } } },
|
||||
],
|
||||
};
|
||||
|
||||
return Promise.all([
|
||||
// Owner counts.
|
||||
prisma.document.groupBy({
|
||||
@ -87,6 +103,7 @@ const getCounts = async ({ user, createdAt }: GetCountsOption) => {
|
||||
createdAt,
|
||||
teamId: null,
|
||||
deletedAt: null,
|
||||
AND: [searchFilter],
|
||||
},
|
||||
}),
|
||||
// Not signed counts.
|
||||
@ -105,6 +122,7 @@ const getCounts = async ({ user, createdAt }: GetCountsOption) => {
|
||||
},
|
||||
},
|
||||
createdAt,
|
||||
AND: [searchFilter],
|
||||
},
|
||||
}),
|
||||
// Has signed counts.
|
||||
@ -142,6 +160,7 @@ const getCounts = async ({ user, createdAt }: GetCountsOption) => {
|
||||
},
|
||||
},
|
||||
],
|
||||
AND: [searchFilter],
|
||||
},
|
||||
}),
|
||||
]);
|
||||
@ -155,6 +174,7 @@ type GetTeamCountsOption = {
|
||||
userId: number;
|
||||
createdAt: Prisma.DocumentWhereInput['createdAt'];
|
||||
currentTeamMemberRole?: TeamMemberRole;
|
||||
search?: string;
|
||||
};
|
||||
|
||||
const getTeamCounts = async (options: GetTeamCountsOption) => {
|
||||
@ -169,6 +189,14 @@ const getTeamCounts = async (options: GetTeamCountsOption) => {
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const searchFilter: Prisma.DocumentWhereInput = {
|
||||
OR: [
|
||||
{ title: { contains: options.search, mode: 'insensitive' } },
|
||||
{ Recipient: { some: { name: { contains: options.search, mode: 'insensitive' } } } },
|
||||
{ Recipient: { some: { email: { contains: options.search, mode: 'insensitive' } } } },
|
||||
],
|
||||
};
|
||||
|
||||
let ownerCountsWhereInput: Prisma.DocumentWhereInput = {
|
||||
userId: userIdWhereClause,
|
||||
createdAt,
|
||||
@ -220,6 +248,7 @@ const getTeamCounts = async (options: GetTeamCountsOption) => {
|
||||
},
|
||||
},
|
||||
],
|
||||
...searchFilter,
|
||||
};
|
||||
|
||||
if (teamEmail) {
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { nanoid } from '@documenso/lib/universal/id';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import type { DocumentSigningOrder, Field } from '@documenso/prisma/client';
|
||||
import {
|
||||
DocumentSigningOrder,
|
||||
DocumentSource,
|
||||
type Field,
|
||||
type Recipient,
|
||||
RecipientRole,
|
||||
SendStatus,
|
||||
@ -153,7 +154,7 @@ export const createDocumentFromTemplate = async ({
|
||||
const document = await tx.document.create({
|
||||
data: {
|
||||
source: DocumentSource.TEMPLATE,
|
||||
externalId,
|
||||
externalId: externalId || template.externalId,
|
||||
templateId: template.id,
|
||||
userId,
|
||||
teamId: template.teamId,
|
||||
@ -172,7 +173,9 @@ export const createDocumentFromTemplate = async ({
|
||||
dateFormat: override?.dateFormat || template.templateMeta?.dateFormat,
|
||||
redirectUrl: override?.redirectUrl || template.templateMeta?.redirectUrl,
|
||||
signingOrder:
|
||||
override?.signingOrder || template.templateMeta?.signingOrder || undefined,
|
||||
override?.signingOrder ||
|
||||
template.templateMeta?.signingOrder ||
|
||||
DocumentSigningOrder.PARALLEL,
|
||||
},
|
||||
},
|
||||
Recipient: {
|
||||
|
||||
@ -100,7 +100,7 @@ export const updateTemplateSettings = async ({
|
||||
},
|
||||
data: {
|
||||
title: data.title,
|
||||
externalId: data.externalId || null,
|
||||
externalId: data.externalId,
|
||||
type: data.type,
|
||||
publicDescription: data.publicDescription,
|
||||
publicTitle: data.publicTitle,
|
||||
|
||||
@ -115,7 +115,7 @@ msgstr "Admin"
|
||||
msgid "Advanced Options"
|
||||
msgstr "Erweiterte Optionen"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:527
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:565
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:402
|
||||
msgid "Advanced settings"
|
||||
msgstr "Erweiterte Einstellungen"
|
||||
@ -178,7 +178,7 @@ msgstr "CC'd"
|
||||
msgid "Character Limit"
|
||||
msgstr "Zeichenbeschränkung"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:950
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:993
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:788
|
||||
msgid "Checkbox"
|
||||
msgstr "Checkbox"
|
||||
@ -207,7 +207,7 @@ msgstr "Schließen"
|
||||
msgid "Configure Direct Recipient"
|
||||
msgstr "Direkten Empfänger konfigurieren"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:528
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:566
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:403
|
||||
msgid "Configure the {0} field"
|
||||
msgstr "Konfigurieren Sie das Feld {0}"
|
||||
@ -224,7 +224,7 @@ msgstr "In die Zwischenablage kopiert"
|
||||
msgid "Custom Text"
|
||||
msgstr "Benutzerdefinierter Text"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:846
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:889
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:684
|
||||
msgid "Date"
|
||||
msgstr "Datum"
|
||||
@ -256,7 +256,7 @@ msgstr "Herunterladen"
|
||||
msgid "Drag & drop your PDF here."
|
||||
msgstr "Ziehen Sie Ihr PDF hierher."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:976
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1019
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:814
|
||||
msgid "Dropdown"
|
||||
msgstr "Dropdown"
|
||||
@ -265,7 +265,7 @@ msgstr "Dropdown"
|
||||
msgid "Dropdown options"
|
||||
msgstr "Dropdown-Optionen"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:794
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:837
|
||||
#: packages/ui/primitives/document-flow/add-signature.tsx:272
|
||||
#: packages/ui/primitives/document-flow/add-signers.tsx:500
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:632
|
||||
@ -278,6 +278,10 @@ msgstr "E-Mail"
|
||||
msgid "Email Options"
|
||||
msgstr "E-Mail-Optionen"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1082
|
||||
msgid "Empty field"
|
||||
msgstr ""
|
||||
|
||||
#: packages/lib/constants/template.ts:8
|
||||
msgid "Enable Direct Link Signing"
|
||||
msgstr "Direktlink-Signierung aktivieren"
|
||||
@ -384,7 +388,7 @@ msgstr "Nachricht <0>(Optional)</0>"
|
||||
msgid "Min"
|
||||
msgstr "Min"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:820
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:863
|
||||
#: packages/ui/primitives/document-flow/add-signature.tsx:298
|
||||
#: packages/ui/primitives/document-flow/add-signers.tsx:535
|
||||
#: packages/ui/primitives/document-flow/add-signers.tsx:541
|
||||
@ -406,12 +410,12 @@ msgstr "Muss unterzeichnen"
|
||||
msgid "Needs to view"
|
||||
msgstr "Muss sehen"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:631
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:674
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:497
|
||||
msgid "No recipient matching this description was found."
|
||||
msgstr "Kein passender Empfänger mit dieser Beschreibung gefunden."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:647
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:690
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:513
|
||||
msgid "No recipients with this role"
|
||||
msgstr "Keine Empfänger mit dieser Rolle"
|
||||
@ -436,7 +440,7 @@ msgstr "Kein Unterschriftsfeld gefunden"
|
||||
msgid "No value found."
|
||||
msgstr "Kein Wert gefunden."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:898
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:941
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:736
|
||||
msgid "Number"
|
||||
msgstr "Nummer"
|
||||
@ -471,7 +475,7 @@ msgstr "Wählen Sie eine Zahl"
|
||||
msgid "Placeholder"
|
||||
msgstr "Platzhalter"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:924
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:967
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:762
|
||||
msgid "Radio"
|
||||
msgstr "Radio"
|
||||
@ -507,7 +511,7 @@ msgstr "Rot"
|
||||
msgid "Redirect URL"
|
||||
msgstr "Weiterleitungs-URL"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1027
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1069
|
||||
msgid "Remove"
|
||||
msgstr "Entfernen"
|
||||
|
||||
@ -578,7 +582,7 @@ msgstr "Erweiterte Einstellungen anzeigen"
|
||||
msgid "Sign"
|
||||
msgstr "Unterschreiben"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:742
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:785
|
||||
#: packages/ui/primitives/document-flow/add-signature.tsx:323
|
||||
#: packages/ui/primitives/document-flow/field-icon.tsx:52
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:580
|
||||
@ -626,7 +630,7 @@ msgstr "Einreichen"
|
||||
msgid "Template title"
|
||||
msgstr "Vorlagentitel"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:872
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:915
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:710
|
||||
msgid "Text"
|
||||
msgstr "Text"
|
||||
@ -687,7 +691,7 @@ msgstr "Der Name des Unterzeichners"
|
||||
msgid "This can be overriden by setting the authentication requirements directly on each recipient in the next step."
|
||||
msgstr "Dies kann überschrieben werden, indem die Authentifizierungsanforderungen im nächsten Schritt direkt für jeden Empfänger festgelegt werden."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:703
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:746
|
||||
msgid "This document has already been sent to this recipient. You can no longer edit this recipient."
|
||||
msgstr "Dieses Dokument wurde bereits an diesen Empfänger gesendet. Sie können diesen Empfänger nicht mehr bearbeiten."
|
||||
|
||||
@ -699,7 +703,7 @@ msgstr "Dieses Dokument ist durch ein Passwort geschützt. Bitte geben Sie das P
|
||||
msgid "This field cannot be modified or deleted. When you share this template's direct link or add it to your public profile, anyone who accesses it can input their name and email, and fill in the fields assigned to them."
|
||||
msgstr "Dieses Feld kann nicht geändert oder gelöscht werden. Wenn Sie den direkten Link dieser Vorlage teilen oder zu Ihrem öffentlichen Profil hinzufügen, kann jeder, der darauf zugreift, seinen Namen und seine E-Mail-Adresse eingeben und die ihm zugewiesenen Felder ausfüllen."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1007
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1050
|
||||
msgid "This recipient can no longer be modified as they have signed a field, or completed the document."
|
||||
msgstr ""
|
||||
|
||||
@ -724,7 +728,7 @@ msgstr "Zeitzone"
|
||||
msgid "Title"
|
||||
msgstr "Titel"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:990
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1033
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:828
|
||||
msgid "To proceed further, please set at least one value for the {0} field."
|
||||
msgstr "Um fortzufahren, legen Sie bitte mindestens einen Wert für das Feld {0} fest."
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1316,7 +1316,7 @@ msgstr "Dokument wird dauerhaft gelöscht"
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit/document-edit-page-view.tsx:109
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/loading.tsx:16
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/sent/page.tsx:15
|
||||
#: apps/web/src/app/(dashboard)/documents/documents-page-view.tsx:114
|
||||
#: apps/web/src/app/(dashboard)/documents/documents-page-view.tsx:119
|
||||
#: apps/web/src/app/(profile)/p/[url]/page.tsx:166
|
||||
#: apps/web/src/app/not-found.tsx:21
|
||||
#: apps/web/src/components/(dashboard)/common/command-menu.tsx:205
|
||||
|
||||
@ -110,7 +110,7 @@ msgstr "Admin"
|
||||
msgid "Advanced Options"
|
||||
msgstr "Advanced Options"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:527
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:565
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:402
|
||||
msgid "Advanced settings"
|
||||
msgstr "Advanced settings"
|
||||
@ -173,7 +173,7 @@ msgstr "CC'd"
|
||||
msgid "Character Limit"
|
||||
msgstr "Character Limit"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:950
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:993
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:788
|
||||
msgid "Checkbox"
|
||||
msgstr "Checkbox"
|
||||
@ -202,7 +202,7 @@ msgstr "Close"
|
||||
msgid "Configure Direct Recipient"
|
||||
msgstr "Configure Direct Recipient"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:528
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:566
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:403
|
||||
msgid "Configure the {0} field"
|
||||
msgstr "Configure the {0} field"
|
||||
@ -219,7 +219,7 @@ msgstr "Copied to clipboard"
|
||||
msgid "Custom Text"
|
||||
msgstr "Custom Text"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:846
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:889
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:684
|
||||
msgid "Date"
|
||||
msgstr "Date"
|
||||
@ -251,7 +251,7 @@ msgstr "Download"
|
||||
msgid "Drag & drop your PDF here."
|
||||
msgstr "Drag & drop your PDF here."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:976
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1019
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:814
|
||||
msgid "Dropdown"
|
||||
msgstr "Dropdown"
|
||||
@ -260,7 +260,7 @@ msgstr "Dropdown"
|
||||
msgid "Dropdown options"
|
||||
msgstr "Dropdown options"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:794
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:837
|
||||
#: packages/ui/primitives/document-flow/add-signature.tsx:272
|
||||
#: packages/ui/primitives/document-flow/add-signers.tsx:500
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:632
|
||||
@ -273,6 +273,10 @@ msgstr "Email"
|
||||
msgid "Email Options"
|
||||
msgstr "Email Options"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1082
|
||||
msgid "Empty field"
|
||||
msgstr "Empty field"
|
||||
|
||||
#: packages/lib/constants/template.ts:8
|
||||
msgid "Enable Direct Link Signing"
|
||||
msgstr "Enable Direct Link Signing"
|
||||
@ -379,7 +383,7 @@ msgstr "Message <0>(Optional)</0>"
|
||||
msgid "Min"
|
||||
msgstr "Min"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:820
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:863
|
||||
#: packages/ui/primitives/document-flow/add-signature.tsx:298
|
||||
#: packages/ui/primitives/document-flow/add-signers.tsx:535
|
||||
#: packages/ui/primitives/document-flow/add-signers.tsx:541
|
||||
@ -401,12 +405,12 @@ msgstr "Needs to sign"
|
||||
msgid "Needs to view"
|
||||
msgstr "Needs to view"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:631
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:674
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:497
|
||||
msgid "No recipient matching this description was found."
|
||||
msgstr "No recipient matching this description was found."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:647
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:690
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:513
|
||||
msgid "No recipients with this role"
|
||||
msgstr "No recipients with this role"
|
||||
@ -431,7 +435,7 @@ msgstr "No signature field found"
|
||||
msgid "No value found."
|
||||
msgstr "No value found."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:898
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:941
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:736
|
||||
msgid "Number"
|
||||
msgstr "Number"
|
||||
@ -466,7 +470,7 @@ msgstr "Pick a number"
|
||||
msgid "Placeholder"
|
||||
msgstr "Placeholder"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:924
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:967
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:762
|
||||
msgid "Radio"
|
||||
msgstr "Radio"
|
||||
@ -502,7 +506,7 @@ msgstr "Red"
|
||||
msgid "Redirect URL"
|
||||
msgstr "Redirect URL"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1027
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1069
|
||||
msgid "Remove"
|
||||
msgstr "Remove"
|
||||
|
||||
@ -573,7 +577,7 @@ msgstr "Show advanced settings"
|
||||
msgid "Sign"
|
||||
msgstr "Sign"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:742
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:785
|
||||
#: packages/ui/primitives/document-flow/add-signature.tsx:323
|
||||
#: packages/ui/primitives/document-flow/field-icon.tsx:52
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:580
|
||||
@ -621,7 +625,7 @@ msgstr "Submit"
|
||||
msgid "Template title"
|
||||
msgstr "Template title"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:872
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:915
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:710
|
||||
msgid "Text"
|
||||
msgstr "Text"
|
||||
@ -682,7 +686,7 @@ msgstr "The signer's name"
|
||||
msgid "This can be overriden by setting the authentication requirements directly on each recipient in the next step."
|
||||
msgstr "This can be overriden by setting the authentication requirements directly on each recipient in the next step."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:703
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:746
|
||||
msgid "This document has already been sent to this recipient. You can no longer edit this recipient."
|
||||
msgstr "This document has already been sent to this recipient. You can no longer edit this recipient."
|
||||
|
||||
@ -694,7 +698,7 @@ msgstr "This document is password protected. Please enter the password to view t
|
||||
msgid "This field cannot be modified or deleted. When you share this template's direct link or add it to your public profile, anyone who accesses it can input their name and email, and fill in the fields assigned to them."
|
||||
msgstr "This field cannot be modified or deleted. When you share this template's direct link or add it to your public profile, anyone who accesses it can input their name and email, and fill in the fields assigned to them."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1007
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1050
|
||||
msgid "This recipient can no longer be modified as they have signed a field, or completed the document."
|
||||
msgstr "This recipient can no longer be modified as they have signed a field, or completed the document."
|
||||
|
||||
@ -719,7 +723,7 @@ msgstr "Time Zone"
|
||||
msgid "Title"
|
||||
msgstr "Title"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:990
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1033
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:828
|
||||
msgid "To proceed further, please set at least one value for the {0} field."
|
||||
msgstr "To proceed further, please set at least one value for the {0} field."
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1311,7 +1311,7 @@ msgstr "Document will be permanently deleted"
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit/document-edit-page-view.tsx:109
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/loading.tsx:16
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/sent/page.tsx:15
|
||||
#: apps/web/src/app/(dashboard)/documents/documents-page-view.tsx:114
|
||||
#: apps/web/src/app/(dashboard)/documents/documents-page-view.tsx:119
|
||||
#: apps/web/src/app/(profile)/p/[url]/page.tsx:166
|
||||
#: apps/web/src/app/not-found.tsx:21
|
||||
#: apps/web/src/components/(dashboard)/common/command-menu.tsx:205
|
||||
|
||||
@ -115,7 +115,7 @@ msgstr "Administrateur"
|
||||
msgid "Advanced Options"
|
||||
msgstr "Options avancées"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:527
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:565
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:402
|
||||
msgid "Advanced settings"
|
||||
msgstr "Paramètres avancés"
|
||||
@ -174,7 +174,7 @@ msgstr "CC'd"
|
||||
msgid "Character Limit"
|
||||
msgstr "Limite de caractères"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:950
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:993
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:788
|
||||
msgid "Checkbox"
|
||||
msgstr "Case à cocher"
|
||||
@ -203,7 +203,7 @@ msgstr "Fermer"
|
||||
msgid "Configure Direct Recipient"
|
||||
msgstr "Configurer le destinataire direct"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:528
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:566
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:403
|
||||
msgid "Configure the {0} field"
|
||||
msgstr "Configurer le champ {0}"
|
||||
@ -220,7 +220,7 @@ msgstr "Copié dans le presse-papiers"
|
||||
msgid "Custom Text"
|
||||
msgstr "Texte personnalisé"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:846
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:889
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:684
|
||||
msgid "Date"
|
||||
msgstr "Date"
|
||||
@ -252,7 +252,7 @@ msgstr "Télécharger"
|
||||
msgid "Drag & drop your PDF here."
|
||||
msgstr "Faites glisser et déposez votre PDF ici."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:976
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1019
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:814
|
||||
msgid "Dropdown"
|
||||
msgstr "Liste déroulante"
|
||||
@ -261,7 +261,7 @@ msgstr "Liste déroulante"
|
||||
msgid "Dropdown options"
|
||||
msgstr "Options de liste déroulante"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:794
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:837
|
||||
#: packages/ui/primitives/document-flow/add-signature.tsx:272
|
||||
#: packages/ui/primitives/document-flow/add-signers.tsx:500
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:632
|
||||
@ -274,6 +274,10 @@ msgstr "Email"
|
||||
msgid "Email Options"
|
||||
msgstr "Options d'email"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1082
|
||||
msgid "Empty field"
|
||||
msgstr ""
|
||||
|
||||
#: packages/lib/constants/template.ts:8
|
||||
msgid "Enable Direct Link Signing"
|
||||
msgstr "Activer la signature de lien direct"
|
||||
@ -380,7 +384,7 @@ msgstr "Message <0>(Optionnel)</0>"
|
||||
msgid "Min"
|
||||
msgstr "Min"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:820
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:863
|
||||
#: packages/ui/primitives/document-flow/add-signature.tsx:298
|
||||
#: packages/ui/primitives/document-flow/add-signers.tsx:535
|
||||
#: packages/ui/primitives/document-flow/add-signers.tsx:541
|
||||
@ -402,12 +406,12 @@ msgstr "Nécessite une signature"
|
||||
msgid "Needs to view"
|
||||
msgstr "Nécessite une visualisation"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:631
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:674
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:497
|
||||
msgid "No recipient matching this description was found."
|
||||
msgstr "Aucun destinataire correspondant à cette description n'a été trouvé."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:647
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:690
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:513
|
||||
msgid "No recipients with this role"
|
||||
msgstr "Aucun destinataire avec ce rôle"
|
||||
@ -432,7 +436,7 @@ msgstr "Aucun champ de signature trouvé"
|
||||
msgid "No value found."
|
||||
msgstr "Aucune valeur trouvée."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:898
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:941
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:736
|
||||
msgid "Number"
|
||||
msgstr "Numéro"
|
||||
@ -467,7 +471,7 @@ msgstr "Choisissez un numéro"
|
||||
msgid "Placeholder"
|
||||
msgstr "Espace réservé"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:924
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:967
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:762
|
||||
msgid "Radio"
|
||||
msgstr "Radio"
|
||||
@ -503,7 +507,7 @@ msgstr "Rouge"
|
||||
msgid "Redirect URL"
|
||||
msgstr "URL de redirection"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1027
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1069
|
||||
msgid "Remove"
|
||||
msgstr "Retirer"
|
||||
|
||||
@ -574,7 +578,7 @@ msgstr "Afficher les paramètres avancés"
|
||||
msgid "Sign"
|
||||
msgstr "Signer"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:742
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:785
|
||||
#: packages/ui/primitives/document-flow/add-signature.tsx:323
|
||||
#: packages/ui/primitives/document-flow/field-icon.tsx:52
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:580
|
||||
@ -622,7 +626,7 @@ msgstr "Soumettre"
|
||||
msgid "Template title"
|
||||
msgstr "Titre du modèle"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:872
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:915
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:710
|
||||
msgid "Text"
|
||||
msgstr "Texte"
|
||||
@ -683,7 +687,7 @@ msgstr "Le nom du signataire"
|
||||
msgid "This can be overriden by setting the authentication requirements directly on each recipient in the next step."
|
||||
msgstr "Cela peut être remplacé par le paramétrage direct des exigences d'authentification pour chaque destinataire à l'étape suivante."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:703
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:746
|
||||
msgid "This document has already been sent to this recipient. You can no longer edit this recipient."
|
||||
msgstr "Ce document a déjà été envoyé à ce destinataire. Vous ne pouvez plus modifier ce destinataire."
|
||||
|
||||
@ -695,7 +699,7 @@ msgstr "Ce document est protégé par mot de passe. Veuillez entrer le mot de pa
|
||||
msgid "This field cannot be modified or deleted. When you share this template's direct link or add it to your public profile, anyone who accesses it can input their name and email, and fill in the fields assigned to them."
|
||||
msgstr "Ce champ ne peut pas être modifié ou supprimé. Lorsque vous partagez le lien direct de ce modèle ou l'ajoutez à votre profil public, toute personne qui y accède peut saisir son nom et son email, et remplir les champs qui lui sont attribués."
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1007
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1050
|
||||
msgid "This recipient can no longer be modified as they have signed a field, or completed the document."
|
||||
msgstr ""
|
||||
|
||||
@ -720,7 +724,7 @@ msgstr "Fuseau horaire"
|
||||
msgid "Title"
|
||||
msgstr "Titre"
|
||||
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:990
|
||||
#: packages/ui/primitives/document-flow/add-fields.tsx:1033
|
||||
#: packages/ui/primitives/template-flow/add-template-fields.tsx:828
|
||||
msgid "To proceed further, please set at least one value for the {0} field."
|
||||
msgstr "Pour continuer, veuillez définir au moins une valeur pour le champ {0}."
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1316,7 +1316,7 @@ msgstr "Le document sera supprimé de manière permanente"
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/edit/document-edit-page-view.tsx:109
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/loading.tsx:16
|
||||
#: apps/web/src/app/(dashboard)/documents/[id]/sent/page.tsx:15
|
||||
#: apps/web/src/app/(dashboard)/documents/documents-page-view.tsx:114
|
||||
#: apps/web/src/app/(dashboard)/documents/documents-page-view.tsx:119
|
||||
#: apps/web/src/app/(profile)/p/[url]/page.tsx:166
|
||||
#: apps/web/src/app/not-found.tsx:21
|
||||
#: apps/web/src/components/(dashboard)/common/command-menu.tsx:205
|
||||
|
||||
@ -39,3 +39,27 @@ export const validateFieldsInserted = (fields: Field[]): boolean => {
|
||||
|
||||
return uninsertedFields.length === 0;
|
||||
};
|
||||
|
||||
export const validateFieldsUninserted = (): boolean => {
|
||||
const fieldCardElements = document.getElementsByClassName('react-draggable');
|
||||
|
||||
const errorElements: HTMLElement[] = [];
|
||||
|
||||
Array.from(fieldCardElements).forEach((element) => {
|
||||
const innerDiv = element.querySelector('div');
|
||||
const hasError = innerDiv?.getAttribute('data-error') === 'true';
|
||||
|
||||
if (hasError) {
|
||||
errorElements.push(element as HTMLElement);
|
||||
} else {
|
||||
element.removeAttribute('data-error');
|
||||
}
|
||||
});
|
||||
|
||||
if (errorElements.length > 0) {
|
||||
errorElements[0].scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
return false;
|
||||
}
|
||||
|
||||
return errorElements.length === 0;
|
||||
};
|
||||
|
||||
@ -105,13 +105,15 @@ export const unseedTeam = async (teamUrl: string) => {
|
||||
type SeedTeamMemberOptions = {
|
||||
teamId: number;
|
||||
role?: TeamMemberRole;
|
||||
name?: string;
|
||||
};
|
||||
|
||||
export const seedTeamMember = async ({
|
||||
teamId,
|
||||
name,
|
||||
role = TeamMemberRole.ADMIN,
|
||||
}: SeedTeamMemberOptions) => {
|
||||
const user = await seedUser();
|
||||
const user = await seedUser({ name });
|
||||
|
||||
await prisma.teamMember.create({
|
||||
data: {
|
||||
|
||||
@ -21,8 +21,13 @@ export const seedUser = async ({
|
||||
password = 'password',
|
||||
verified = true,
|
||||
}: SeedUserOptions = {}) => {
|
||||
if (!name) {
|
||||
let url = name;
|
||||
|
||||
if (name) {
|
||||
url = nanoid();
|
||||
} else {
|
||||
name = nanoid();
|
||||
url = name;
|
||||
}
|
||||
|
||||
if (!email) {
|
||||
@ -35,7 +40,7 @@ export const seedUser = async ({
|
||||
email,
|
||||
password: hashSync(password),
|
||||
emailVerified: verified ? new Date() : undefined,
|
||||
url: name,
|
||||
url,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@ -101,7 +101,7 @@ export const templateRouter = router({
|
||||
.input(ZCreateDocumentFromTemplateMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
const { templateId, teamId } = input;
|
||||
const { templateId, teamId, recipients } = input;
|
||||
|
||||
const limits = await getServerLimits({ email: ctx.user.email, teamId });
|
||||
|
||||
@ -115,7 +115,7 @@ export const templateRouter = router({
|
||||
templateId,
|
||||
teamId,
|
||||
userId: ctx.user.id,
|
||||
recipients: input.recipients,
|
||||
recipients,
|
||||
requestMetadata,
|
||||
});
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@ export function FieldToolTip({ children, color, className = '', field }: FieldTo
|
||||
}}
|
||||
>
|
||||
<TooltipProvider>
|
||||
<Tooltip delayDuration={0} open={!field.inserted}>
|
||||
<Tooltip delayDuration={0} open={!field.inserted || !field.fieldMeta}>
|
||||
<TooltipTrigger className="absolute inset-0 w-full"></TooltipTrigger>
|
||||
|
||||
<TooltipContent className={tooltipVariants({ color, className })} sideOffset={2}>
|
||||
|
||||
@ -5,6 +5,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Caveat } from 'next/font/google';
|
||||
|
||||
import { Trans, msg } from '@lingui/macro';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import {
|
||||
CalendarDays,
|
||||
Check,
|
||||
@ -32,6 +33,7 @@ import {
|
||||
ZFieldMetaSchema,
|
||||
} from '@documenso/lib/types/field-meta';
|
||||
import { nanoid } from '@documenso/lib/universal/id';
|
||||
import { validateFieldsUninserted } from '@documenso/lib/utils/fields';
|
||||
import {
|
||||
canRecipientBeModified,
|
||||
canRecipientFieldsBeModified,
|
||||
@ -39,6 +41,7 @@ import {
|
||||
import type { Field, Recipient } from '@documenso/prisma/client';
|
||||
import { FieldType, RecipientRole, SendStatus } from '@documenso/prisma/client';
|
||||
|
||||
import { FieldToolTip } from '../../components/field/field-tooltip';
|
||||
import { getSignerColorStyles, useSignerColors } from '../../lib/signer-colors';
|
||||
import { cn } from '../../lib/utils';
|
||||
import { Alert, AlertDescription } from '../alert';
|
||||
@ -96,12 +99,6 @@ export type AddFieldsFormProps = {
|
||||
teamId?: number;
|
||||
};
|
||||
|
||||
/*
|
||||
I hate this, but due to TailwindCSS JIT, I couldnn't find a better way to do this for now.
|
||||
|
||||
TODO: Try to find a better way to do this.
|
||||
*/
|
||||
|
||||
export const AddFieldsFormPartial = ({
|
||||
documentFlow,
|
||||
hideRecipients = false,
|
||||
@ -195,6 +192,7 @@ export const AddFieldsFormPartial = ({
|
||||
const selectedSignerStyles = useSignerColors(
|
||||
selectedSignerIndex === -1 ? 0 : selectedSignerIndex,
|
||||
);
|
||||
const [validateUninsertedFields, setValidateUninsertedFields] = useState(false);
|
||||
|
||||
const filterFieldsWithEmptyValues = (fields: typeof localFields, fieldType: string) =>
|
||||
fields
|
||||
@ -228,6 +226,38 @@ export const AddFieldsFormPartial = ({
|
||||
const hasErrors =
|
||||
emptyCheckboxFields.length > 0 || emptyRadioFields.length > 0 || emptySelectFields.length > 0;
|
||||
|
||||
const fieldsWithError = useMemo(() => {
|
||||
const fields = localFields.filter((field) => {
|
||||
const hasError =
|
||||
((field.type === FieldType.CHECKBOX ||
|
||||
field.type === FieldType.RADIO ||
|
||||
field.type === FieldType.DROPDOWN) &&
|
||||
field.fieldMeta === undefined) ||
|
||||
(field.fieldMeta && 'values' in field.fieldMeta && field?.fieldMeta?.values?.length === 0);
|
||||
|
||||
return hasError;
|
||||
});
|
||||
|
||||
const mappedFields = fields.map((field) => ({
|
||||
id: field.nativeId ?? 0,
|
||||
secondaryId: field.formId,
|
||||
documentId: null,
|
||||
templateId: null,
|
||||
recipientId: 0,
|
||||
type: field.type,
|
||||
page: field.pageNumber,
|
||||
positionX: new Prisma.Decimal(field.pageX),
|
||||
positionY: new Prisma.Decimal(field.pageY),
|
||||
width: new Prisma.Decimal(field.pageWidth),
|
||||
height: new Prisma.Decimal(field.pageHeight),
|
||||
customText: '',
|
||||
inserted: true,
|
||||
fieldMeta: field.fieldMeta ?? null,
|
||||
}));
|
||||
|
||||
return mappedFields;
|
||||
}, [localFields]);
|
||||
|
||||
const isFieldsDisabled = useMemo(() => {
|
||||
if (!selectedSigner) {
|
||||
return true;
|
||||
@ -515,6 +545,14 @@ export const AddFieldsFormPartial = ({
|
||||
|
||||
if (!everySignerHasSignature) {
|
||||
setIsMissingSignatureDialogVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
setValidateUninsertedFields(true);
|
||||
const isFieldsValid = validateFieldsUninserted();
|
||||
|
||||
if (!isFieldsValid) {
|
||||
return;
|
||||
} else {
|
||||
void onFormSubmit();
|
||||
}
|
||||
@ -566,6 +604,10 @@ export const AddFieldsFormPartial = ({
|
||||
{isDocumentPdfLoaded &&
|
||||
localFields.map((field, index) => {
|
||||
const recipientIndex = recipients.findIndex((r) => r.email === field.signerEmail);
|
||||
const hasFieldError =
|
||||
emptyCheckboxFields.find((f) => f.formId === field.formId) ||
|
||||
emptyRadioFields.find((f) => f.formId === field.formId) ||
|
||||
emptySelectFields.find((f) => f.formId === field.formId);
|
||||
|
||||
return (
|
||||
<FieldItem
|
||||
@ -590,6 +632,7 @@ export const AddFieldsFormPartial = ({
|
||||
handleAdvancedSettings();
|
||||
}}
|
||||
hideRecipients={hideRecipients}
|
||||
hasErrors={!!hasFieldError}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@ -1018,7 +1061,6 @@ export const AddFieldsFormPartial = ({
|
||||
<DocumentFlowFormContainerActions
|
||||
loading={isSubmitting}
|
||||
disabled={isSubmitting}
|
||||
disableNextStep={hasErrors}
|
||||
onGoBackClick={() => {
|
||||
previousStep();
|
||||
remove();
|
||||
@ -1035,6 +1077,11 @@ export const AddFieldsFormPartial = ({
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{validateUninsertedFields && fieldsWithError[0] && (
|
||||
<FieldToolTip key={fieldsWithError[0].id} field={fieldsWithError[0]} color="warning">
|
||||
<Trans>Empty field</Trans>
|
||||
</FieldToolTip>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -44,6 +44,7 @@ export type FieldItemProps = {
|
||||
onBlur?: () => void;
|
||||
recipientIndex?: number;
|
||||
hideRecipients?: boolean;
|
||||
hasErrors?: boolean;
|
||||
};
|
||||
|
||||
export const FieldItem = ({
|
||||
@ -61,6 +62,7 @@ export const FieldItem = ({
|
||||
onAdvancedSettings,
|
||||
recipientIndex = 0,
|
||||
hideRecipients = false,
|
||||
hasErrors,
|
||||
}: FieldItemProps) => {
|
||||
const [active, setActive] = useState(false);
|
||||
const [coords, setCoords] = useState({
|
||||
@ -201,10 +203,15 @@ export const FieldItem = ({
|
||||
<div
|
||||
className={cn(
|
||||
'relative flex h-full w-full items-center justify-center bg-white',
|
||||
!hasErrors && signerStyles.default.base,
|
||||
!hasErrors && signerStyles.default.fieldItem,
|
||||
{
|
||||
'rounded-lg border border-red-400 bg-red-400/20 shadow-[0_0_0_5px_theme(colors.red.500/10%),0_0_0_2px_theme(colors.red.500/40%),0_0_0_0.5px_theme(colors.red.500)]':
|
||||
hasErrors,
|
||||
},
|
||||
!fixedSize && '[container-type:size]',
|
||||
signerStyles.default.base,
|
||||
signerStyles.default.fieldItem,
|
||||
)}
|
||||
data-error={hasErrors ? 'true' : undefined}
|
||||
onClick={() => {
|
||||
setSettingsActive((prev) => !prev);
|
||||
onFocus?.();
|
||||
|
||||
Reference in New Issue
Block a user