mirror of
https://github.com/documenso/documenso.git
synced 2025-11-10 04:22:32 +10:00
Compare commits
15 Commits
fix/zapier
...
v1.9.1-rc.
| Author | SHA1 | Date | |
|---|---|---|---|
| 235d846d2b | |||
| ca3d65ad10 | |||
| 617e3a46e0 | |||
| 255c33cdab | |||
| 1560218d4a | |||
| 5c7768c253 | |||
| 7bb93e4233 | |||
| 42e39f7ef1 | |||
| 838e399c73 | |||
| b6a891acc8 | |||
| 84b4d58856 | |||
| c51c32fdc6 | |||
| 59c1e55233 | |||
| 2fbaf56c06 | |||
| 70320cd24b |
@ -1,4 +1,4 @@
|
|||||||
> 🚨 We are live on Product Hunt 🎉 Check out our latest launch: <a href="documen.so/sign-everywhere">The Platform Plan</a>!
|
> 🚨 We are live on Product Hunt 🎉 Check out our latest launch: <a href="https://documen.so/sign-everywhere">The Platform Plan</a>!
|
||||||
|
|
||||||
<a href="https://www.producthunt.com/posts/documenso-platform-plan?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-documenso-platform-plan" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=670576&theme=light" alt="Documenso Platform Plan - Whitelabeled signing flows in your product | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
<a href="https://www.producthunt.com/posts/documenso-platform-plan?embed=true&utm_source=badge-featured&utm_medium=badge&utm_souce=badge-documenso-platform-plan" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=670576&theme=light" alt="Documenso Platform Plan - Whitelabeled signing flows in your product | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||||
|
|
||||||
|
|||||||
@ -6,5 +6,6 @@
|
|||||||
"solid": "Solid Integration",
|
"solid": "Solid Integration",
|
||||||
"preact": "Preact Integration",
|
"preact": "Preact Integration",
|
||||||
"angular": "Angular Integration",
|
"angular": "Angular Integration",
|
||||||
"css-variables": "CSS Variables"
|
"css-variables": "CSS Variables",
|
||||||
|
"web-components": "Web Components"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -73,14 +73,15 @@ These customization options are available for both Direct Templates and Signing
|
|||||||
|
|
||||||
We support embedding across a range of popular JavaScript frameworks, including:
|
We support embedding across a range of popular JavaScript frameworks, including:
|
||||||
|
|
||||||
| Framework | Package |
|
| Framework | Package |
|
||||||
| --------- | ---------------------------------------------------------------------------------- |
|
| --------- | ---------------------------------------------------------------------------------- |
|
||||||
| React | [@documenso/embed-react](https://www.npmjs.com/package/@documenso/embed-react) |
|
| React | [@documenso/embed-react](https://www.npmjs.com/package/@documenso/embed-react) |
|
||||||
| Preact | [@documenso/embed-preact](https://www.npmjs.com/package/@documenso/embed-preact) |
|
| Preact | [@documenso/embed-preact](https://www.npmjs.com/package/@documenso/embed-preact) |
|
||||||
| Vue | [@documenso/embed-vue](https://www.npmjs.com/package/@documenso/embed-vue) |
|
| Vue | [@documenso/embed-vue](https://www.npmjs.com/package/@documenso/embed-vue) |
|
||||||
| Svelte | [@documenso/embed-svelte](https://www.npmjs.com/package/@documenso/embed-svelte) |
|
| Svelte | [@documenso/embed-svelte](https://www.npmjs.com/package/@documenso/embed-svelte) |
|
||||||
| Solid | [@documenso/embed-solid](https://www.npmjs.com/package/@documenso/embed-solid) |
|
| Solid | [@documenso/embed-solid](https://www.npmjs.com/package/@documenso/embed-solid) |
|
||||||
| Angular | [@documenso/embed-angular](https://www.npmjs.com/package/@documenso/embed-angular) |
|
| Angular | [@documenso/embed-angular](https://www.npmjs.com/package/@documenso/embed-angular) |
|
||||||
|
| Web Components | [@documenso/embed-webcomponent](https://www.npmjs.com/package/@documenso/embed-webcomponent) |
|
||||||
|
|
||||||
Additionally, we provide **web components** for more generalized use. However, please note that web components are still in their early stages and haven't been extensively tested.
|
Additionally, we provide **web components** for more generalized use. However, please note that web components are still in their early stages and haven't been extensively tested.
|
||||||
|
|
||||||
@ -166,6 +167,7 @@ Once you've obtained the appropriate tokens, you can integrate the signing exper
|
|||||||
- [Svelte](/developers/embedding/svelte)
|
- [Svelte](/developers/embedding/svelte)
|
||||||
- [Solid](/developers/embedding/solid)
|
- [Solid](/developers/embedding/solid)
|
||||||
- [Angular](/developers/embedding/angular)
|
- [Angular](/developers/embedding/angular)
|
||||||
|
- [Web Components](/developers/embedding/web-components)
|
||||||
|
|
||||||
If you're using **web components**, the integration process is slightly different. Keep in mind that web components are currently less tested but can still provide flexibility for general use cases.
|
If you're using **web components**, the integration process is slightly different. Keep in mind that web components are currently less tested but can still provide flexibility for general use cases.
|
||||||
|
|
||||||
@ -177,4 +179,5 @@ If you're using **web components**, the integration process is slightly differen
|
|||||||
- [Solid Integration](/developers/embedding/solid)
|
- [Solid Integration](/developers/embedding/solid)
|
||||||
- [Preact Integration](/developers/embedding/preact)
|
- [Preact Integration](/developers/embedding/preact)
|
||||||
- [Angular Integration](/developers/embedding/angular)
|
- [Angular Integration](/developers/embedding/angular)
|
||||||
|
- [Web Components](/developers/embedding/web-components)
|
||||||
- [CSS Variables](/developers/embedding/css-variables)
|
- [CSS Variables](/developers/embedding/css-variables)
|
||||||
|
|||||||
@ -0,0 +1,89 @@
|
|||||||
|
---
|
||||||
|
title: Web Components Integration
|
||||||
|
description: Learn how to use our embedding SDK via Web Components on a framework-less web application.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Web Components Integration
|
||||||
|
|
||||||
|
Our Web Components SDK provides a simple way to embed a signing experience within your framework-less web application. It supports both direct link templates and signing tokens.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
To install the SDK, run the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @documenso/embed-webcomponent
|
||||||
|
```
|
||||||
|
|
||||||
|
Then in your html file, add the following to add the script, replacing the path with the proper path to the web component script.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script src="YOUR_PATH_HERE"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
To embed a signing experience, you'll need to provide the token for the document you want to embed. This can be done in a few different ways, depending on your use case.
|
||||||
|
|
||||||
|
### Direct Link Template
|
||||||
|
|
||||||
|
If you have a direct link template, you can simply provide the token for the template to the `documenso-embed-direct-template` tag.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<documenso-embed-direct-template
|
||||||
|
token="YOUR_TOKEN_HERE"
|
||||||
|
</documenso-embed-direct-template>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Attributes
|
||||||
|
|
||||||
|
| Attribute | Type | Description |
|
||||||
|
| ------------------- | ------------------- | ------------------------------------------------------------------------------------------ |
|
||||||
|
| token | string | The token for the document you want to embed |
|
||||||
|
| host | string (optional) | The host to be used for the signing experience, relevant for self-hosters |
|
||||||
|
| name | string (optional) | The name the signer that will be used by default for signing |
|
||||||
|
| lockName | boolean (optional) | Whether or not the name field should be locked disallowing modifications |
|
||||||
|
| email | string (optional) | The email the signer that will be used by default for signing |
|
||||||
|
| lockEmail | boolean (optional) | Whether or not the email field should be locked disallowing modifications |
|
||||||
|
| onDocumentReady | function (optional) | A callback function that will be called when the document is loaded and ready to be signed |
|
||||||
|
| onDocumentCompleted | function (optional) | A callback function that will be called when the document has been completed |
|
||||||
|
| onDocumentError | function (optional) | A callback function that will be called when an error occurs with the document |
|
||||||
|
| onFieldSigned | function (optional) | A callback function that will be called when a field is signed |
|
||||||
|
| onFieldUnsigned | function (optional) | A callback function that will be called when a field is unsigned |
|
||||||
|
|
||||||
|
### Signing Token
|
||||||
|
|
||||||
|
If you have a signing token, you can provide it to the `documenso-embed-sign-document` tag.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<documenso-embed-sign-document
|
||||||
|
token="YOUR_TOKEN_HERE"
|
||||||
|
</documenso-embed-sign-document>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Attributes
|
||||||
|
|
||||||
|
| Attribute | Type | Description |
|
||||||
|
| ------------------- | ------------------- | ------------------------------------------------------------------------------------------ |
|
||||||
|
| token | string | The token for the document you want to embed |
|
||||||
|
| host | string (optional) | The host to be used for the signing experience, relevant for self-hosters |
|
||||||
|
| name | string (optional) | The name the signer that will be used by default for signing |
|
||||||
|
| lockName | boolean (optional) | Whether or not the name field should be locked disallowing modifications |
|
||||||
|
| onDocumentReady | function (optional) | A callback function that will be called when the document is loaded and ready to be signed |
|
||||||
|
| onDocumentCompleted | function (optional) | A callback function that will be called when the document has been completed |
|
||||||
|
| onDocumentError | function (optional) | A callback function that will be called when an error occurs with the document |
|
||||||
|
|
||||||
|
### Creating via JavaScript
|
||||||
|
|
||||||
|
You can also create the tag element using javascript, for dynamic generation of either modes. For example, this would add the sign document embed to the DOM.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
document.getElementById('my-wrapper-here').innerHTML = '';
|
||||||
|
|
||||||
|
const tag = document.createElement('documenso-embed-sign-document');
|
||||||
|
tag.setAttribute('token', data.token);
|
||||||
|
tag.style.width = '100%';
|
||||||
|
tag.style.height = '100%';
|
||||||
|
|
||||||
|
document.getElementById('my-wrapper-here').appendChild(tag);
|
||||||
|
```
|
||||||
@ -21,14 +21,25 @@ Check out the [API V1 documentation](https://app.documenso.com/api/v1/openapi) f
|
|||||||
|
|
||||||
## API V2 - Beta
|
## API V2 - Beta
|
||||||
|
|
||||||
Our new API V2 is currently in Beta. The new API features typed SDKs for TypeScript, Python and Go and example code for many more.
|
<Callout type="warning">API V2 is currently beta, and will be subject to breaking changes</Callout>
|
||||||
|
|
||||||
<Callout type="warning">
|
Check out the [API V2 documentation](https://documen.so/api-v2-docs) for details about the API endpoints, request parameters, response formats, and authentication methods.
|
||||||
NOW IN BETA: [API V2 Documentation](https://documen.so/api-v2-docs)
|
|
||||||
|
Our new API V2 supports the following typed SDKs:
|
||||||
|
|
||||||
|
- [TypeScript](https://github.com/documenso/sdk-typescript)
|
||||||
|
- [Python](https://github.com/documenso/sdk-python)
|
||||||
|
- [Go](https://github.com/documenso/sdk-go)
|
||||||
|
|
||||||
|
<Callout type="info">
|
||||||
|
For the staging API, please use the following base URL:
|
||||||
|
`https://stg-app.documenso.dev/api/v2-beta/`
|
||||||
</Callout>
|
</Callout>
|
||||||
|
|
||||||
🚀 [V2 Announcement](https://documen.so/sdk-blog)
|
🚀 [V2 Announcement](https://documen.so/sdk-blog)
|
||||||
|
|
||||||
|
📖 [Documentation](https://documen.so/api-v2-docs)
|
||||||
|
|
||||||
💬 [Leave Feedback](https://documen.so/sdk-feedback)
|
💬 [Leave Feedback](https://documen.so/sdk-feedback)
|
||||||
|
|
||||||
🔔 [Breaking Changes](https://documen.so/sdk-breaking)
|
🔔 [Breaking Changes](https://documen.so/sdk-breaking)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@documenso/web",
|
"name": "@documenso/web",
|
||||||
"version": "1.9.1-rc.2",
|
"version": "1.9.1-rc.7",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -44,7 +44,12 @@ export const CheckboxField = ({ field, onSignField, onUnsignField }: CheckboxFie
|
|||||||
const [isPending, startTransition] = useTransition();
|
const [isPending, startTransition] = useTransition();
|
||||||
const { executeActionAuthProcedure } = useRequiredDocumentAuthContext();
|
const { executeActionAuthProcedure } = useRequiredDocumentAuthContext();
|
||||||
|
|
||||||
const parsedFieldMeta = ZCheckboxFieldMeta.parse(field.fieldMeta);
|
const parsedFieldMeta = ZCheckboxFieldMeta.parse(
|
||||||
|
field.fieldMeta ?? {
|
||||||
|
type: 'checkbox',
|
||||||
|
values: [{ id: 1, checked: false, value: '' }],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const values = parsedFieldMeta.values?.map((item) => ({
|
const values = parsedFieldMeta.values?.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
|
|||||||
@ -49,7 +49,7 @@ export type EmbedDirectTemplateClientPageProps = {
|
|||||||
fields: Field[];
|
fields: Field[];
|
||||||
metadata?: DocumentMeta | TemplateMeta | null;
|
metadata?: DocumentMeta | TemplateMeta | null;
|
||||||
hidePoweredBy?: boolean;
|
hidePoweredBy?: boolean;
|
||||||
isPlatformOrEnterprise?: boolean;
|
allowWhiteLabelling?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const EmbedDirectTemplateClientPage = ({
|
export const EmbedDirectTemplateClientPage = ({
|
||||||
@ -60,7 +60,7 @@ export const EmbedDirectTemplateClientPage = ({
|
|||||||
fields,
|
fields,
|
||||||
metadata,
|
metadata,
|
||||||
hidePoweredBy = false,
|
hidePoweredBy = false,
|
||||||
isPlatformOrEnterprise = false,
|
allowWhiteLabelling = false,
|
||||||
}: EmbedDirectTemplateClientPageProps) => {
|
}: EmbedDirectTemplateClientPageProps) => {
|
||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
@ -288,7 +288,7 @@ export const EmbedDirectTemplateClientPage = ({
|
|||||||
document.documentElement.classList.add('dark-mode-disabled');
|
document.documentElement.classList.add('dark-mode-disabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPlatformOrEnterprise) {
|
if (allowWhiteLabelling) {
|
||||||
injectCss({
|
injectCss({
|
||||||
css: data.css,
|
css: data.css,
|
||||||
cssVars: data.cssVars,
|
cssVars: data.cssVars,
|
||||||
@ -349,7 +349,7 @@ export const EmbedDirectTemplateClientPage = ({
|
|||||||
{/* Widget */}
|
{/* Widget */}
|
||||||
<div
|
<div
|
||||||
key={isExpanded ? 'expanded' : 'collapsed'}
|
key={isExpanded ? 'expanded' : 'collapsed'}
|
||||||
className="group/document-widget fixed bottom-8 left-0 z-50 h-fit w-full flex-shrink-0 px-6 md:sticky md:top-4 md:z-auto md:w-[350px] md:px-0"
|
className="group/document-widget fixed bottom-8 left-0 z-50 h-fit max-h-[calc(100dvh-2rem)] w-full flex-shrink-0 px-6 md:sticky md:top-4 md:z-auto md:w-[350px] md:px-0"
|
||||||
data-expanded={isExpanded || undefined}
|
data-expanded={isExpanded || undefined}
|
||||||
>
|
>
|
||||||
<div className="border-border bg-widget flex h-fit w-full flex-col rounded-xl border px-4 py-4 md:min-h-[min(calc(100dvh-2rem),48rem)] md:py-6">
|
<div className="border-border bg-widget flex h-fit w-full flex-col rounded-xl border px-4 py-4 md:min-h-[min(calc(100dvh-2rem),48rem)] md:py-6">
|
||||||
@ -360,19 +360,34 @@ export const EmbedDirectTemplateClientPage = ({
|
|||||||
<Trans>Sign document</Trans>
|
<Trans>Sign document</Trans>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<Button variant="outline" className="h-8 w-8 p-0 md:hidden">
|
{isExpanded ? (
|
||||||
{isExpanded ? (
|
<Button
|
||||||
<LucideChevronDown
|
variant="outline"
|
||||||
className="text-muted-foreground h-5 w-5"
|
className="h-8 w-8 p-0 md:hidden"
|
||||||
onClick={() => setIsExpanded(false)}
|
onClick={() => setIsExpanded(false)}
|
||||||
/>
|
>
|
||||||
) : (
|
<LucideChevronDown className="text-muted-foreground h-5 w-5" />
|
||||||
<LucideChevronUp
|
</Button>
|
||||||
className="text-muted-foreground h-5 w-5"
|
) : pendingFields.length > 0 ? (
|
||||||
onClick={() => setIsExpanded(true)}
|
<Button
|
||||||
/>
|
variant="outline"
|
||||||
)}
|
className="h-8 w-8 p-0 md:hidden"
|
||||||
</Button>
|
onClick={() => setIsExpanded(true)}
|
||||||
|
>
|
||||||
|
<LucideChevronUp className="text-muted-foreground h-5 w-5" />
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
size="sm"
|
||||||
|
className="md:hidden"
|
||||||
|
disabled={isThrottled || (hasSignatureField && !signatureValid)}
|
||||||
|
loading={isSubmitting}
|
||||||
|
onClick={() => throttledOnCompleteClick()}
|
||||||
|
>
|
||||||
|
<Trans>Complete</Trans>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { notFound } from 'next/navigation';
|
|||||||
|
|
||||||
import { match } from 'ts-pattern';
|
import { match } from 'ts-pattern';
|
||||||
|
|
||||||
|
import { isCommunityPlan as isUserCommunityPlan } from '@documenso/ee/server-only/util/is-community-plan';
|
||||||
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
|
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
|
||||||
import { isDocumentPlatform } from '@documenso/ee/server-only/util/is-document-platform';
|
import { isDocumentPlatform } from '@documenso/ee/server-only/util/is-document-platform';
|
||||||
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
|
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
|
||||||
@ -55,12 +56,16 @@ export default async function EmbedDirectTemplatePage({ params }: EmbedDirectTem
|
|||||||
documentAuth: template.authOptions,
|
documentAuth: template.authOptions,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [isPlatformDocument, isEnterpriseDocument] = await Promise.all([
|
const [isPlatformDocument, isEnterpriseDocument, isCommunityPlan] = await Promise.all([
|
||||||
isDocumentPlatform(template),
|
isDocumentPlatform(template),
|
||||||
isUserEnterprise({
|
isUserEnterprise({
|
||||||
userId: template.userId,
|
userId: template.userId,
|
||||||
teamId: template.teamId ?? undefined,
|
teamId: template.teamId ?? undefined,
|
||||||
}),
|
}),
|
||||||
|
isUserCommunityPlan({
|
||||||
|
userId: template.userId,
|
||||||
|
teamId: template.teamId ?? undefined,
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const isAccessAuthValid = match(derivedRecipientAccessAuth)
|
const isAccessAuthValid = match(derivedRecipientAccessAuth)
|
||||||
@ -105,8 +110,10 @@ export default async function EmbedDirectTemplatePage({ params }: EmbedDirectTem
|
|||||||
recipient={recipient}
|
recipient={recipient}
|
||||||
fields={fields}
|
fields={fields}
|
||||||
metadata={template.templateMeta}
|
metadata={template.templateMeta}
|
||||||
hidePoweredBy={isPlatformDocument || isEnterpriseDocument || hidePoweredBy}
|
hidePoweredBy={
|
||||||
isPlatformOrEnterprise={isPlatformDocument || isEnterpriseDocument}
|
isCommunityPlan || isPlatformDocument || isEnterpriseDocument || hidePoweredBy
|
||||||
|
}
|
||||||
|
allowWhiteLabelling={isCommunityPlan || isPlatformDocument || isEnterpriseDocument}
|
||||||
/>
|
/>
|
||||||
</RecipientProvider>
|
</RecipientProvider>
|
||||||
</DocumentAuthProvider>
|
</DocumentAuthProvider>
|
||||||
|
|||||||
@ -51,7 +51,7 @@ export type EmbedSignDocumentClientPageProps = {
|
|||||||
metadata?: DocumentMeta | TemplateMeta | null;
|
metadata?: DocumentMeta | TemplateMeta | null;
|
||||||
isCompleted?: boolean;
|
isCompleted?: boolean;
|
||||||
hidePoweredBy?: boolean;
|
hidePoweredBy?: boolean;
|
||||||
isPlatformOrEnterprise?: boolean;
|
allowWhitelabelling?: boolean;
|
||||||
allRecipients?: RecipientWithFields[];
|
allRecipients?: RecipientWithFields[];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ export const EmbedSignDocumentClientPage = ({
|
|||||||
metadata,
|
metadata,
|
||||||
isCompleted,
|
isCompleted,
|
||||||
hidePoweredBy = false,
|
hidePoweredBy = false,
|
||||||
isPlatformOrEnterprise = false,
|
allowWhitelabelling = false,
|
||||||
allRecipients = [],
|
allRecipients = [],
|
||||||
}: EmbedSignDocumentClientPageProps) => {
|
}: EmbedSignDocumentClientPageProps) => {
|
||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
@ -212,7 +212,7 @@ export const EmbedSignDocumentClientPage = ({
|
|||||||
document.documentElement.classList.add('dark-mode-disabled');
|
document.documentElement.classList.add('dark-mode-disabled');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPlatformOrEnterprise) {
|
if (allowWhitelabelling) {
|
||||||
injectCss({
|
injectCss({
|
||||||
css: data.css,
|
css: data.css,
|
||||||
cssVars: data.cssVars,
|
cssVars: data.cssVars,
|
||||||
@ -288,7 +288,7 @@ export const EmbedSignDocumentClientPage = ({
|
|||||||
{/* Widget */}
|
{/* Widget */}
|
||||||
<div
|
<div
|
||||||
key={isExpanded ? 'expanded' : 'collapsed'}
|
key={isExpanded ? 'expanded' : 'collapsed'}
|
||||||
className="embed--DocumentWidgetContainer group/document-widget fixed bottom-8 left-0 z-50 h-fit w-full flex-shrink-0 px-6 md:sticky md:top-4 md:z-auto md:w-[350px] md:px-0"
|
className="embed--DocumentWidgetContainer group/document-widget fixed bottom-8 left-0 z-50 h-fit max-h-[calc(100dvh-2rem)] w-full flex-shrink-0 px-6 md:sticky md:top-4 md:z-auto md:w-[350px] md:px-0"
|
||||||
data-expanded={isExpanded || undefined}
|
data-expanded={isExpanded || undefined}
|
||||||
>
|
>
|
||||||
<div className="embed--DocumentWidget border-border bg-widget flex w-full flex-col rounded-xl border px-4 py-4 md:py-6">
|
<div className="embed--DocumentWidget border-border bg-widget flex w-full flex-col rounded-xl border px-4 py-4 md:py-6">
|
||||||
@ -303,19 +303,36 @@ export const EmbedSignDocumentClientPage = ({
|
|||||||
)}
|
)}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<Button variant="outline" className="h-8 w-8 p-0 md:hidden">
|
{isExpanded ? (
|
||||||
{isExpanded ? (
|
<Button
|
||||||
<LucideChevronDown
|
variant="outline"
|
||||||
className="text-muted-foreground h-5 w-5"
|
className="bg-background dark:bg-foreground h-8 w-8 p-0 md:hidden"
|
||||||
onClick={() => setIsExpanded(false)}
|
onClick={() => setIsExpanded(false)}
|
||||||
/>
|
>
|
||||||
) : (
|
<LucideChevronDown className="text-muted-foreground dark:text-background h-5 w-5" />
|
||||||
<LucideChevronUp
|
</Button>
|
||||||
className="text-muted-foreground h-5 w-5"
|
) : pendingFields.length > 0 ? (
|
||||||
onClick={() => setIsExpanded(true)}
|
<Button
|
||||||
/>
|
variant="outline"
|
||||||
)}
|
className="bg-background dark:bg-foreground h-8 w-8 p-0 md:hidden"
|
||||||
</Button>
|
onClick={() => setIsExpanded(true)}
|
||||||
|
>
|
||||||
|
<LucideChevronUp className="text-muted-foreground dark:text-background h-5 w-5" />
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
size="sm"
|
||||||
|
className="md:hidden"
|
||||||
|
disabled={
|
||||||
|
isThrottled || (!isAssistantMode && hasSignatureField && !signatureValid)
|
||||||
|
}
|
||||||
|
loading={isSubmitting}
|
||||||
|
onClick={() => throttledOnCompleteClick()}
|
||||||
|
>
|
||||||
|
<Trans>Complete</Trans>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { notFound } from 'next/navigation';
|
|||||||
|
|
||||||
import { match } from 'ts-pattern';
|
import { match } from 'ts-pattern';
|
||||||
|
|
||||||
|
import { isCommunityPlan as isUserCommunityPlan } from '@documenso/ee/server-only/util/is-community-plan';
|
||||||
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
|
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
|
||||||
import { isDocumentPlatform } from '@documenso/ee/server-only/util/is-document-platform';
|
import { isDocumentPlatform } from '@documenso/ee/server-only/util/is-document-platform';
|
||||||
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
|
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
|
||||||
@ -62,12 +63,16 @@ export default async function EmbedSignDocumentPage({ params }: EmbedSignDocumen
|
|||||||
return <EmbedPaywall />;
|
return <EmbedPaywall />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [isPlatformDocument, isEnterpriseDocument] = await Promise.all([
|
const [isPlatformDocument, isEnterpriseDocument, isCommunityPlan] = await Promise.all([
|
||||||
isDocumentPlatform(document),
|
isDocumentPlatform(document),
|
||||||
isUserEnterprise({
|
isUserEnterprise({
|
||||||
userId: document.userId,
|
userId: document.userId,
|
||||||
teamId: document.teamId ?? undefined,
|
teamId: document.teamId ?? undefined,
|
||||||
}),
|
}),
|
||||||
|
isUserCommunityPlan({
|
||||||
|
userId: document.userId,
|
||||||
|
teamId: document.teamId ?? undefined,
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const { derivedRecipientAccessAuth } = extractDocumentAuthMethods({
|
const { derivedRecipientAccessAuth } = extractDocumentAuthMethods({
|
||||||
@ -126,8 +131,10 @@ export default async function EmbedSignDocumentPage({ params }: EmbedSignDocumen
|
|||||||
fields={fields}
|
fields={fields}
|
||||||
metadata={document.documentMeta}
|
metadata={document.documentMeta}
|
||||||
isCompleted={document.status === DocumentStatus.COMPLETED}
|
isCompleted={document.status === DocumentStatus.COMPLETED}
|
||||||
hidePoweredBy={isPlatformDocument || isEnterpriseDocument || hidePoweredBy}
|
hidePoweredBy={
|
||||||
isPlatformOrEnterprise={isPlatformDocument || isEnterpriseDocument}
|
isCommunityPlan || isPlatformDocument || isEnterpriseDocument || hidePoweredBy
|
||||||
|
}
|
||||||
|
allowWhitelabelling={isCommunityPlan || isPlatformDocument || isEnterpriseDocument}
|
||||||
allRecipients={allRecipients}
|
allRecipients={allRecipients}
|
||||||
/>
|
/>
|
||||||
</DocumentAuthProvider>
|
</DocumentAuthProvider>
|
||||||
|
|||||||
6
package-lock.json
generated
6
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@documenso/root",
|
"name": "@documenso/root",
|
||||||
"version": "1.9.1-rc.2",
|
"version": "1.9.1-rc.7",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@documenso/root",
|
"name": "@documenso/root",
|
||||||
"version": "1.9.1-rc.2",
|
"version": "1.9.1-rc.7",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"apps/*",
|
"apps/*",
|
||||||
"packages/*"
|
"packages/*"
|
||||||
@ -106,7 +106,7 @@
|
|||||||
},
|
},
|
||||||
"apps/web": {
|
"apps/web": {
|
||||||
"name": "@documenso/web",
|
"name": "@documenso/web",
|
||||||
"version": "1.9.1-rc.2",
|
"version": "1.9.1-rc.7",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@documenso/api": "*",
|
"@documenso/api": "*",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.9.1-rc.2",
|
"version": "1.9.1-rc.7",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "turbo run build",
|
"build": "turbo run build",
|
||||||
"build:web": "turbo run build --filter=@documenso/web",
|
"build:web": "turbo run build --filter=@documenso/web",
|
||||||
|
|||||||
@ -4,15 +4,14 @@ import { stripe } from '@documenso/lib/server-only/stripe';
|
|||||||
type PlanType = (typeof STRIPE_PLAN_TYPE)[keyof typeof STRIPE_PLAN_TYPE];
|
type PlanType = (typeof STRIPE_PLAN_TYPE)[keyof typeof STRIPE_PLAN_TYPE];
|
||||||
|
|
||||||
export const getPricesByPlan = async (plan: PlanType | PlanType[]) => {
|
export const getPricesByPlan = async (plan: PlanType | PlanType[]) => {
|
||||||
const planTypes = typeof plan === 'string' ? [plan] : plan;
|
const planTypes: string[] = typeof plan === 'string' ? [plan] : plan;
|
||||||
|
|
||||||
const query = planTypes.map((planType) => `metadata['plan']:'${planType}'`).join(' OR ');
|
const prices = await stripe.prices.list({
|
||||||
|
|
||||||
const { data: prices } = await stripe.prices.search({
|
|
||||||
query,
|
|
||||||
expand: ['data.product'],
|
expand: ['data.product'],
|
||||||
limit: 100,
|
limit: 100,
|
||||||
});
|
});
|
||||||
|
|
||||||
return prices.filter((price) => price.type === 'recurring');
|
return prices.data.filter(
|
||||||
|
(price) => price.type === 'recurring' && planTypes.includes(price.metadata.plan),
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
56
packages/ee/server-only/util/is-community-plan.ts
Normal file
56
packages/ee/server-only/util/is-community-plan.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { subscriptionsContainsActivePlan } from '@documenso/lib/utils/billing';
|
||||||
|
import { prisma } from '@documenso/prisma';
|
||||||
|
import type { Subscription } from '@documenso/prisma/client';
|
||||||
|
|
||||||
|
import { getCommunityPlanPriceIds } from '../stripe/get-community-plan-prices';
|
||||||
|
|
||||||
|
export type IsCommunityPlanOptions = {
|
||||||
|
userId: number;
|
||||||
|
teamId?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the user or team is on the community plan.
|
||||||
|
*/
|
||||||
|
export const isCommunityPlan = async ({
|
||||||
|
userId,
|
||||||
|
teamId,
|
||||||
|
}: IsCommunityPlanOptions): Promise<boolean> => {
|
||||||
|
let subscriptions: Subscription[] = [];
|
||||||
|
|
||||||
|
if (teamId) {
|
||||||
|
subscriptions = await prisma.team
|
||||||
|
.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
id: teamId,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
owner: {
|
||||||
|
include: {
|
||||||
|
subscriptions: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((team) => team.owner.subscriptions);
|
||||||
|
} else {
|
||||||
|
subscriptions = await prisma.user
|
||||||
|
.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
subscriptions: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((user) => user.subscriptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subscriptions.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const communityPlanPriceIds = await getCommunityPlanPriceIds();
|
||||||
|
|
||||||
|
return subscriptionsContainsActivePlan(subscriptions, communityPlanPriceIds);
|
||||||
|
};
|
||||||
@ -88,6 +88,7 @@ export const findDocuments = async ({
|
|||||||
const searchFilter: Prisma.DocumentWhereInput = {
|
const searchFilter: Prisma.DocumentWhereInput = {
|
||||||
OR: [
|
OR: [
|
||||||
{ title: { contains: query, mode: 'insensitive' } },
|
{ title: { contains: query, mode: 'insensitive' } },
|
||||||
|
{ externalId: { contains: query, mode: 'insensitive' } },
|
||||||
{ recipients: { some: { name: { contains: query, mode: 'insensitive' } } } },
|
{ recipients: { some: { name: { contains: query, mode: 'insensitive' } } } },
|
||||||
{ recipients: { some: { email: { contains: query, mode: 'insensitive' } } } },
|
{ recipients: { some: { email: { contains: query, mode: 'insensitive' } } } },
|
||||||
],
|
],
|
||||||
|
|||||||
@ -34,6 +34,14 @@ export const searchDocumentsWithKeyword = async ({
|
|||||||
userId: userId,
|
userId: userId,
|
||||||
deletedAt: null,
|
deletedAt: null,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
externalId: {
|
||||||
|
contains: query,
|
||||||
|
mode: 'insensitive',
|
||||||
|
},
|
||||||
|
userId: userId,
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
recipients: {
|
recipients: {
|
||||||
some: {
|
some: {
|
||||||
@ -88,6 +96,23 @@ export const searchDocumentsWithKeyword = async ({
|
|||||||
},
|
},
|
||||||
deletedAt: null,
|
deletedAt: null,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
externalId: {
|
||||||
|
contains: query,
|
||||||
|
mode: 'insensitive',
|
||||||
|
},
|
||||||
|
teamId: {
|
||||||
|
not: null,
|
||||||
|
},
|
||||||
|
team: {
|
||||||
|
members: {
|
||||||
|
some: {
|
||||||
|
userId: userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
include: {
|
include: {
|
||||||
|
|||||||
Reference in New Issue
Block a user