mirror of
https://github.com/documenso/documenso.git
synced 2026-07-11 21:45:18 +10:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e4dd6799c7 | |||
| 7fee24042d | |||
| 400b6a24f1 | |||
| 1b1e3d197b | |||
| a276e18e1f |
@@ -76,6 +76,8 @@ The Enterprise Edition is required when you:
|
||||
4. Restart your Documenso instance
|
||||
5. Verify the license is active in the **Admin Panel** under the **Stats** section
|
||||
|
||||
See [Apply Your License Key](/docs/self-hosting/configuration/license) for the full walkthrough, including how to enable individual features once licensed.
|
||||
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
@@ -197,7 +199,7 @@ See [Support](/docs/policies/support) for complete support options.
|
||||
1. Sign the Enterprise license agreement
|
||||
2. Receive license key and access credentials
|
||||
3. Deploy using [self-hosting guides](/docs/self-hosting) or access Documenso Cloud
|
||||
4. Configure Enterprise features with support assistance
|
||||
4. Apply the key — see [Apply Your License Key](/docs/self-hosting/configuration/license) — and configure Enterprise features with support assistance
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
@@ -238,6 +240,7 @@ See [Support](/docs/policies/support) for complete support options.
|
||||
|
||||
## Related
|
||||
|
||||
- [Apply Your License Key](/docs/self-hosting/configuration/license) - Step-by-step license activation
|
||||
- [Community Edition](/docs/policies/community-edition) - AGPL-3.0 open-source license
|
||||
- [Licenses](/docs/policies/licenses) - Complete licensing overview and FAQ
|
||||
- [Support](/docs/policies/support) - Support channels and response times
|
||||
|
||||
@@ -443,11 +443,11 @@ Telemetry collects only: app version, installation ID, and node ID. No personal
|
||||
|
||||
## Enterprise Features
|
||||
|
||||
These variables require an active [Enterprise Edition](/docs/policies/enterprise-edition) license. Obtain a license key from [license.documenso.com](https://license.documenso.com) and set it below to unlock enterprise features such as SSO, embed editor, and 21 CFR Part 11 compliance.
|
||||
These variables require an active [Enterprise Edition](/docs/policies/enterprise-edition) license. Obtain a license key from [license.documenso.com](https://license.documenso.com) and set it below to unlock enterprise features such as SSO, embed editor, and 21 CFR Part 11 compliance. See [Apply Your License Key](/docs/self-hosting/configuration/license) for step-by-step setup.
|
||||
|
||||
| Variable | Description |
|
||||
| ------------------------------------ | ------------------------------------------------ |
|
||||
| `NEXT_PRIVATE_DOCUMENSO_LICENSE_KEY` | License key for enterprise features |
|
||||
| `NEXT_PRIVATE_DOCUMENSO_LICENSE_KEY` | License key for enterprise features — see [Apply Your License Key](/docs/self-hosting/configuration/license) for how to apply it |
|
||||
| `NEXT_PRIVATE_STRIPE_API_KEY` | Stripe API key for billing |
|
||||
| `NEXT_PRIVATE_STRIPE_WEBHOOK_SECRET` | Stripe webhook secret |
|
||||
| `NEXT_PRIVATE_SES_ACCESS_KEY_ID` | AWS SES access key for email domain verification |
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
---
|
||||
title: Apply Your License Key
|
||||
description: Activate your Enterprise license key to unlock enterprise features on your self-hosted instance.
|
||||
---
|
||||
|
||||
import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
|
||||
import { Callout } from 'fumadocs-ui/components/callout';
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
|
||||
|
||||
A license key activates the Enterprise features available to your self-hosted instance, such as CSC signing, SSO, embed white-labelling, and 21 CFR Part 11 compliance.
|
||||
|
||||
<Callout type="info">
|
||||
The license key applies to your **whole instance**, not an individual user account. There's one
|
||||
key per deployment.
|
||||
</Callout>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- An active Enterprise license key — contact [sales](https://documen.so/enterprise) to set up an
|
||||
Enterprise subscription, then copy your key from [license.documenso.com](https://license.documenso.com).
|
||||
See [Enterprise Edition](/docs/policies/enterprise-edition) for details.
|
||||
- A running self-hosted Documenso instance that you're able to restart
|
||||
|
||||
## Step 1: Set the environment variable
|
||||
|
||||
Set `NEXT_PRIVATE_DOCUMENSO_LICENSE_KEY` to your license key.
|
||||
|
||||
<Tabs items={['Docker Compose', 'docker run', '.env']}>
|
||||
<Tab value="Docker Compose">
|
||||
|
||||
Add the variable to your `.env` file (or directly under `environment:` in `compose.yml`):
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_DOCUMENSO_LICENSE_KEY=your-license-key-here
|
||||
```
|
||||
|
||||
Then apply it:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab value="docker run">
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name documenso \
|
||||
-e NEXT_PRIVATE_DOCUMENSO_LICENSE_KEY=your-license-key-here \
|
||||
documenso/documenso:latest
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab value=".env">
|
||||
|
||||
If you're running Documenso directly (not in a container), add the variable to your `.env` file:
|
||||
|
||||
```bash
|
||||
NEXT_PRIVATE_DOCUMENSO_LICENSE_KEY=your-license-key-here
|
||||
```
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Step 2: Restart the instance
|
||||
|
||||
The license key is only read once, at process startup. Setting the variable in a running container or shell has no effect until the process restarts.
|
||||
|
||||
```bash
|
||||
# Docker Compose
|
||||
docker compose restart documenso
|
||||
|
||||
# Docker
|
||||
docker restart documenso
|
||||
```
|
||||
|
||||
On startup, Documenso validates the key against the Documenso license server and caches the result locally for future startups, so a brief license-server outage won't lock you out.
|
||||
|
||||
## What the license enables
|
||||
|
||||
A valid license doesn't turn every enterprise feature on everywhere — activation depends on the feature:
|
||||
|
||||
- **CSC signing** activates instance-wide automatically once the license is active and CSC transport is configured. See [CSC / QES Signing](/docs/self-hosting/configuration/signing-certificate/csc-qes) for the full setup.
|
||||
- **SSO, embed white-labelling, 21 CFR Part 11, and similar** are provisioned per organisation. Follow each feature's own guide to configure it once the license is active.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<Accordions type="multiple">
|
||||
<Accordion title="Enterprise features are still unavailable after applying the key">
|
||||
- Confirm the key is present in the environment the running process actually reads — `docker
|
||||
exec` into the container and check `env | grep LICENSE` if unsure.
|
||||
- Confirm the instance was fully restarted after the variable was set, not just reloaded.
|
||||
- Re-copy the key to rule out truncation or accidental whitespace.
|
||||
</Accordion>
|
||||
<Accordion title="A specific feature still isn't working">
|
||||
Instance-wide features (like CSC signing) also need their own configuration — an active license
|
||||
alone isn't enough. Check that feature's guide to confirm the required settings are in place.
|
||||
Per-organisation features additionally need to be provisioned for the organisation that's using
|
||||
them.
|
||||
</Accordion>
|
||||
</Accordions>
|
||||
|
||||
## See Also
|
||||
|
||||
- [Environment Variables](/docs/self-hosting/configuration/environment) - Complete configuration reference
|
||||
- [Enterprise Edition](/docs/policies/enterprise-edition) - What's included and how to purchase a license
|
||||
- [CSC / QES Signing](/docs/self-hosting/configuration/signing-certificate/csc-qes) - Enable CSC-based signing
|
||||
@@ -2,6 +2,7 @@
|
||||
"title": "Configuration",
|
||||
"pages": [
|
||||
"environment",
|
||||
"license",
|
||||
"database",
|
||||
"email",
|
||||
"storage",
|
||||
|
||||
@@ -49,7 +49,7 @@ The callback URL is fixed — Documenso derives it from `NEXT_PUBLIC_WEBAPP_URL`
|
||||
|
||||
### Enterprise Edition license
|
||||
|
||||
CSC mode is gated by the `instanceCscSigning` license flag. Without a valid Enterprise license, the transport refuses to start (`CSC_UNLICENSED`).
|
||||
CSC mode is gated by the `instanceCscSigning` license flag. Without a valid Enterprise license, the transport refuses to start (`CSC_UNLICENSED`). See [Apply Your License Key](/docs/self-hosting/configuration/license) to activate one.
|
||||
|
||||
</Step>
|
||||
<Step>
|
||||
|
||||
@@ -141,7 +141,7 @@ See the [Quick Start guide](/docs/self-hosting/getting-started/quick-start) for
|
||||
|
||||
Self-hosted Documenso includes full core functionality under the AGPL-3.0 license. If you need enterprise features such as SSO, embed editor white label, or 21 CFR Part 11 compliance, you can activate them with a license key.
|
||||
|
||||
See [Enterprise Edition](/docs/policies/enterprise-edition) for details and [Licenses](/docs/policies/licenses) for a comparison.
|
||||
See [Enterprise Edition](/docs/policies/enterprise-edition) for details and [Licenses](/docs/policies/licenses) for a comparison. Already have a key? See [Apply Your License Key](/docs/self-hosting/configuration/license).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -275,9 +275,9 @@ export const ManagePublicTemplateDialog = ({
|
||||
|
||||
<TableCell>
|
||||
{selectedTemplateId === row.id ? (
|
||||
<CheckCircle2Icon className="h-5 w-5 text-foreground" />
|
||||
<CheckCircle2Icon className="h-5 w-5 text-neutral-600 dark:text-neutral-200" />
|
||||
) : (
|
||||
<CircleIcon className="h-5 w-5 text-muted-foreground/40" />
|
||||
<CircleIcon className="h-5 w-5 text-neutral-300 dark:text-neutral-600" />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
@@ -215,7 +215,7 @@ export const TemplateDirectLinkDialog = ({
|
||||
{DIRECT_TEMPLATE_DOCUMENTATION.map((step, index) => (
|
||||
<li className="relative" key={index}>
|
||||
<div className="absolute -left-12">
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded-full border-[3px] border-border font-bold text-sm">
|
||||
<div className="flex h-8 w-8 items-center justify-center rounded-full border-[3px] border-neutral-200 font-bold text-sm">
|
||||
{index + 1}
|
||||
</div>
|
||||
</div>
|
||||
@@ -259,8 +259,8 @@ export const TemplateDirectLinkDialog = ({
|
||||
.with({ token: P.nullish, currentStep: 'SELECT_RECIPIENT' }, () => (
|
||||
<DialogContent className="relative">
|
||||
{isCreatingTemplateDirectLink && validDirectTemplateRecipients.length !== 0 && (
|
||||
<div className="absolute inset-0 z-50 flex items-center justify-center rounded bg-background/50">
|
||||
<LoaderIcon className="h-6 w-6 animate-spin text-muted-foreground" />
|
||||
<div className="absolute inset-0 z-50 flex items-center justify-center rounded bg-white/50 dark:bg-black/50">
|
||||
<LoaderIcon className="h-6 w-6 animate-spin text-gray-500" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -317,9 +317,9 @@ export const TemplateDirectLinkDialog = ({
|
||||
|
||||
<TableCell>
|
||||
{selectedRecipientId === row.id ? (
|
||||
<CircleDotIcon className="h-5 w-5 text-muted-foreground/40" />
|
||||
<CircleDotIcon className="h-5 w-5 text-neutral-300" />
|
||||
) : (
|
||||
<CircleIcon className="h-5 w-5 text-muted-foreground/40" />
|
||||
<CircleIcon className="h-5 w-5 text-neutral-300" />
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
import { DOCUMENT_TITLE_MAX_LENGTH } from '@documenso/trpc/server/document-router/schema';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { FormControl, FormField, FormItem, FormLabel, FormMessage } from '@documenso/ui/primitives/form/form';
|
||||
import { Input } from '@documenso/ui/primitives/input';
|
||||
import { RadioGroup, RadioGroupItem } from '@documenso/ui/primitives/radio-group';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
import { useEffect } from 'react';
|
||||
import { useFormContext, useWatch } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const DOCUMENT_NAME_SOURCE = {
|
||||
TEMPLATE: 'template',
|
||||
UPLOAD: 'upload',
|
||||
CUSTOM: 'custom',
|
||||
} as const;
|
||||
|
||||
type TDocumentNameSource = (typeof DOCUMENT_NAME_SOURCE)[keyof typeof DOCUMENT_NAME_SOURCE];
|
||||
|
||||
type TTemplateUseCustomDocumentData = {
|
||||
data?: File;
|
||||
};
|
||||
|
||||
type TTemplateUseDocumentNameData = {
|
||||
documentNameSource: TDocumentNameSource;
|
||||
customDocumentName: string;
|
||||
customDocumentData?: TTemplateUseCustomDocumentData[];
|
||||
};
|
||||
|
||||
type TTemplateUseDocumentNameFormValues = TTemplateUseDocumentNameData & {
|
||||
useCustomDocument: boolean;
|
||||
};
|
||||
|
||||
const getUploadedDocumentTitle = (customDocumentData?: TTemplateUseCustomDocumentData[]) => {
|
||||
const uploadedFiles = customDocumentData?.map((item) => item.data).filter((file): file is File => file !== undefined);
|
||||
|
||||
if (uploadedFiles?.length !== 1) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return uploadedFiles[0].name.replace(/\.[^/.]+$/, '').trim();
|
||||
};
|
||||
|
||||
export const getTemplateUseDocumentTitle = (data: TTemplateUseDocumentNameData) => {
|
||||
if (data.documentNameSource === DOCUMENT_NAME_SOURCE.UPLOAD) {
|
||||
return getUploadedDocumentTitle(data.customDocumentData);
|
||||
}
|
||||
|
||||
if (data.documentNameSource === DOCUMENT_NAME_SOURCE.CUSTOM) {
|
||||
return data.customDocumentName.trim();
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const refineTemplateUseDocumentName = (data: TTemplateUseDocumentNameData, ctx: z.RefinementCtx) => {
|
||||
if (data.documentNameSource === DOCUMENT_NAME_SOURCE.TEMPLATE) {
|
||||
return;
|
||||
}
|
||||
|
||||
const title = getTemplateUseDocumentTitle(data);
|
||||
const path = data.documentNameSource === DOCUMENT_NAME_SOURCE.CUSTOM ? 'customDocumentName' : 'documentNameSource';
|
||||
|
||||
if (!title) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message:
|
||||
data.documentNameSource === DOCUMENT_NAME_SOURCE.CUSTOM
|
||||
? msg`Document name is required`.id
|
||||
: msg`Select exactly one uploaded document to use its file name.`.id,
|
||||
path: [path],
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (title.length > DOCUMENT_TITLE_MAX_LENGTH) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: msg`Document name is too long`.id,
|
||||
path: [path],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const TemplateUseDialogDocumentName = () => {
|
||||
const { t } = useLingui();
|
||||
const { clearErrors, control, setValue } = useFormContext<TTemplateUseDocumentNameFormValues>();
|
||||
const [useCustomDocument, documentNameSource, customDocumentData] = useWatch({
|
||||
control,
|
||||
name: ['useCustomDocument', 'documentNameSource', 'customDocumentData'],
|
||||
});
|
||||
|
||||
const uploadedDocumentCount = customDocumentData?.filter((item) => item.data !== undefined).length ?? 0;
|
||||
const canUseUploadedDocumentName = useCustomDocument && uploadedDocumentCount === 1;
|
||||
|
||||
useEffect(() => {
|
||||
if (documentNameSource !== DOCUMENT_NAME_SOURCE.UPLOAD || canUseUploadedDocumentName) {
|
||||
return;
|
||||
}
|
||||
|
||||
setValue('documentNameSource', DOCUMENT_NAME_SOURCE.TEMPLATE);
|
||||
clearErrors('documentNameSource');
|
||||
}, [canUseUploadedDocumentName, clearErrors, documentNameSource, setValue]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormField
|
||||
control={control}
|
||||
name="documentNameSource"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans>Document name</Trans>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
aria-label={t`Document name`}
|
||||
value={field.value}
|
||||
onValueChange={(value) => {
|
||||
field.onChange(value);
|
||||
|
||||
if (value !== DOCUMENT_NAME_SOURCE.CUSTOM) {
|
||||
clearErrors('customDocumentName');
|
||||
}
|
||||
|
||||
if (value !== DOCUMENT_NAME_SOURCE.UPLOAD) {
|
||||
clearErrors('documentNameSource');
|
||||
}
|
||||
}}
|
||||
className="space-y-2"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<RadioGroupItem id="document-name-source-template" value={DOCUMENT_NAME_SOURCE.TEMPLATE} />
|
||||
<label className="text-sm" htmlFor="document-name-source-template">
|
||||
<Trans>Use template name</Trans>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-2">
|
||||
<RadioGroupItem
|
||||
id="document-name-source-upload"
|
||||
value={DOCUMENT_NAME_SOURCE.UPLOAD}
|
||||
disabled={!canUseUploadedDocumentName}
|
||||
className="mt-0.5"
|
||||
/>
|
||||
<div>
|
||||
<label
|
||||
className={cn('text-sm', {
|
||||
'cursor-not-allowed text-muted-foreground': !canUseUploadedDocumentName,
|
||||
})}
|
||||
htmlFor="document-name-source-upload"
|
||||
>
|
||||
<Trans>Use uploaded file name</Trans>
|
||||
</label>
|
||||
|
||||
{!canUseUploadedDocumentName && (
|
||||
<p className="text-muted-foreground text-xs">
|
||||
<Trans>Select exactly one uploaded document to use its file name.</Trans>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<RadioGroupItem id="document-name-source-custom" value={DOCUMENT_NAME_SOURCE.CUSTOM} />
|
||||
<label className="text-sm" htmlFor="document-name-source-custom">
|
||||
<Trans>Enter custom document name</Trans>
|
||||
</label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{documentNameSource === DOCUMENT_NAME_SOURCE.CUSTOM && (
|
||||
<FormField
|
||||
control={control}
|
||||
name="customDocumentName"
|
||||
render={({ field }) => (
|
||||
<FormItem className="ml-6">
|
||||
<FormLabel>
|
||||
<Trans>Custom document name</Trans>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder={t`Enter a document name`} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -38,27 +38,44 @@ import { useNavigate } from 'react-router';
|
||||
import * as z from 'zod';
|
||||
import { getTemplateUseErrorMessage } from '~/utils/toast-error-messages';
|
||||
|
||||
const ZAddRecipientsForNewDocumentSchema = z.object({
|
||||
distributeDocument: z.boolean(),
|
||||
useCustomDocument: z.boolean().default(false),
|
||||
customDocumentData: z
|
||||
.array(
|
||||
import {
|
||||
DOCUMENT_NAME_SOURCE,
|
||||
getTemplateUseDocumentTitle,
|
||||
refineTemplateUseDocumentName,
|
||||
TemplateUseDialogDocumentName,
|
||||
} from './template-use-dialog-document-name';
|
||||
|
||||
const ZAddRecipientsForNewDocumentSchema = z
|
||||
.object({
|
||||
distributeDocument: z.boolean(),
|
||||
useCustomDocument: z.boolean().default(false),
|
||||
documentNameSource: z.enum([
|
||||
DOCUMENT_NAME_SOURCE.TEMPLATE,
|
||||
DOCUMENT_NAME_SOURCE.UPLOAD,
|
||||
DOCUMENT_NAME_SOURCE.CUSTOM,
|
||||
]),
|
||||
customDocumentName: z.string(),
|
||||
customDocumentData: z
|
||||
.array(
|
||||
z.object({
|
||||
title: z.string(),
|
||||
data: z.instanceof(File).optional(),
|
||||
envelopeItemId: z.string(),
|
||||
}),
|
||||
)
|
||||
.optional(),
|
||||
recipients: z.array(
|
||||
z.object({
|
||||
title: z.string(),
|
||||
data: z.instanceof(File).optional(),
|
||||
envelopeItemId: z.string(),
|
||||
id: z.number(),
|
||||
email: ZRecipientEmailSchema,
|
||||
name: z.string(),
|
||||
signingOrder: z.number().optional(),
|
||||
}),
|
||||
)
|
||||
.optional(),
|
||||
recipients: z.array(
|
||||
z.object({
|
||||
id: z.number(),
|
||||
email: ZRecipientEmailSchema,
|
||||
name: z.string(),
|
||||
signingOrder: z.number().optional(),
|
||||
}),
|
||||
),
|
||||
});
|
||||
),
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
refineTemplateUseDocumentName(data, ctx);
|
||||
});
|
||||
|
||||
type TAddRecipientsForNewDocumentSchema = z.infer<typeof ZAddRecipientsForNewDocumentSchema>;
|
||||
|
||||
@@ -106,6 +123,8 @@ export function TemplateUseDialog({
|
||||
return {
|
||||
distributeDocument: false,
|
||||
useCustomDocument: false,
|
||||
documentNameSource: DOCUMENT_NAME_SOURCE.TEMPLATE,
|
||||
customDocumentName: '',
|
||||
customDocumentData: envelopeItems.map((item) => ({
|
||||
title: item.title,
|
||||
data: undefined,
|
||||
@@ -142,6 +161,8 @@ export function TemplateUseDialog({
|
||||
|
||||
const onSubmit = async (data: TAddRecipientsForNewDocumentSchema) => {
|
||||
try {
|
||||
const documentTitle = getTemplateUseDocumentTitle(data);
|
||||
|
||||
const customFilesToUpload = (data.customDocumentData || []).filter(
|
||||
(item): item is { data: File; envelopeItemId: string; title: string } =>
|
||||
item.data !== undefined && item.envelopeItemId !== undefined && item.title !== undefined,
|
||||
@@ -163,6 +184,7 @@ export function TemplateUseDialog({
|
||||
recipients: data.recipients,
|
||||
distributeDocument: data.distributeDocument,
|
||||
customDocumentData,
|
||||
...(documentTitle ? { override: { title: documentTitle } } : {}),
|
||||
});
|
||||
|
||||
toast({
|
||||
@@ -195,6 +217,8 @@ export function TemplateUseDialog({
|
||||
name: 'recipients',
|
||||
});
|
||||
|
||||
const useCustomDocument = form.watch('useCustomDocument');
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
form.reset(generateDefaultFormValues());
|
||||
@@ -401,7 +425,16 @@ export function TemplateUseDialog({
|
||||
onCheckedChange={(checked) => {
|
||||
field.onChange(checked);
|
||||
if (!checked) {
|
||||
form.setValue('customDocumentData', undefined);
|
||||
const customDocumentData = form.getValues('customDocumentData');
|
||||
|
||||
form.setValue(
|
||||
'customDocumentData',
|
||||
customDocumentData?.map((item) => ({
|
||||
...item,
|
||||
data: undefined,
|
||||
})),
|
||||
);
|
||||
form.clearErrors('customDocumentData');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@@ -428,7 +461,7 @@ export function TemplateUseDialog({
|
||||
)}
|
||||
/>
|
||||
|
||||
{form.watch('useCustomDocument') && (
|
||||
{useCustomDocument && (
|
||||
<div className="my-4 space-y-2">
|
||||
{isLoadingEnvelopeItems ? (
|
||||
<SpinnerBox className="py-16" />
|
||||
@@ -537,6 +570,7 @@ export function TemplateUseDialog({
|
||||
}
|
||||
|
||||
field.onChange(file);
|
||||
form.clearErrors('customDocumentData');
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
@@ -550,6 +584,8 @@ export function TemplateUseDialog({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<TemplateUseDialogDocumentName />
|
||||
</div>
|
||||
|
||||
<DialogFooter className="mt-4">
|
||||
|
||||
@@ -146,7 +146,7 @@ export const AvatarImageForm = ({ className, team, organisation }: AvatarImageFo
|
||||
<div className="relative">
|
||||
<Avatar className="h-16 w-16 border-2 border-solid">
|
||||
{avatarImageId && <AvatarImage src={formatAvatarUrl(avatarImageId)} />}
|
||||
<AvatarFallback className="text-muted-foreground text-sm">{initials}</AvatarFallback>
|
||||
<AvatarFallback className="text-gray-400 text-sm">{initials}</AvatarFallback>
|
||||
</Avatar>
|
||||
|
||||
{hasAvatarImage && (
|
||||
|
||||
@@ -96,7 +96,7 @@ export const SignUpForm = ({
|
||||
password: '',
|
||||
signature: '',
|
||||
},
|
||||
mode: 'onBlur',
|
||||
mode: 'onChange',
|
||||
resolver: zodResolver(ZSignUpFormSchema),
|
||||
});
|
||||
|
||||
|
||||
+1
-1
@@ -155,7 +155,7 @@ export const DocumentSigningFieldContainer = ({
|
||||
{(field.type === FieldType.RADIO || field.type === FieldType.CHECKBOX) && field.fieldMeta?.label && (
|
||||
<div
|
||||
className={cn(
|
||||
'absolute -top-16 right-0 left-0 rounded-md p-2 text-center text-foreground text-xs',
|
||||
'absolute -top-16 right-0 left-0 rounded-md p-2 text-center text-gray-700 text-xs',
|
||||
{
|
||||
'border border-border bg-foreground/5': !field.inserted,
|
||||
},
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ export const DocumentSigningPageViewV2 = () => {
|
||||
}, [recipientFieldsRemaining, selectedAssistantRecipientFields, currentEnvelopeItem]);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen w-screen bg-muted dark:bg-background">
|
||||
<div className="min-h-screen w-screen bg-gray-50 dark:bg-background">
|
||||
<SignFieldEmailDialog.Root />
|
||||
<SignFieldTextDialog.Root />
|
||||
<SignFieldNumberDialog.Root />
|
||||
|
||||
@@ -75,7 +75,7 @@ export const DocumentPageViewRecentActivity = ({ documentId, userId }: DocumentP
|
||||
</div>
|
||||
|
||||
<div className="relative flex h-6 w-6 flex-none items-center justify-center bg-widget">
|
||||
<div className="h-1.5 w-1.5 rounded-full bg-widget ring-1 ring-border" />
|
||||
<div className="h-1.5 w-1.5 rounded-full bg-widget ring-1 ring-gray-300 dark:ring-neutral-600" />
|
||||
</div>
|
||||
|
||||
<button
|
||||
@@ -109,27 +109,27 @@ export const DocumentPageViewRecentActivity = ({ documentId, userId }: DocumentP
|
||||
<div className="relative flex h-6 w-6 flex-none items-center justify-center bg-widget text-foreground/40">
|
||||
{match(auditLog.type)
|
||||
.with(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED, () => (
|
||||
<div className="rounded-full border border-border bg-widget p-1">
|
||||
<div className="rounded-full border border-gray-300 bg-widget p-1 dark:border-neutral-600">
|
||||
<CheckCheckIcon className="h-3 w-3" aria-hidden="true" />
|
||||
</div>
|
||||
))
|
||||
.with(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED, () => (
|
||||
<div className="rounded-full border border-border bg-widget p-1">
|
||||
<div className="rounded-full border border-gray-300 bg-widget p-1 dark:border-neutral-600">
|
||||
<CheckIcon className="h-3 w-3" aria-hidden="true" />
|
||||
</div>
|
||||
))
|
||||
.with(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_REJECTED, () => (
|
||||
<div className="rounded-full border border-border bg-widget p-1">
|
||||
<div className="rounded-full border border-gray-300 bg-widget p-1 dark:border-neutral-600">
|
||||
<AlertTriangle className="h-3 w-3" aria-hidden="true" />
|
||||
</div>
|
||||
))
|
||||
.with(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED, () => (
|
||||
<div className="rounded-full border border-border bg-widget p-1">
|
||||
<div className="rounded-full border border-gray-300 bg-widget p-1 dark:border-neutral-600">
|
||||
<MailOpen className="h-3 w-3" aria-hidden="true" />
|
||||
</div>
|
||||
))
|
||||
.otherwise(() => (
|
||||
<div className="h-1.5 w-1.5 rounded-full bg-widget ring-1 ring-border" />
|
||||
<div className="h-1.5 w-1.5 rounded-full bg-widget ring-1 ring-gray-300 dark:ring-neutral-600" />
|
||||
))}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -261,7 +261,7 @@ export const EnvelopeEditorPreviewPage = () => {
|
||||
|
||||
{/* Right Section - Form Fields Panel */}
|
||||
{currentEnvelopeItem && false && (
|
||||
<div className="sticky top-0 h-full w-80 flex-shrink-0 overflow-y-auto border-border border-l bg-background py-4">
|
||||
<div className="sticky top-0 h-full w-80 flex-shrink-0 overflow-y-auto border-gray-200 border-l bg-white py-4">
|
||||
{/* Add fields section. */}
|
||||
<section className="px-4">
|
||||
{/* <h3 className="mb-2 text-sm font-semibold text-gray-900">
|
||||
@@ -326,7 +326,7 @@ export const EnvelopeEditorPreviewPage = () => {
|
||||
|
||||
{/* Recipient selector section. */}
|
||||
<section className="px-4">
|
||||
<h3 className="mb-2 font-semibold text-foreground text-sm">
|
||||
<h3 className="mb-2 font-semibold text-gray-900 text-sm">
|
||||
<Trans>Selected Recipient</Trans>
|
||||
</h3>
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ export const EnvelopeItemTitleInput = ({
|
||||
{/* Hidden span to measure text width */}
|
||||
<span
|
||||
ref={measureRef}
|
||||
className="pointer-events-none absolute top-0 left-0 whitespace-nowrap font-medium text-muted-foreground text-sm opacity-0"
|
||||
className="pointer-events-none absolute top-0 left-0 whitespace-nowrap font-medium text-gray-600 text-sm opacity-0"
|
||||
style={{ font: 'inherit' }}
|
||||
>
|
||||
{envelopeItemTitle || placeholder}
|
||||
|
||||
@@ -271,7 +271,7 @@ export const EnvelopeEditor = () => {
|
||||
`cursor-pointer rounded-lg text-left transition-colors ${
|
||||
isActive
|
||||
? 'border border-green-200 bg-green-50 dark:border-green-500/20 dark:bg-green-500/10'
|
||||
: 'border border-border hover:bg-muted/50'
|
||||
: 'border border-gray-200 hover:bg-gray-50 dark:border-gray-400/20 dark:hover:bg-gray-400/10'
|
||||
}`,
|
||||
{
|
||||
'p-3': !minimizeLeftSidebar,
|
||||
@@ -284,10 +284,10 @@ export const EnvelopeEditor = () => {
|
||||
className={`rounded border p-2 ${
|
||||
isActive
|
||||
? 'border-green-200 bg-green-50 dark:border-green-500/20 dark:bg-green-500/10'
|
||||
: 'border-muted bg-muted'
|
||||
: 'border-gray-100 bg-gray-100 dark:border-gray-400/20 dark:bg-gray-400/10'
|
||||
}`}
|
||||
>
|
||||
<Icon className={`h-4 w-4 ${isActive ? 'text-green-600' : 'text-muted-foreground'}`} />
|
||||
<Icon className={`h-4 w-4 ${isActive ? 'text-green-600' : 'text-gray-600'}`} />
|
||||
</div>
|
||||
|
||||
{!minimizeLeftSidebar && (
|
||||
|
||||
@@ -31,14 +31,14 @@ export const EnvelopeItemSelector = ({
|
||||
>
|
||||
<div
|
||||
className={`flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full font-medium text-xs ${
|
||||
isSelected ? 'bg-green-100 text-green-600' : 'bg-muted text-muted-foreground'
|
||||
isSelected ? 'bg-green-100 text-green-600' : 'bg-gray-200 text-gray-600'
|
||||
}`}
|
||||
>
|
||||
{number}
|
||||
</div>
|
||||
<div className="min-w-0 text-left">
|
||||
<div className="truncate font-medium text-sm">{primaryText}</div>
|
||||
<div className="text-muted-foreground text-xs">{secondaryText}</div>
|
||||
<div className="text-gray-500 text-xs">{secondaryText}</div>
|
||||
</div>
|
||||
{actionSlot ?? (
|
||||
<div
|
||||
|
||||
@@ -31,7 +31,7 @@ export const TemplateDirectLinkBadge = ({ token, enabled, className }: TemplateD
|
||||
<button
|
||||
title="Copy direct link"
|
||||
className={cn(
|
||||
'flex flex-row items-center rounded border border-border bg-muted px-1.5 py-0.5 text-xs',
|
||||
'flex flex-row items-center rounded border border-neutral-300 bg-neutral-200 px-1.5 py-0.5 text-xs dark:border-neutral-500 dark:bg-neutral-600',
|
||||
className,
|
||||
)}
|
||||
onClick={async () => onCopyClick(token)}
|
||||
|
||||
@@ -71,7 +71,7 @@ export const TemplatePageViewRecentActivity = ({
|
||||
</div>
|
||||
|
||||
<div className="relative flex h-6 w-6 flex-none items-center justify-center bg-widget">
|
||||
<div className="h-1.5 w-1.5 rounded-full bg-widget ring-1 ring-border" />
|
||||
<div className="h-1.5 w-1.5 rounded-full bg-widget ring-1 ring-gray-300 dark:ring-neutral-600" />
|
||||
</div>
|
||||
|
||||
<button
|
||||
@@ -108,7 +108,7 @@ export const TemplatePageViewRecentActivity = ({
|
||||
</div>
|
||||
|
||||
<div className="relative flex h-6 w-6 flex-none items-center justify-center bg-widget text-foreground/40">
|
||||
<div className="h-1.5 w-1.5 rounded-full bg-widget ring-1 ring-border" />
|
||||
<div className="h-1.5 w-1.5 rounded-full bg-widget ring-1 ring-gray-300 dark:ring-neutral-600" />
|
||||
</div>
|
||||
|
||||
<Link
|
||||
|
||||
@@ -127,8 +127,8 @@ export const AdminDashboardUsersTable = ({ users, totalPages, perPage, page }: A
|
||||
</DataTable>
|
||||
|
||||
{isPending && (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-background/50">
|
||||
<Loader className="h-8 w-8 animate-spin text-muted-foreground" />
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-white/50">
|
||||
<Loader className="h-8 w-8 animate-spin text-gray-500" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -204,8 +204,8 @@ export const AdminOrganisationOverviewTable = ({
|
||||
</DataTable>
|
||||
|
||||
{isPending && (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-background/50">
|
||||
<Loader className="h-8 w-8 animate-spin text-muted-foreground" />
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-white/50">
|
||||
<Loader className="h-8 w-8 animate-spin text-gray-500" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -110,7 +110,7 @@ export const AdminOrganisationsTable = ({
|
||||
return (
|
||||
<div
|
||||
className={`inline-flex items-center rounded-full px-2 py-1 font-medium text-xs ${
|
||||
isPaid ? 'bg-green-100 text-green-800' : 'bg-muted text-muted-foreground'
|
||||
isPaid ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'
|
||||
}`}
|
||||
>
|
||||
{isPaid ? (
|
||||
|
||||
@@ -253,8 +253,8 @@ export const OrganisationInsightsTable = ({
|
||||
</div>
|
||||
|
||||
{isLoading && (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-background/50">
|
||||
<Loader className="h-8 w-8 animate-spin text-muted-foreground" />
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-white/50">
|
||||
<Loader className="h-8 w-8 animate-spin text-gray-500" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -72,7 +72,7 @@ export const SettingsPublicProfileTemplatesTable = () => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mt-6 divide-y divide-border overflow-hidden rounded-lg border border-border">
|
||||
<div className="mt-6 divide-y divide-neutral-200 overflow-hidden rounded-lg border border-neutral-200 dark:divide-foreground/30 dark:border-foreground/30">
|
||||
{/* Loading and error handling states. */}
|
||||
{publicDirectTemplates.length === 0 && (
|
||||
<>
|
||||
@@ -132,7 +132,7 @@ export const SettingsPublicProfileTemplatesTable = () => {
|
||||
|
||||
<div>
|
||||
<p className="break-all text-sm">{template.publicTitle}</p>
|
||||
<p className="break-all text-muted-foreground text-xs">{template.publicDescription}</p>
|
||||
<p className="break-all text-neutral-400 text-xs">{template.publicDescription}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ export const TemplatesTable = ({
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<div className="mb-2 flex w-fit flex-row items-center rounded border border-border bg-muted px-1.5 py-0.5 text-xs">
|
||||
<div className="mb-2 flex w-fit flex-row items-center rounded border border-neutral-300 bg-neutral-200 px-1.5 py-0.5 text-xs dark:border-neutral-500 dark:bg-neutral-600">
|
||||
<Link2Icon className="mr-1 h-3 w-3" />
|
||||
<Trans>direct link</Trans>
|
||||
</div>
|
||||
@@ -310,8 +310,8 @@ export const TemplatesTable = ({
|
||||
</DataTable>
|
||||
|
||||
{isPending && (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-background/50">
|
||||
<Loader className="h-8 w-8 animate-spin text-muted-foreground" />
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-white/50">
|
||||
<Loader className="h-8 w-8 animate-spin text-gray-500" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -149,8 +149,8 @@ export default function AdminDocumentsPage() {
|
||||
</DataTable>
|
||||
|
||||
{isFindDocumentsLoading && (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-background/50">
|
||||
<Loader className="h-8 w-8 animate-spin text-muted-foreground" />
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-white/50">
|
||||
<Loader className="h-8 w-8 animate-spin text-gray-500" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -182,8 +182,8 @@ export default function AdminEmailDomainsPage() {
|
||||
</DataTable>
|
||||
|
||||
{isFindEmailDomainsLoading && (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-background/50">
|
||||
<Loader className="h-8 w-8 animate-spin text-muted-foreground" />
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-white/50">
|
||||
<Loader className="h-8 w-8 animate-spin text-gray-500" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -173,8 +173,8 @@ export default function AdminUnsealedDocumentsPage() {
|
||||
</DataTable>
|
||||
|
||||
{isLoading && (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-background/50">
|
||||
<Loader className="h-8 w-8 animate-spin text-muted-foreground" />
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-white/50">
|
||||
<Loader className="h-8 w-8 animate-spin text-gray-500" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -108,7 +108,7 @@ export default function DashboardPage() {
|
||||
<div className="flex items-center gap-3">
|
||||
<Avatar className="h-10 w-10 border border-solid">
|
||||
{org.avatarImageId && <AvatarImage src={formatAvatarUrl(org.avatarImageId)} />}
|
||||
<AvatarFallback className="text-muted-foreground text-sm">
|
||||
<AvatarFallback className="text-gray-400 text-sm">
|
||||
{org.name.slice(0, 1).toUpperCase()}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
@@ -182,7 +182,7 @@ export default function DashboardPage() {
|
||||
<div className="flex items-center gap-3">
|
||||
<Avatar className="h-10 w-10 border border-solid">
|
||||
{team.avatarImageId && <AvatarImage src={formatAvatarUrl(team.avatarImageId)} />}
|
||||
<AvatarFallback className="text-muted-foreground text-sm">
|
||||
<AvatarFallback className="text-gray-400 text-sm">
|
||||
{team.name.slice(0, 1).toUpperCase()}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
|
||||
@@ -129,7 +129,7 @@ export default function OrganisationSettingsTeamsPage() {
|
||||
<div className="flex items-center gap-3">
|
||||
<Avatar className="h-10 w-10 border-2 border-solid">
|
||||
{team.avatarImageId && <AvatarImage src={formatAvatarUrl(team.avatarImageId)} />}
|
||||
<AvatarFallback className="text-muted-foreground text-sm">
|
||||
<AvatarFallback className="text-gray-400 text-sm">
|
||||
{team.name.slice(0, 1).toUpperCase()}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
|
||||
@@ -95,8 +95,8 @@ export default function WebhookPage() {
|
||||
<WebhookCreateDialog />
|
||||
</SettingsHeader>
|
||||
{isLoading && (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-background/50">
|
||||
<Loader className="h-8 w-8 animate-spin text-muted-foreground" />
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-white/50">
|
||||
<Loader className="h-8 w-8 animate-spin text-gray-500" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -59,9 +59,7 @@ export default function PublicProfilePage({ loaderData }: Route.ComponentProps)
|
||||
<Avatar className="h-24 w-24 border-2 border-solid dark:border-border">
|
||||
{publicProfile.avatarImageId && <AvatarImage src={formatAvatarUrl(publicProfile.avatarImageId)} />}
|
||||
|
||||
<AvatarFallback className="text-muted-foreground text-sm">
|
||||
{extractInitials(publicProfile.name)}
|
||||
</AvatarFallback>
|
||||
<AvatarFallback className="text-gray-400 text-sm">{extractInitials(publicProfile.name)}</AvatarFallback>
|
||||
</Avatar>
|
||||
|
||||
<div className="mt-4 flex flex-row items-center justify-center">
|
||||
@@ -140,7 +138,7 @@ export default function PublicProfilePage({ loaderData }: Route.ComponentProps)
|
||||
<Table className="w-full" overflowHidden>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-full rounded-tl-md bg-muted">
|
||||
<TableHead className="w-full rounded-tl-md bg-neutral-50 dark:bg-neutral-700">
|
||||
<Trans>Documents</Trans>
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
|
||||
@@ -36,8 +36,8 @@
|
||||
"@lingui/react": "^5.6.0",
|
||||
"@oslojs/crypto": "^1.0.1",
|
||||
"@oslojs/encoding": "^1.1.0",
|
||||
"@react-router/node": "^7.12.0",
|
||||
"@react-router/serve": "^7.12.0",
|
||||
"@react-router/node": "^7.18.1",
|
||||
"@react-router/serve": "^7.18.1",
|
||||
"@simplewebauthn/browser": "^13.2.2",
|
||||
"@simplewebauthn/server": "^13.2.2",
|
||||
"@tanstack/react-query": "5.90.10",
|
||||
@@ -81,8 +81,8 @@
|
||||
"@babel/preset-typescript": "^7.28.5",
|
||||
"@lingui/babel-plugin-lingui-macro": "^5.6.0",
|
||||
"@lingui/vite-plugin": "^5.6.0",
|
||||
"@react-router/dev": "^7.12.0",
|
||||
"@react-router/remix-routes-option-adapter": "^7.12.0",
|
||||
"@react-router/dev": "^7.18.1",
|
||||
"@react-router/remix-routes-option-adapter": "^7.18.1",
|
||||
"@rollup/plugin-babel": "^6.1.0",
|
||||
"@rollup/plugin-commonjs": "^28.0.9",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
|
||||
Generated
+2048
-1264
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@ const alertVariants = cva(
|
||||
variants: {
|
||||
variant: {
|
||||
default: 'bg-green-50 text-green-700 [&_.alert-title]:text-green-800 [&>svg]:text-green-400',
|
||||
neutral: 'bg-muted/50 text-muted-foreground [&_.alert-title]:text-foreground',
|
||||
neutral: 'bg-gray-50 dark:bg-neutral-900/20 text-muted-foreground [&_.alert-title]:text-foreground',
|
||||
secondary: 'bg-blue-50 text-blue-700 [&_.alert-title]:text-blue-800 [&>svg]:text-blue-400',
|
||||
destructive: 'bg-red-50 text-red-700 [&_.alert-title]:text-red-800 [&>svg]:text-red-400',
|
||||
warning: 'bg-yellow-50 text-yellow-700 [&_.alert-title]:text-yellow-800 [&>svg]:text-yellow-400',
|
||||
|
||||
@@ -63,7 +63,7 @@ const AvatarWithText = ({
|
||||
<div className={cn('flex w-full max-w-xs items-center gap-2', className)}>
|
||||
<Avatar className={cn('h-10 w-10 border-2 border-white border-solid dark:border-border', avatarClass)}>
|
||||
{avatarSrc && <AvatarImage src={avatarSrc} />}
|
||||
<AvatarFallback className="text-muted-foreground text-xs">{avatarFallback}</AvatarFallback>
|
||||
<AvatarFallback className="text-gray-400 text-xs">{avatarFallback}</AvatarFallback>
|
||||
</Avatar>
|
||||
|
||||
<div className={cn('flex flex-col truncate text-left font-normal text-sm', textSectionClassName)}>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { cn } from '../lib/utils';
|
||||
const badgeVariants = cva('inline-flex items-center rounded-md text-xs font-medium ring-1 ring-inset w-fit', {
|
||||
variants: {
|
||||
variant: {
|
||||
neutral: 'bg-muted text-muted-foreground ring-border',
|
||||
neutral: 'bg-gray-50 text-gray-600 ring-gray-500/10 dark:bg-gray-400/10 dark:text-gray-400 dark:ring-gray-400/20',
|
||||
destructive: 'bg-red-50 text-red-700 ring-red-600/10 dark:bg-red-400/10 dark:text-red-400 dark:ring-red-400/20',
|
||||
warning:
|
||||
'bg-yellow-50 text-yellow-800 ring-yellow-600/20 dark:bg-yellow-400/10 dark:text-yellow-500 dark:ring-yellow-400/20',
|
||||
|
||||
@@ -62,7 +62,7 @@ const CommandInput = React.forwardRef<
|
||||
<CommandPrimitive.Input
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50',
|
||||
'flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-foreground-muted disabled:cursor-not-allowed disabled:opacity-50',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -327,7 +327,7 @@ export const AddSettingsFormPartial = ({
|
||||
|
||||
<Accordion type="multiple" className="mt-6">
|
||||
<AccordionItem value="advanced-options" className="border-none">
|
||||
<AccordionTrigger className="mb-2 rounded border px-3 py-2 text-left text-foreground hover:bg-muted/50 hover:no-underline">
|
||||
<AccordionTrigger className="mb-2 rounded border px-3 py-2 text-left text-foreground hover:bg-neutral-200/30 hover:no-underline">
|
||||
<Trans>Advanced Options</Trans>
|
||||
</AccordionTrigger>
|
||||
|
||||
|
||||
@@ -273,7 +273,7 @@ const FieldItemInner = ({
|
||||
>
|
||||
{(field.type === FieldType.RADIO || field.type === FieldType.CHECKBOX) && field.fieldMeta?.label && (
|
||||
<div
|
||||
className={cn('absolute -top-16 right-0 left-0 rounded-md p-2 text-center text-foreground text-xs', {
|
||||
className={cn('absolute -top-16 right-0 left-0 rounded-md p-2 text-center text-gray-700 text-xs', {
|
||||
'border border-primary bg-foreground/5': !fieldHasCheckedValues,
|
||||
'border border-primary bg-documenso-200': fieldHasCheckedValues,
|
||||
})}
|
||||
|
||||
@@ -454,7 +454,7 @@ export const AddTemplateSettingsFormPartial = ({
|
||||
{distributionMethod === DocumentDistributionMethod.EMAIL && (
|
||||
<Accordion type="multiple">
|
||||
<AccordionItem value="email-options" className="border-none">
|
||||
<AccordionTrigger className="rounded border px-3 py-2 text-left text-foreground hover:bg-muted/50 hover:no-underline">
|
||||
<AccordionTrigger className="rounded border px-3 py-2 text-left text-foreground hover:bg-neutral-200/30 hover:no-underline">
|
||||
<Trans>Email Options</Trans>
|
||||
</AccordionTrigger>
|
||||
|
||||
@@ -588,7 +588,7 @@ export const AddTemplateSettingsFormPartial = ({
|
||||
|
||||
<Accordion type="multiple">
|
||||
<AccordionItem value="advanced-options" className="border-none">
|
||||
<AccordionTrigger className="rounded border px-3 py-2 text-left text-foreground hover:bg-muted/50 hover:no-underline">
|
||||
<AccordionTrigger className="rounded border px-3 py-2 text-left text-foreground hover:bg-neutral-200/30 hover:no-underline">
|
||||
<Trans>Advanced Options</Trans>
|
||||
</AccordionTrigger>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user