diff --git a/.env.example b/.env.example
index d188894de..c482c128e 100644
--- a/.env.example
+++ b/.env.example
@@ -4,8 +4,10 @@ NEXTAUTH_SECRET="secret"
# [[CRYPTO]]
# Application Key for symmetric encryption and decryption
-# This should be a random string of at least 32 characters
+# REQUIRED: This should be a random string of at least 32 characters
NEXT_PRIVATE_ENCRYPTION_KEY="CAFEBABE"
+# REQUIRED: This should be a random string of at least 32 characters
+NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY="DEADBEEF"
# [[AUTH OPTIONAL]]
NEXT_PRIVATE_GOOGLE_CLIENT_ID=""
@@ -23,7 +25,7 @@ NEXT_PRIVATE_DIRECT_DATABASE_URL="postgres://documenso:password@127.0.0.1:54320/
# [[E2E Tests]]
E2E_TEST_AUTHENTICATE_USERNAME="Test User"
E2E_TEST_AUTHENTICATE_USER_EMAIL="testuser@mail.com"
-E2E_TEST_AUTHENTICATE_USER_PASSWORD="test_password"
+E2E_TEST_AUTHENTICATE_USER_PASSWORD="test_Password123"
# [[STORAGE]]
# OPTIONAL: Defines the storage transport to use. Available options: database (default) | s3
@@ -72,6 +74,8 @@ NEXT_PRIVATE_MAILCHANNELS_DKIM_DOMAIN=
NEXT_PRIVATE_MAILCHANNELS_DKIM_SELECTOR=
# OPTIONAL: The private key to use for DKIM signing.
NEXT_PRIVATE_MAILCHANNELS_DKIM_PRIVATE_KEY=
+# OPTIONAL: Displays the maximum document upload limit to the user in MBs
+NEXT_PUBLIC_DOCUMENT_SIZE_UPLOAD_LIMIT=5
# [[STRIPE]]
NEXT_PRIVATE_STRIPE_API_KEY=
diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml
index ab21e8828..ffb788c23 100644
--- a/.github/ISSUE_TEMPLATE/feature-request.yml
+++ b/.github/ISSUE_TEMPLATE/feature-request.yml
@@ -33,3 +33,4 @@ body:
- label: I have explained the use case or scenario for this feature.
- label: I have included any relevant technical details or design suggestions.
- label: I understand that this is a suggestion and that there is no guarantee of implementation.
+ - label: I want to work on creating a PR for this issue if approved
diff --git a/apps/marketing/content/blog/commodifying-signing.mdx b/apps/marketing/content/blog/commodifying-signing.mdx
new file mode 100644
index 000000000..0a9cf4050
--- /dev/null
+++ b/apps/marketing/content/blog/commodifying-signing.mdx
@@ -0,0 +1,87 @@
+---
+title: Commodifying Signing
+description: We are creating signing as a public good and are commoditizing it to make it cheaper and better.
+authorName: 'Timur Ercan'
+authorImage: '/blog/blog-author-timur.jpeg'
+authorRole: 'Co-Founder'
+date: 2024-01-25
+Tags:
+ - Vision
+ - Mission
+ - Open Source
+---
+
+
+
+
+
+ Lighthouses are often used as an example of a public good; As they benefit all maritime users, but no one can be excluded from using them as a navigational aid. Use by one person neither prevents access by other people, nor does it reduce availability to others.
+
+
+
+# Commodifying Signing
+
+> TLDR; We are creating signing as a public good and are commoditizing it to make it cheaper and better.
+
+While we are in full-on building mode with Documenso, I think a lot about the big picture of what we are attempting to do. One phrase that keeps popping up is, "We are commodifying signing." Let's dig deeper into what that means.
+
+Let's start with why we are doing this. Documenso's mission is to solve the domain of signing once and for all for everyone. In so many calls, I hear stories about how organizations build their own solution because the existing ones are too expensive or need to be more flexible. That means not hundreds but probably thousands of companies worldwide have done the same. This is simply wasting humanity's time. Since digital signing systems are understood well enough that seemingly "everyone" can build them, given enough pain, It's time to do it once correctly.
+
+## Is signing already a commodity?
+
+> In economics, a **commodity** is an economic good, usually a resource, that has explicitly full or substantial fungibility: that is, the market treats instances of the good as equivalent or nearly so with no regard to who produced them.
+
+That sounds like the signing market today. There is no shortage of signing providers, and you can get similar signing services from many places. So why is this different from what we want, and why does this not satisfy the market?
+
+- Signing is expensive and painful when you are locked into your vendor, and they charge by signing volume.
+- Signing is also expensive and painful when you have to build it yourself since no vendor fits your requirements or you are not allowed to
+
+To understand why, we need to look at the landscape as it is today:
+
+- **Commodity**: Signing SaaS
+- **Private Goods**: Signing Code Base, Regulatory Know-How
+- **Public Goods**: Web Tech, Digital Signature Algorithms and Standards
+
+What the current players have done is to commodify the listed public goods into commercial products:
+
+> […]the action and process of transforming goods, services, ideas, nature, personal information, people, or animals into commodities.
+> (Let's ignore the end of that list for now and what it says about humanity, yikes)
+
+While this paradigm brought digital signing to many businesses worldwide, we aim for a different future. To solve signing once and for all, we need to achieve two core points:
+
+- Making it cheaper so it's profitable for everyone to use
+- Making it more accessible so everyone can use it (e.g. regulated industries) and flexible enough (extendable, open).
+
+To achieve this, we must transform the landscape to look like this:
+
+- **Commodities**: Enterprise Components, Support, Hosting, Self-Host Licenses
+- **Public Goods**: (no longer private): OS (Open Source) Signing Code Base, OS Regulatory Know-How
+- **Public Goods**: OS Web Tech, Digital Signature Algorithms and Standards
+
+## Raising the Bar
+
+Before creating a commodity, we are raising the bar of what the underlying public good is. Having an open source singing framework you can extend, self-host, and understand makes the resulting solution much more accessible and extendable for everyone. Now for the final feat of making signing cheaper:
+
+As we have seen, signing has already been commodified. But since it was done by a closed source and, frankly, a very opaque industry, no downward price spiral has ensued. By building Documenso open source with an open culture, we can pierce the veil and trigger what the space has been missing for a long time: Commoditization. If you had to read that again, so did I:
+
+> In business literature, **commoditization** is defined as the process by which goods that have economic value and are distinguishable in terms of attributes (uniqueness or brand) become simple commodities in the eyes of the market or consumers.
+
+By only selling what creates value for the customer (hosting a highly available service, keeping it compliant, supporting with technical issues and challenges, preparing industry-specific components), we are commoditizing signing since everyone can do it now: The resources enabling it are public goods, aka. open source. A leveled playing field, as described above, is the perfect environment for a community-first, technology-first, and value-first company like Documenso to flourish.
+
+## Changing the Game
+
+In this new world, a company needing signing (literally every company) can decide if the ROI (Return on Investment) of building signing themselves is greater than simply paying for the value-added activities they will need anyway. Pricing our offering not on volume but fixed is a nice additional wedge into the market we intend to use here.
+
+The market dynamic now changes to who can offer the greatest value added to the public goods, driving the price down as this can be done much more efficiently than locking customers into closed source SaaS. Documenso, being a lean company, which we intend to stay with for a long time, will help kickstart this effect. Open Source capital efficiency is real. Our planned enterprise components, hosting support, and partner ecosystem will all leverage this effect.
+
+We will grow our community around the public good, the open-source repo, and create an ecosystem around the commodities built on top of it (components, hosting, compliance, support). We will solve signing once and for all, and the world will be better for it. Onwards.
+
+As always, feel free to connect on [Twitter / X](https://twitter.com/eltimuro) (DM open) or [Discord](https://documen.so/discord) if you have any questions or comments.
+
+Best from Hamburg\
+Timur
diff --git a/apps/marketing/content/blog/linear-gh.mdx b/apps/marketing/content/blog/linear-gh.mdx
index 27b1ae208..1267931d6 100644
--- a/apps/marketing/content/blog/linear-gh.mdx
+++ b/apps/marketing/content/blog/linear-gh.mdx
@@ -109,7 +109,7 @@ It's similar to the Kanban board for the development backlog.
While the internal design backlog also existed in Linear, the public design repository is new. Since designing in the open is tricky, we opted to publish the detailed design artifacts with the corresponding feature instead.
We already have design.documenso.com housing our general design system. Here, we will publish the specifics of how we applied this to each feature. We will publish the first artifacts here soon, what may be in the cards can be found on the [LIVE Roadmap](https://documen.so/live).
-Feel free to connect with us on [Twitter / X](https://twitter.com/eltimuro) (DM open) or [Discord](https://documen.so/discord) if you have any questions or comments! We're always here to help and would love to hear from you :)
+Feel free to connect with us on [Twitter / X](https://twitter.com/eltimuro) (DM open) or [Discord](https://documen.so/discord) if you have any questions or comments! We're always here to help and would love to hear from you :)
Best from Hamburg\
Timur
diff --git a/apps/marketing/content/blog/manifest.mdx b/apps/marketing/content/blog/manifest.mdx
index 4abd7c068..7f2b7e7cd 100644
--- a/apps/marketing/content/blog/manifest.mdx
+++ b/apps/marketing/content/blog/manifest.mdx
@@ -7,6 +7,8 @@ authorRole: 'Co-Founder'
date: 2023-07-13
tags:
- Manifesto
+ - Open Source
+ - Vision
---
diff --git a/apps/marketing/public/blog/lighthouse.jpeg b/apps/marketing/public/blog/lighthouse.jpeg
new file mode 100644
index 000000000..d71e1eb51
Binary files /dev/null and b/apps/marketing/public/blog/lighthouse.jpeg differ
diff --git a/apps/marketing/src/app/(marketing)/[content]/page.tsx b/apps/marketing/src/app/(marketing)/[content]/page.tsx
index 5c846e9f2..62c83f400 100644
--- a/apps/marketing/src/app/(marketing)/[content]/page.tsx
+++ b/apps/marketing/src/app/(marketing)/[content]/page.tsx
@@ -15,7 +15,7 @@ export const generateMetadata = ({ params }: { params: { content: string } }) =>
notFound();
}
- return { title: `Documenso - ${document.title}` };
+ return { title: document.title };
};
const mdxComponents: MDXComponents = {
diff --git a/apps/marketing/src/app/(marketing)/blog/[post]/page.tsx b/apps/marketing/src/app/(marketing)/blog/[post]/page.tsx
index f1952cc72..866539a92 100644
--- a/apps/marketing/src/app/(marketing)/blog/[post]/page.tsx
+++ b/apps/marketing/src/app/(marketing)/blog/[post]/page.tsx
@@ -18,7 +18,9 @@ export const generateMetadata = ({ params }: { params: { post: string } }) => {
}
return {
- title: `Documenso - ${blogPost.title}`,
+ title: {
+ absolute: `${blogPost.title} - Documenso Blog`,
+ },
description: blogPost.description,
};
};
diff --git a/apps/marketing/src/app/(marketing)/blog/page.tsx b/apps/marketing/src/app/(marketing)/blog/page.tsx
index 747a56ddf..2eac963d1 100644
--- a/apps/marketing/src/app/(marketing)/blog/page.tsx
+++ b/apps/marketing/src/app/(marketing)/blog/page.tsx
@@ -1,5 +1,10 @@
+import type { Metadata } from 'next';
+
import { allBlogPosts } from 'contentlayer/generated';
+export const metadata: Metadata = {
+ title: 'Blog',
+};
export default function BlogPage() {
const blogPosts = allBlogPosts.sort((a, b) => {
const dateA = new Date(a.date);
diff --git a/apps/marketing/src/app/(marketing)/open/data.ts b/apps/marketing/src/app/(marketing)/open/data.ts
index 3b109ea74..a3f314d9f 100644
--- a/apps/marketing/src/app/(marketing)/open/data.ts
+++ b/apps/marketing/src/app/(marketing)/open/data.ts
@@ -47,6 +47,14 @@ export const TEAM_MEMBERS = [
engagement: 'Full-Time',
joinDate: 'October 9th, 2023',
},
+ {
+ name: 'Adithya Krishna',
+ role: 'Software Engineer - II',
+ salary: '-',
+ location: 'India',
+ engagement: 'Full-Time',
+ joinDate: 'December 1st, 2023',
+ },
];
export const FUNDING_RAISED = [
diff --git a/apps/marketing/src/app/(marketing)/open/page.tsx b/apps/marketing/src/app/(marketing)/open/page.tsx
index e237919bc..a1fea41e4 100644
--- a/apps/marketing/src/app/(marketing)/open/page.tsx
+++ b/apps/marketing/src/app/(marketing)/open/page.tsx
@@ -1,3 +1,5 @@
+import type { Metadata } from 'next';
+
import { z } from 'zod';
import { getUserMonthlyGrowth } from '@documenso/lib/server-only/user/get-user-monthly-growth';
@@ -14,6 +16,10 @@ import { MonthlyTotalUsersChart } from './monthly-total-users-chart';
import { TeamMembers } from './team-members';
import { OpenPageTooltip } from './tooltip';
+export const metadata: Metadata = {
+ title: 'Open Startup',
+};
+
export const revalidate = 3600;
export const dynamic = 'force-dynamic';
diff --git a/apps/marketing/src/app/(marketing)/oss-friends/page.tsx b/apps/marketing/src/app/(marketing)/oss-friends/page.tsx
index a91446408..65a4a55f8 100644
--- a/apps/marketing/src/app/(marketing)/oss-friends/page.tsx
+++ b/apps/marketing/src/app/(marketing)/oss-friends/page.tsx
@@ -1,3 +1,4 @@
+import type { Metadata } from 'next';
import Image from 'next/image';
import { z } from 'zod';
@@ -5,7 +6,12 @@ import { z } from 'zod';
import backgroundPattern from '@documenso/assets/images/background-pattern.png';
import { OSSFriendsContainer } from './container';
-import { TOSSFriendsSchema, ZOSSFriendsSchema } from './schema';
+import type { TOSSFriendsSchema } from './schema';
+import { ZOSSFriendsSchema } from './schema';
+
+export const metadata: Metadata = {
+ title: 'OSS Friends',
+};
export default async function OSSFriendsPage() {
const ossFriends: TOSSFriendsSchema = await fetch('https://formbricks.com/api/oss-friends', {
diff --git a/apps/marketing/src/app/(marketing)/page.tsx b/apps/marketing/src/app/(marketing)/page.tsx
index 377384701..10918299a 100644
--- a/apps/marketing/src/app/(marketing)/page.tsx
+++ b/apps/marketing/src/app/(marketing)/page.tsx
@@ -1,4 +1,5 @@
/* eslint-disable no-unused-vars, @typescript-eslint/no-unused-vars */
+import type { Metadata } from 'next';
import { Caveat } from 'next/font/google';
import { cn } from '@documenso/ui/lib/utils';
@@ -10,6 +11,11 @@ import { OpenBuildTemplateBento } from '~/components/(marketing)/open-build-temp
import { ShareConnectPaidWidgetBento } from '~/components/(marketing)/share-connect-paid-widget-bento';
export const revalidate = 600;
+export const metadata: Metadata = {
+ title: {
+ absolute: 'Documenso - The Open Source DocuSign Alternative',
+ },
+};
const fontCaveat = Caveat({
weight: ['500'],
diff --git a/apps/marketing/src/app/(marketing)/pricing/page.tsx b/apps/marketing/src/app/(marketing)/pricing/page.tsx
index 92043b3b3..e4c7b776a 100644
--- a/apps/marketing/src/app/(marketing)/pricing/page.tsx
+++ b/apps/marketing/src/app/(marketing)/pricing/page.tsx
@@ -1,5 +1,4 @@
-'use client';
-
+import type { Metadata } from 'next';
import Link from 'next/link';
import {
@@ -12,6 +11,10 @@ import { Button } from '@documenso/ui/primitives/button';
import { PricingTable } from '~/components/(marketing)/pricing-table';
+export const metadata: Metadata = {
+ title: 'Pricing',
+};
+
export type PricingPageProps = {
searchParams?: {
planId?: string;
diff --git a/apps/marketing/src/app/(marketing)/singleplayer/client.tsx b/apps/marketing/src/app/(marketing)/singleplayer/client.tsx
index 389528bf8..a1b56257a 100644
--- a/apps/marketing/src/app/(marketing)/singleplayer/client.tsx
+++ b/apps/marketing/src/app/(marketing)/singleplayer/client.tsx
@@ -158,6 +158,7 @@ export const SinglePlayerClient = () => {
readStatus: 'OPENED',
signingStatus: 'NOT_SIGNED',
sendStatus: 'NOT_SENT',
+ role: 'SIGNER',
};
const onFileDrop = async (file: File) => {
diff --git a/apps/marketing/src/app/(marketing)/singleplayer/page.tsx b/apps/marketing/src/app/(marketing)/singleplayer/page.tsx
index a98906476..aafad32a8 100644
--- a/apps/marketing/src/app/(marketing)/singleplayer/page.tsx
+++ b/apps/marketing/src/app/(marketing)/singleplayer/page.tsx
@@ -1,5 +1,11 @@
+import type { Metadata } from 'next';
+
import { SinglePlayerClient } from './client';
+export const metadata: Metadata = {
+ title: 'Singleplayer',
+};
+
export const revalidate = 0;
// !: This entire file is a hack to get around failed prerendering of
diff --git a/apps/marketing/src/app/layout.tsx b/apps/marketing/src/app/layout.tsx
index 05206a76f..1745149c6 100644
--- a/apps/marketing/src/app/layout.tsx
+++ b/apps/marketing/src/app/layout.tsx
@@ -18,7 +18,10 @@ const fontInter = Inter({ subsets: ['latin'], variable: '--font-sans' });
const fontCaveat = Caveat({ subsets: ['latin'], variable: '--font-signature' });
export const metadata = {
- title: 'Documenso - The Open Source DocuSign Alternative',
+ title: {
+ template: '%s - Documenso',
+ default: 'Documenso',
+ },
description:
'Join Documenso, the open signing infrastructure, and get a 10x better signing experience. Pricing starts at $30/mo. forever! Sign in now and enjoy a faster, smarter, and more beautiful document signing process. Integrates with your favorite tools, customizable, and expandable. Support our mission and become a part of our open-source community.',
keywords:
diff --git a/apps/marketing/src/components/(marketing)/widget.tsx b/apps/marketing/src/components/(marketing)/widget.tsx
index 80c13b275..d4305a04c 100644
--- a/apps/marketing/src/components/(marketing)/widget.tsx
+++ b/apps/marketing/src/components/(marketing)/widget.tsx
@@ -399,6 +399,7 @@ export const Widget = ({ className, children, ...props }: WidgetProps) => {
diff --git a/apps/web/src/app/(dashboard)/documents/data-table-action-button.tsx b/apps/web/src/app/(dashboard)/documents/data-table-action-button.tsx
index 9910ef111..ecddf1190 100644
--- a/apps/web/src/app/(dashboard)/documents/data-table-action-button.tsx
+++ b/apps/web/src/app/(dashboard)/documents/data-table-action-button.tsx
@@ -2,13 +2,13 @@
import Link from 'next/link';
-import { Download, Edit, Pencil } from 'lucide-react';
+import { CheckCircle, Download, Edit, EyeIcon, Pencil } from 'lucide-react';
import { useSession } from 'next-auth/react';
import { match } from 'ts-pattern';
import { downloadPDF } from '@documenso/lib/client-only/download-pdf';
import type { Document, Recipient, User } from '@documenso/prisma/client';
-import { DocumentStatus, SigningStatus } from '@documenso/prisma/client';
+import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/client';
import type { DocumentWithData } from '@documenso/prisma/types/document-with-data';
import { trpc as trpcClient } from '@documenso/trpc/client';
import { Button } from '@documenso/ui/primitives/button';
@@ -37,6 +37,7 @@ export const DataTableActionButton = ({ row }: DataTableActionButtonProps) => {
const isPending = row.status === DocumentStatus.PENDING;
const isComplete = row.status === DocumentStatus.COMPLETED;
const isSigned = recipient?.signingStatus === SigningStatus.SIGNED;
+ const role = recipient?.role;
const onDownloadClick = async () => {
try {
@@ -68,6 +69,11 @@ export const DataTableActionButton = ({ row }: DataTableActionButtonProps) => {
}
};
+ // TODO: Consider if want to keep this logic for hiding viewing for CC'ers
+ if (recipient?.role === RecipientRole.CC && isComplete === false) {
+ return null;
+ }
+
return match({
isOwner,
isRecipient,
@@ -87,15 +93,32 @@ export const DataTableActionButton = ({ row }: DataTableActionButtonProps) => {
.with({ isRecipient: true, isPending: true, isSigned: false }, () => (
))
.with({ isPending: true, isSigned: true }, () => (
))
.with({ isComplete: true }, () => (
diff --git a/apps/web/src/app/(dashboard)/documents/data-table-action-dropdown.tsx b/apps/web/src/app/(dashboard)/documents/data-table-action-dropdown.tsx
index f14321b35..e1d9b64bb 100644
--- a/apps/web/src/app/(dashboard)/documents/data-table-action-dropdown.tsx
+++ b/apps/web/src/app/(dashboard)/documents/data-table-action-dropdown.tsx
@@ -5,9 +5,11 @@ import { useState } from 'react';
import Link from 'next/link';
import {
+ CheckCircle,
Copy,
Download,
Edit,
+ EyeIcon,
Loader,
MoreHorizontal,
Pencil,
@@ -19,7 +21,7 @@ import { useSession } from 'next-auth/react';
import { downloadPDF } from '@documenso/lib/client-only/download-pdf';
import type { Document, Recipient, User } from '@documenso/prisma/client';
-import { DocumentStatus } from '@documenso/prisma/client';
+import { DocumentStatus, RecipientRole } from '@documenso/prisma/client';
import type { DocumentWithData } from '@documenso/prisma/types/document-with-data';
import { trpc as trpcClient } from '@documenso/trpc/client';
import { DocumentShareButton } from '@documenso/ui/components/document/document-share-button';
@@ -105,12 +107,32 @@ export const DataTableActionDropdown = ({ row }: DataTableActionDropdownProps) =
Action
-
-
-
- Sign
-
-
+ {recipient?.role !== RecipientRole.CC && (
+
+
+ {recipient?.role === RecipientRole.VIEWER && (
+ <>
+
+ View
+ >
+ )}
+
+ {recipient?.role === RecipientRole.SIGNER && (
+ <>
+
+ Sign
+ >
+ )}
+
+ {recipient?.role === RecipientRole.APPROVER && (
+ <>
+
+ Approve
+ >
+ )}
+
+
+ )}
diff --git a/apps/web/src/app/(dashboard)/documents/page.tsx b/apps/web/src/app/(dashboard)/documents/page.tsx
index 8bb321377..5780df1dc 100644
--- a/apps/web/src/app/(dashboard)/documents/page.tsx
+++ b/apps/web/src/app/(dashboard)/documents/page.tsx
@@ -1,6 +1,8 @@
+import type { Metadata } from 'next';
import Link from 'next/link';
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
+import type { PeriodSelectorValue } from '@documenso/lib/server-only/document/find-documents';
import { findDocuments } from '@documenso/lib/server-only/document/find-documents';
import { getStats } from '@documenso/lib/server-only/document/get-stats';
import { isExtendedDocumentStatus } from '@documenso/prisma/guards/is-extended-document-status';
@@ -8,7 +10,6 @@ import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-documen
import { Tabs, TabsList, TabsTrigger } from '@documenso/ui/primitives/tabs';
import { PeriodSelector } from '~/components/(dashboard)/period-selector/period-selector';
-import type { PeriodSelectorValue } from '~/components/(dashboard)/period-selector/types';
import { isPeriodSelectorValue } from '~/components/(dashboard)/period-selector/types';
import { DocumentStatus } from '~/components/formatter/document-status';
@@ -25,18 +26,22 @@ export type DocumentsPageProps = {
};
};
+export const metadata: Metadata = {
+ title: 'Documents',
+};
export default async function DocumentsPage({ searchParams = {} }: DocumentsPageProps) {
const { user } = await getRequiredServerComponentSession();
- const stats = await getStats({
- user,
- });
-
const status = isExtendedDocumentStatus(searchParams.status) ? searchParams.status : 'ALL';
const period = isPeriodSelectorValue(searchParams.period) ? searchParams.period : '';
const page = Number(searchParams.page) || 1;
const perPage = Number(searchParams.perPage) || 20;
+ const stats = await getStats({
+ user,
+ period,
+ });
+
const results = await findDocuments({
userId: user.id,
status,
@@ -69,7 +74,7 @@ export default async function DocumentsPage({ searchParams = {} }: DocumentsPage
- Add and manage your two factor security settings to add an extra layer of security to your
- account!
-
+
+ Create one-time passwords that serve as a secondary authentication method for
+ confirming your identity when requested during the sign-in process.
+
+
-
-
Two-factor methods
+
+
-
-
+ {user.twoFactorEnabled && (
+
+
+ Recovery codes
- {user.twoFactorEnabled && (
-
-
Recovery methods
+
+ Two factor authentication recovery codes are used to access your account in the
+ event that you lose access to your authenticator app.
+
+
-
+
+
+ )}
+ ) : (
+
+
+ Your account is managed by {IDENTITY_PROVIDER_NAME[user.identityProvider]}
+
+
+
+ To update your password, enable two-factor authentication, and manage other security
+ settings, please go to your {IDENTITY_PROVIDER_NAME[user.identityProvider]} account
+ settings.
+
+
)}
+
+
+
+ Recent activity
+
+
+ View all recent security activity related to your account.
+
+
+
+
+
);
}
diff --git a/apps/web/src/app/(dashboard)/templates/data-table-templates.tsx b/apps/web/src/app/(dashboard)/templates/data-table-templates.tsx
index 63d6888b1..7930dcd0e 100644
--- a/apps/web/src/app/(dashboard)/templates/data-table-templates.tsx
+++ b/apps/web/src/app/(dashboard)/templates/data-table-templates.tsx
@@ -2,13 +2,16 @@
import { useState, useTransition } from 'react';
+import Link from 'next/link';
import { useRouter } from 'next/navigation';
-import { Loader, Plus } from 'lucide-react';
+import { AlertTriangle, Loader, Plus } from 'lucide-react';
+import { useLimits } from '@documenso/ee/server-only/limits/provider/client';
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
import type { Template } from '@documenso/prisma/client';
import { trpc } from '@documenso/trpc/react';
+import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
import { Button } from '@documenso/ui/primitives/button';
import { DataTable } from '@documenso/ui/primitives/data-table';
import { DataTablePagination } from '@documenso/ui/primitives/data-table-pagination';
@@ -36,6 +39,8 @@ export const TemplatesDataTable = ({
const [isPending, startTransition] = useTransition();
const updateSearchParams = useUpdateSearchParams();
+ const { remaining } = useLimits();
+
const router = useRouter();
const { toast } = useToast();
@@ -77,6 +82,19 @@ export const TemplatesDataTable = ({
return (
+ {remaining.documents === 0 && (
+
+
+ Document Limit Exceeded!
+
+ You have reached your document limit.{' '}
+
+ Upgrade your account to continue!
+
+
+
+ )}
+