Compare commits

..

1 Commits

Author SHA1 Message Date
d4e259a9a7 refactor: improve layout of completed signing page 2025-11-18 10:16:13 +02:00
9 changed files with 184 additions and 221 deletions

View File

@ -202,8 +202,12 @@ export default function CompletedSigningPage({ loaderData }: Route.ComponentProp
</p> </p>
))} ))}
<div className="mt-8 flex w-full max-w-sm items-center justify-center gap-4"> <div className="mt-8 flex w-full max-w-xs flex-col items-stretch gap-4 md:w-auto md:max-w-none md:flex-row md:items-center">
<DocumentShareButton documentId={document.id} token={recipient.token} /> <DocumentShareButton
documentId={document.id}
token={recipient.token}
className="w-full max-w-none md:flex-1"
/>
{isDocumentCompleted(document.status) && ( {isDocumentCompleted(document.status) && (
<EnvelopeDownloadDialog <EnvelopeDownloadDialog
@ -212,13 +216,21 @@ export default function CompletedSigningPage({ loaderData }: Route.ComponentProp
envelopeItems={document.envelopeItems} envelopeItems={document.envelopeItems}
token={recipient?.token} token={recipient?.token}
trigger={ trigger={
<Button type="button" variant="outline" className="flex-1"> <Button type="button" variant="outline" className="flex-1 md:flex-initial">
<DownloadIcon className="mr-2 h-5 w-5" /> <DownloadIcon className="mr-2 h-5 w-5" />
<Trans>Download</Trans> <Trans>Download</Trans>
</Button> </Button>
} }
/> />
)} )}
{user && (
<Button asChild>
<Link to="/">
<Trans>Go Back Home</Trans>
</Link>
</Button>
)}
</div> </div>
</div> </div>
@ -238,12 +250,6 @@ export default function CompletedSigningPage({ loaderData }: Route.ComponentProp
<ClaimAccount defaultName={recipientName} defaultEmail={recipient.email} /> <ClaimAccount defaultName={recipientName} defaultEmail={recipient.email} />
</div> </div>
)} )}
{user && (
<Link to="/" className="text-documenso-700 hover:text-documenso-600 mt-2">
<Trans>Go Back Home</Trans>
</Link>
)}
</div> </div>
</div> </div>

View File

@ -106,5 +106,5 @@
"vite-plugin-babel-macros": "^1.0.6", "vite-plugin-babel-macros": "^1.0.6",
"vite-tsconfig-paths": "^5.1.4" "vite-tsconfig-paths": "^5.1.4"
}, },
"version": "2.0.13" "version": "2.0.12"
} }

6
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "@documenso/root", "name": "@documenso/root",
"version": "2.0.13", "version": "2.0.12",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@documenso/root", "name": "@documenso/root",
"version": "2.0.13", "version": "2.0.12",
"workspaces": [ "workspaces": [
"apps/*", "apps/*",
"packages/*" "packages/*"
@ -101,7 +101,7 @@
}, },
"apps/remix": { "apps/remix": {
"name": "@documenso/remix", "name": "@documenso/remix",
"version": "2.0.13", "version": "2.0.12",
"dependencies": { "dependencies": {
"@cantoo/pdf-lib": "^2.5.2", "@cantoo/pdf-lib": "^2.5.2",
"@documenso/api": "*", "@documenso/api": "*",

View File

@ -1,6 +1,6 @@
{ {
"private": true, "private": true,
"version": "2.0.13", "version": "2.0.12",
"scripts": { "scripts": {
"build": "turbo run build", "build": "turbo run build",
"dev": "turbo run dev --filter=@documenso/remix", "dev": "turbo run dev --filter=@documenso/remix",

View File

@ -116,28 +116,28 @@ async function getTeamInsights(
): Promise<OrganisationDetailedInsights> { ): Promise<OrganisationDetailedInsights> {
const teamsQuery = kyselyPrisma.$kysely const teamsQuery = kyselyPrisma.$kysely
.selectFrom('Team as t') .selectFrom('Team as t')
.leftJoin('Envelope as e', (join) =>
join
.onRef('t.id', '=', 'e.teamId')
.on('e.deletedAt', 'is', null)
.on('e.type', '=', sql.lit(EnvelopeType.DOCUMENT)),
)
.leftJoin('TeamGroup as tg', 'tg.teamId', 't.id')
.leftJoin('OrganisationGroup as og', 'og.id', 'tg.organisationGroupId')
.leftJoin('OrganisationGroupMember as ogm', 'ogm.groupId', 'og.id')
.leftJoin('OrganisationMember as om', 'om.id', 'ogm.organisationMemberId')
.where('t.organisationId', '=', organisationId) .where('t.organisationId', '=', organisationId)
.select((eb) => [ .select([
't.id', 't.id as id',
't.name', 't.name as name',
't.createdAt', 't.createdAt as createdAt',
eb sql<number>`COUNT(DISTINCT om."userId")`.as('memberCount'),
.selectFrom('TeamGroup as tg') (createdAtFrom
.innerJoin('OrganisationGroup as og', 'og.id', 'tg.organisationGroupId') ? sql<number>`COUNT(DISTINCT CASE WHEN e.id IS NOT NULL AND e."createdAt" >= ${createdAtFrom} THEN e.id END)`
.innerJoin('OrganisationGroupMember as ogm', 'ogm.groupId', 'og.id') : sql<number>`COUNT(DISTINCT e.id)`
.innerJoin('OrganisationMember as om', 'om.id', 'ogm.organisationMemberId') ).as('documentCount'),
.whereRef('tg.teamId', '=', 't.id')
.select(sql<number>`count(distinct om."userId")`.as('count'))
.as('memberCount'),
eb
.selectFrom('Envelope as e')
.whereRef('e.teamId', '=', 't.id')
.where('e.deletedAt', 'is', null)
.where('e.type', '=', sql.lit(EnvelopeType.DOCUMENT))
.$if(!!createdAtFrom, (qb) => qb.where('e.createdAt', '>=', createdAtFrom!))
.select(sql<number>`count(e.id)`.as('count'))
.as('documentCount'),
]) ])
.groupBy(['t.id', 't.name', 't.createdAt'])
.orderBy('documentCount', 'desc') .orderBy('documentCount', 'desc')
.limit(perPage) .limit(perPage)
.offset(offset); .offset(offset);
@ -164,38 +164,48 @@ async function getUserInsights(
perPage: number, perPage: number,
createdAtFrom: Date | null, createdAtFrom: Date | null,
): Promise<OrganisationDetailedInsights> { ): Promise<OrganisationDetailedInsights> {
const usersQuery = kyselyPrisma.$kysely const usersBase = kyselyPrisma.$kysely
.selectFrom('OrganisationMember as om') .selectFrom('OrganisationMember as om')
.innerJoin('User as u', 'u.id', 'om.userId') .innerJoin('User as u', 'u.id', 'om.userId')
.where('om.organisationId', '=', organisationId) .where('om.organisationId', '=', organisationId)
.select((eb) => [ .leftJoin('Envelope as e', (join) =>
'u.id', join
'u.name', .onRef('e.userId', '=', 'u.id')
'u.email', .on('e.deletedAt', 'is', null)
'u.createdAt', .on('e.type', '=', sql.lit(EnvelopeType.DOCUMENT)),
eb )
.selectFrom('Envelope as e') .leftJoin('Team as td', (join) =>
.innerJoin('Team as t', 't.id', 'e.teamId') join.onRef('td.id', '=', 'e.teamId').on('td.organisationId', '=', organisationId),
.whereRef('e.userId', '=', 'u.id') )
.where('t.organisationId', '=', organisationId) .leftJoin('Recipient as r', (join) =>
.where('e.deletedAt', 'is', null) join.onRef('r.email', '=', 'u.email').on('r.signedAt', 'is not', null),
.where('e.type', '=', sql.lit(EnvelopeType.DOCUMENT)) )
.$if(!!createdAtFrom, (qb) => qb.where('e.createdAt', '>=', createdAtFrom!)) .leftJoin('Envelope as se', (join) =>
.select(sql<number>`count(e.id)`.as('count')) join
.as('documentCount'), .onRef('se.id', '=', 'r.envelopeId')
eb .on('se.deletedAt', 'is', null)
.selectFrom('Recipient as r') .on('se.type', '=', sql.lit(EnvelopeType.DOCUMENT)),
.innerJoin('Envelope as e', 'e.id', 'r.envelopeId') )
.innerJoin('Team as t', 't.id', 'e.teamId') .leftJoin('Team as ts', (join) =>
.whereRef('r.email', '=', 'u.email') join.onRef('ts.id', '=', 'se.teamId').on('ts.organisationId', '=', organisationId),
.where('r.signedAt', 'is not', null) );
.where('t.organisationId', '=', organisationId)
.where('e.deletedAt', 'is', null) const usersQuery = usersBase
.where('e.type', '=', sql.lit(EnvelopeType.DOCUMENT)) .select([
.$if(!!createdAtFrom, (qb) => qb.where('e.createdAt', '>=', createdAtFrom!)) 'u.id as id',
.select(sql<number>`count(e.id)`.as('count')) 'u.name as name',
.as('signedDocumentCount'), 'u.email as email',
'u.createdAt as createdAt',
(createdAtFrom
? sql<number>`COUNT(DISTINCT CASE WHEN e.id IS NOT NULL AND td.id IS NOT NULL AND e."createdAt" >= ${createdAtFrom} THEN e.id END)`
: sql<number>`COUNT(DISTINCT CASE WHEN td.id IS NOT NULL THEN e.id END)`
).as('documentCount'),
(createdAtFrom
? sql<number>`COUNT(DISTINCT CASE WHEN e.id IS NOT NULL AND td.id IS NOT NULL AND e.status = 'COMPLETED' AND e."createdAt" >= ${createdAtFrom} THEN e.id END)`
: sql<number>`COUNT(DISTINCT CASE WHEN e.id IS NOT NULL AND td.id IS NOT NULL AND e.status = 'COMPLETED' THEN e.id END)`
).as('signedDocumentCount'),
]) ])
.groupBy(['u.id', 'u.name', 'u.email', 'u.createdAt'])
.orderBy('u.createdAt', 'desc') .orderBy('u.createdAt', 'desc')
.limit(perPage) .limit(perPage)
.offset(offset); .offset(offset);
@ -282,51 +292,72 @@ async function getOrganisationSummary(
organisationId: string, organisationId: string,
createdAtFrom: Date | null, createdAtFrom: Date | null,
): Promise<OrganisationSummary> { ): Promise<OrganisationSummary> {
const teamCountQuery = kyselyPrisma.$kysely const summaryQuery = kyselyPrisma.$kysely
.selectFrom('Team') .selectFrom('Organisation as o')
.where('organisationId', '=', organisationId) .where('o.id', '=', organisationId)
.select(sql<number>`count(id)`.as('count'))
.executeTakeFirst();
const memberCountQuery = kyselyPrisma.$kysely
.selectFrom('OrganisationMember')
.where('organisationId', '=', organisationId)
.select(sql<number>`count(id)`.as('count'))
.executeTakeFirst();
const envelopeStatsQuery = kyselyPrisma.$kysely
.selectFrom('Envelope as e')
.innerJoin('Team as t', 't.id', 'e.teamId')
.where('t.organisationId', '=', organisationId)
.where('e.deletedAt', 'is', null)
.where('e.type', '=', sql.lit(EnvelopeType.DOCUMENT))
.select([ .select([
sql<number>`count(e.id)`.as('totalDocuments'), sql<number>`(SELECT COUNT(DISTINCT t2.id) FROM "Team" AS t2 WHERE t2."organisationId" = o.id)`.as(
sql<number>`count(case when e.status in ('DRAFT', 'PENDING') then 1 end)`.as( 'totalTeams',
'activeDocuments',
), ),
sql<number>`count(case when e.status = 'COMPLETED' then 1 end)`.as('completedDocuments'), sql<number>`(SELECT COUNT(DISTINCT om2."userId") FROM "OrganisationMember" AS om2 WHERE om2."organisationId" = o.id)`.as(
sql<number>`count(case when e.status = 'COMPLETED' then 1 end)`.as('volumeAllTime'), 'totalMembers',
),
sql<number>`(
SELECT COUNT(DISTINCT e2.id)
FROM "Envelope" AS e2
INNER JOIN "Team" AS t2 ON t2.id = e2."teamId"
WHERE t2."organisationId" = o.id AND e2."deletedAt" IS NULL AND e2.type = 'DOCUMENT'
)`.as('totalDocuments'),
sql<number>`(
SELECT COUNT(DISTINCT e2.id)
FROM "Envelope" AS e2
INNER JOIN "Team" AS t2 ON t2.id = e2."teamId"
WHERE t2."organisationId" = o.id AND e2."deletedAt" IS NULL AND e2.type = 'DOCUMENT' AND e2.status IN ('DRAFT', 'PENDING')
)`.as('activeDocuments'),
sql<number>`(
SELECT COUNT(DISTINCT e2.id)
FROM "Envelope" AS e2
INNER JOIN "Team" AS t2 ON t2.id = e2."teamId"
WHERE t2."organisationId" = o.id AND e2."deletedAt" IS NULL AND e2.type = 'DOCUMENT' AND e2.status = 'COMPLETED'
)`.as('completedDocuments'),
(createdAtFrom (createdAtFrom
? sql<number>`count(case when e.status = 'COMPLETED' and e."createdAt" >= ${createdAtFrom} then 1 end)` ? sql<number>`(
: sql<number>`count(case when e.status = 'COMPLETED' then 1 end)` SELECT COUNT(DISTINCT e2.id)
FROM "Envelope" AS e2
INNER JOIN "Team" AS t2 ON t2.id = e2."teamId"
WHERE t2."organisationId" = o.id
AND e2."deletedAt" IS NULL
AND e2.type = 'DOCUMENT'
AND e2.status = 'COMPLETED'
AND e2."createdAt" >= ${createdAtFrom}
)`
: sql<number>`(
SELECT COUNT(DISTINCT e2.id)
FROM "Envelope" AS e2
INNER JOIN "Team" AS t2 ON t2.id = e2."teamId"
WHERE t2."organisationId" = o.id
AND e2."deletedAt" IS NULL
AND e2.type = 'DOCUMENT'
AND e2.status = 'COMPLETED'
)`
).as('volumeThisPeriod'), ).as('volumeThisPeriod'),
]) sql<number>`(
.executeTakeFirst(); SELECT COUNT(DISTINCT e2.id)
FROM "Envelope" AS e2
INNER JOIN "Team" AS t2 ON t2.id = e2."teamId"
WHERE t2."organisationId" = o.id AND e2."deletedAt" IS NULL AND e2.type = 'DOCUMENT' AND e2.status = 'COMPLETED'
)`.as('volumeAllTime'),
]);
const [teamCount, memberCount, envelopeStats] = await Promise.all([ const result = await summaryQuery.executeTakeFirst();
teamCountQuery,
memberCountQuery,
envelopeStatsQuery,
]);
return { return {
totalTeams: Number(teamCount?.count || 0), totalTeams: Number(result?.totalTeams || 0),
totalMembers: Number(memberCount?.count || 0), totalMembers: Number(result?.totalMembers || 0),
totalDocuments: Number(envelopeStats?.totalDocuments || 0), totalDocuments: Number(result?.totalDocuments || 0),
activeDocuments: Number(envelopeStats?.activeDocuments || 0), activeDocuments: Number(result?.activeDocuments || 0),
completedDocuments: Number(envelopeStats?.completedDocuments || 0), completedDocuments: Number(result?.completedDocuments || 0),
volumeThisPeriod: Number(envelopeStats?.volumeThisPeriod || 0), volumeThisPeriod: Number(result?.volumeThisPeriod || 0),
volumeAllTime: Number(envelopeStats?.volumeAllTime || 0), volumeAllTime: Number(result?.volumeAllTime || 0),
}; };
} }

View File

@ -33,32 +33,25 @@ export async function getSigningVolume({
let findQuery = kyselyPrisma.$kysely let findQuery = kyselyPrisma.$kysely
.selectFrom('Organisation as o') .selectFrom('Organisation as o')
.where((eb) => .leftJoin('Team as t', 'o.id', 't.organisationId')
eb.or([ .leftJoin('Envelope as e', (join) =>
eb('o.name', 'ilike', `%${search}%`), join
eb.exists( .onRef('t.id', '=', 'e.teamId')
eb .on('e.status', '=', sql.lit(DocumentStatus.COMPLETED))
.selectFrom('Team as t') .on('e.deletedAt', 'is', null)
.whereRef('t.organisationId', '=', 'o.id') .on('e.type', '=', sql.lit(EnvelopeType.DOCUMENT)),
.where('t.name', 'ilike', `%${search}%`),
),
]),
) )
.select((eb) => [ .where((eb) =>
eb.or([eb('o.name', 'ilike', `%${search}%`), eb('t.name', 'ilike', `%${search}%`)]),
)
.select([
'o.id as id', 'o.id as id',
'o.createdAt as createdAt', 'o.createdAt as createdAt',
'o.customerId as customerId', 'o.customerId as customerId',
sql<string>`COALESCE(o.name, 'Unknown')`.as('name'), sql<string>`COALESCE(o.name, 'Unknown')`.as('name'),
eb sql<number>`COUNT(DISTINCT e.id)`.as('signingVolume'),
.selectFrom('Envelope as e') ])
.innerJoin('Team as t', 't.id', 'e.teamId') .groupBy(['o.id', 'o.name', 'o.customerId']);
.whereRef('t.organisationId', '=', 'o.id')
.where('e.status', '=', sql.lit(DocumentStatus.COMPLETED))
.where('e.deletedAt', 'is', null)
.where('e.type', '=', sql.lit(EnvelopeType.DOCUMENT))
.select(sql<number>`count(e.id)`.as('count'))
.as('signingVolume'),
]);
switch (sortBy) { switch (sortBy) {
case 'name': case 'name':
@ -78,18 +71,11 @@ export async function getSigningVolume({
const countQuery = kyselyPrisma.$kysely const countQuery = kyselyPrisma.$kysely
.selectFrom('Organisation as o') .selectFrom('Organisation as o')
.leftJoin('Team as t', 'o.id', 't.organisationId')
.where((eb) => .where((eb) =>
eb.or([ eb.or([eb('o.name', 'ilike', `%${search}%`), eb('t.name', 'ilike', `%${search}%`)]),
eb('o.name', 'ilike', `%${search}%`),
eb.exists(
eb
.selectFrom('Team as t')
.whereRef('t.organisationId', '=', 'o.id')
.where('t.name', 'ilike', `%${search}%`),
),
]),
) )
.select(({ fn }) => [fn.countAll().as('count')]); .select(() => [sql<number>`COUNT(DISTINCT o.id)`.as('count')]);
const [results, [{ count }]] = await Promise.all([findQuery.execute(), countQuery.execute()]); const [results, [{ count }]] = await Promise.all([findQuery.execute(), countQuery.execute()]);
@ -118,77 +104,64 @@ export async function getOrganisationInsights({
const offset = Math.max(page - 1, 0) * perPage; const offset = Math.max(page - 1, 0) * perPage;
const now = new Date(); const now = new Date();
let dateCondition = sql<boolean>`1=1`; let dateCondition = sql`1=1`;
if (startDate && endDate) { if (startDate && endDate) {
dateCondition = sql<boolean>`e."createdAt" >= ${startDate} AND e."createdAt" <= ${endDate}`; dateCondition = sql`e."createdAt" >= ${startDate} AND e."createdAt" <= ${endDate}`;
} else { } else {
switch (dateRange) { switch (dateRange) {
case 'last30days': { case 'last30days': {
const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000); const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
dateCondition = sql<boolean>`e."createdAt" >= ${thirtyDaysAgo}`; dateCondition = sql`e."createdAt" >= ${thirtyDaysAgo}`;
break; break;
} }
case 'last90days': { case 'last90days': {
const ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000); const ninetyDaysAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000);
dateCondition = sql<boolean>`e."createdAt" >= ${ninetyDaysAgo}`; dateCondition = sql`e."createdAt" >= ${ninetyDaysAgo}`;
break; break;
} }
case 'lastYear': { case 'lastYear': {
const oneYearAgo = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate()); const oneYearAgo = new Date(now.getFullYear() - 1, now.getMonth(), now.getDate());
dateCondition = sql<boolean>`e."createdAt" >= ${oneYearAgo}`; dateCondition = sql`e."createdAt" >= ${oneYearAgo}`;
break; break;
} }
case 'allTime': case 'allTime':
default: default:
dateCondition = sql<boolean>`1=1`; dateCondition = sql`1=1`;
break; break;
} }
} }
let findQuery = kyselyPrisma.$kysely let findQuery = kyselyPrisma.$kysely
.selectFrom('Organisation as o') .selectFrom('Organisation as o')
.leftJoin('Team as t', 'o.id', 't.organisationId')
.leftJoin('Envelope as e', (join) =>
join
.onRef('t.id', '=', 'e.teamId')
.on('e.status', '=', sql.lit(DocumentStatus.COMPLETED))
.on('e.deletedAt', 'is', null)
.on('e.type', '=', sql.lit(EnvelopeType.DOCUMENT)),
)
.leftJoin('OrganisationMember as om', 'o.id', 'om.organisationId')
.leftJoin('Subscription as s', 'o.id', 's.organisationId') .leftJoin('Subscription as s', 'o.id', 's.organisationId')
.where((eb) => .where((eb) =>
eb.or([ eb.or([eb('o.name', 'ilike', `%${search}%`), eb('t.name', 'ilike', `%${search}%`)]),
eb('o.name', 'ilike', `%${search}%`),
eb.exists(
eb
.selectFrom('Team as t')
.whereRef('t.organisationId', '=', 'o.id')
.where('t.name', 'ilike', `%${search}%`),
),
]),
) )
.select((eb) => [ .select([
'o.id as id', 'o.id as id',
'o.createdAt as createdAt', 'o.createdAt as createdAt',
'o.customerId as customerId', 'o.customerId as customerId',
sql<string>`COALESCE(o.name, 'Unknown')`.as('name'), sql<string>`COALESCE(o.name, 'Unknown')`.as('name'),
sql<number>`COUNT(DISTINCT CASE WHEN e.id IS NOT NULL AND ${dateCondition} THEN e.id END)`.as(
'signingVolume',
),
sql<number>`GREATEST(COUNT(DISTINCT t.id), 1)`.as('teamCount'),
sql<number>`COUNT(DISTINCT om."userId")`.as('memberCount'),
sql<string>`CASE WHEN s.status IS NOT NULL THEN s.status ELSE NULL END`.as( sql<string>`CASE WHEN s.status IS NOT NULL THEN s.status ELSE NULL END`.as(
'subscriptionStatus', 'subscriptionStatus',
), ),
eb ])
.selectFrom('Team as t') .groupBy(['o.id', 'o.name', 'o.customerId', 's.status']);
.whereRef('t.organisationId', '=', 'o.id')
.select(sql<number>`count(t.id)`.as('count'))
.as('teamCount'),
eb
.selectFrom('OrganisationMember as om')
.whereRef('om.organisationId', '=', 'o.id')
.select(sql<number>`count(om.id)`.as('count'))
.as('memberCount'),
eb
.selectFrom('Envelope as e')
.innerJoin('Team as t', 't.id', 'e.teamId')
.whereRef('t.organisationId', '=', 'o.id')
.where('e.status', '=', sql.lit(DocumentStatus.COMPLETED))
.where('e.deletedAt', 'is', null)
.where('e.type', '=', sql.lit(EnvelopeType.DOCUMENT))
.where(dateCondition)
.select(sql<number>`count(e.id)`.as('count'))
.as('signingVolume'),
]);
switch (sortBy) { switch (sortBy) {
case 'name': case 'name':
@ -208,18 +181,11 @@ export async function getOrganisationInsights({
const countQuery = kyselyPrisma.$kysely const countQuery = kyselyPrisma.$kysely
.selectFrom('Organisation as o') .selectFrom('Organisation as o')
.leftJoin('Team as t', 'o.id', 't.organisationId')
.where((eb) => .where((eb) =>
eb.or([ eb.or([eb('o.name', 'ilike', `%${search}%`), eb('t.name', 'ilike', `%${search}%`)]),
eb('o.name', 'ilike', `%${search}%`),
eb.exists(
eb
.selectFrom('Team as t')
.whereRef('t.organisationId', '=', 'o.id')
.where('t.name', 'ilike', `%${search}%`),
),
]),
) )
.select(({ fn }) => [fn.countAll().as('count')]); .select(() => [sql<number>`COUNT(DISTINCT o.id)`.as('count')]);
const [results, [{ count }]] = await Promise.all([findQuery.execute(), countQuery.execute()]); const [results, [{ count }]] = await Promise.all([findQuery.execute(), countQuery.execute()]);

View File

@ -1,29 +0,0 @@
-- CreateIndex
CREATE INDEX "Envelope_teamId_deletedAt_type_status_idx" ON "Envelope"("teamId", "deletedAt", "type", "status");
-- CreateIndex
CREATE INDEX "Envelope_teamId_deletedAt_type_createdAt_idx" ON "Envelope"("teamId", "deletedAt", "type", "createdAt");
-- CreateIndex
CREATE INDEX "Envelope_userId_deletedAt_type_idx" ON "Envelope"("userId", "deletedAt", "type");
-- CreateIndex
CREATE INDEX "Envelope_status_deletedAt_type_idx" ON "Envelope"("status", "deletedAt", "type");
-- CreateIndex
CREATE INDEX "Organisation_name_idx" ON "Organisation"("name");
-- CreateIndex
CREATE INDEX "OrganisationMember_organisationId_idx" ON "OrganisationMember"("organisationId");
-- CreateIndex
CREATE INDEX "Recipient_email_idx" ON "Recipient"("email");
-- CreateIndex
CREATE INDEX "Recipient_signedAt_idx" ON "Recipient"("signedAt");
-- CreateIndex
CREATE INDEX "Recipient_envelopeId_signedAt_idx" ON "Recipient"("envelopeId", "signedAt");
-- CreateIndex
CREATE INDEX "Team_organisationId_name_idx" ON "Team"("organisationId", "name");

View File

@ -433,10 +433,6 @@ model Envelope {
@@index([folderId]) @@index([folderId])
@@index([teamId]) @@index([teamId])
@@index([userId]) @@index([userId])
@@index([teamId, deletedAt, type, status])
@@index([teamId, deletedAt, type, createdAt])
@@index([userId, deletedAt, type])
@@index([status, deletedAt, type])
} }
model EnvelopeItem { model EnvelopeItem {
@ -589,9 +585,6 @@ model Recipient {
@@index([envelopeId]) @@index([envelopeId])
@@index([token]) @@index([token])
@@index([email])
@@index([signedAt])
@@index([envelopeId, signedAt])
} }
enum FieldType { enum FieldType {
@ -701,8 +694,6 @@ model Organisation {
organisationAuthenticationPortalId String @unique organisationAuthenticationPortalId String @unique
organisationAuthenticationPortal OrganisationAuthenticationPortal @relation(fields: [organisationAuthenticationPortalId], references: [id]) organisationAuthenticationPortal OrganisationAuthenticationPortal @relation(fields: [organisationAuthenticationPortalId], references: [id])
@@index([name])
} }
model OrganisationMember { model OrganisationMember {
@ -719,7 +710,6 @@ model OrganisationMember {
organisationGroupMembers OrganisationGroupMember[] organisationGroupMembers OrganisationGroupMember[]
@@unique([userId, organisationId]) @@unique([userId, organisationId])
@@index([organisationId])
} }
model OrganisationMemberInvite { model OrganisationMemberInvite {
@ -894,7 +884,6 @@ model Team {
teamGlobalSettings TeamGlobalSettings @relation(fields: [teamGlobalSettingsId], references: [id], onDelete: Cascade) teamGlobalSettings TeamGlobalSettings @relation(fields: [teamGlobalSettingsId], references: [id], onDelete: Cascade)
@@index([organisationId]) @@index([organisationId])
@@index([organisationId, name])
} }
model TeamEmail { model TeamEmail {

View File

@ -127,11 +127,11 @@ export const DocumentShareButton = ({
<Button <Button
variant="outline" variant="outline"
disabled={!token || !documentId} disabled={!token || !documentId}
className={cn('flex-1 text-[11px]', className)} className={cn('w-full max-w-lg flex-1 text-[11px]', className)}
loading={isLoading} loading={isLoading}
> >
{!isLoading && <Sparkles className="mr-2 h-5 w-5" />} {!isLoading && <Sparkles className="mr-2 h-5 w-5" />}
<Trans>Share Signature Card</Trans> <Trans>Share</Trans>
</Button> </Button>
)} )}
</DialogTrigger> </DialogTrigger>