Sign Document
diff --git a/apps/web/src/app/(signing)/sign/[token]/name-field.tsx b/apps/web/src/app/(signing)/sign/[token]/name-field.tsx
index bbe18fb8a..6e661e77a 100644
--- a/apps/web/src/app/(signing)/sign/[token]/name-field.tsx
+++ b/apps/web/src/app/(signing)/sign/[token]/name-field.tsx
@@ -6,8 +6,8 @@ import { useRouter } from 'next/navigation';
import { Loader } from 'lucide-react';
-import { Recipient } from '@documenso/prisma/client';
-import { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
+import type { Recipient } from '@documenso/prisma/client';
+import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
import { trpc } from '@documenso/trpc/react';
import { Button } from '@documenso/ui/primitives/button';
import { Dialog, DialogContent, DialogFooter, DialogTitle } from '@documenso/ui/primitives/dialog';
@@ -98,7 +98,7 @@ export const NameField = ({ field, recipient }: NameFieldProps) => {
};
return (
-
+
{isLoading && (
diff --git a/apps/web/src/app/(signing)/sign/[token]/page.tsx b/apps/web/src/app/(signing)/sign/[token]/page.tsx
index 97babb82f..efd0b266c 100644
--- a/apps/web/src/app/(signing)/sign/[token]/page.tsx
+++ b/apps/web/src/app/(signing)/sign/[token]/page.tsx
@@ -2,9 +2,12 @@ import { notFound, redirect } from 'next/navigation';
import { match } from 'ts-pattern';
+import { DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
+import { DEFAULT_DOCUMENT_TIME_ZONE } from '@documenso/lib/constants/time-zones';
import { getServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
+import { getDocumentMetaByDocumentId } from '@documenso/lib/server-only/document/get-document-meta-by-document-id';
import { viewedDocument } from '@documenso/lib/server-only/document/viewed-document';
import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-for-token';
import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token';
@@ -14,6 +17,8 @@ import { Card, CardContent } from '@documenso/ui/primitives/card';
import { ElementVisible } from '@documenso/ui/primitives/element-visible';
import { LazyPDFViewer } from '@documenso/ui/primitives/lazy-pdf-viewer';
+import { truncateTitle } from '~/helpers/truncate-title';
+
import { DateField } from './date-field';
import { EmailField } from './email-field';
import { SigningForm } from './form';
@@ -42,10 +47,14 @@ export default async function SigningPage({ params: { token } }: SigningPageProp
viewedDocument({ token }).catch(() => null),
]);
+ const documentMeta = await getDocumentMetaByDocumentId({ id: document!.id }).catch(() => null);
+
if (!document || !document.documentData || !recipient) {
return notFound();
}
+ const truncatedTitle = truncateTitle(document.title);
+
const { documentData } = document;
const { user } = await getServerComponentSession();
@@ -77,7 +86,7 @@ export default async function SigningPage({ params: { token } }: SigningPageProp
>
- {document.title}
+ {truncatedTitle}
@@ -111,7 +120,13 @@ export default async function SigningPage({ params: { token } }: SigningPageProp
))
.with(FieldType.DATE, () => (
-
+
))
.with(FieldType.EMAIL, () => (
diff --git a/apps/web/src/app/(signing)/sign/[token]/sign-dialog.tsx b/apps/web/src/app/(signing)/sign/[token]/sign-dialog.tsx
index 0ce750a39..faecf5d7e 100644
--- a/apps/web/src/app/(signing)/sign/[token]/sign-dialog.tsx
+++ b/apps/web/src/app/(signing)/sign/[token]/sign-dialog.tsx
@@ -1,6 +1,6 @@
import { useState } from 'react';
-import { Document, Field } from '@documenso/prisma/client';
+import type { Document, Field } from '@documenso/prisma/client';
import { Button } from '@documenso/ui/primitives/button';
import {
Dialog,
@@ -9,6 +9,8 @@ import {
DialogTrigger,
} from '@documenso/ui/primitives/dialog';
+import { truncateTitle } from '~/helpers/truncate-title';
+
export type SignDialogProps = {
isSubmitting: boolean;
document: Document;
@@ -23,7 +25,7 @@ export const SignDialog = ({
onSignatureComplete,
}: SignDialogProps) => {
const [showDialog, setShowDialog] = useState(false);
-
+ const truncatedTitle = truncateTitle(document.title);
const isComplete = fields.every((field) => field.inserted);
return (
@@ -43,7 +45,7 @@ export const SignDialog = ({
Sign Document
- You are about to finish signing "{document.title}". Are you sure?
+ You are about to finish signing "{truncatedTitle}". Are you sure?
diff --git a/apps/web/src/app/(signing)/sign/[token]/signature-field.tsx b/apps/web/src/app/(signing)/sign/[token]/signature-field.tsx
index ec3e45fe5..220d3084a 100644
--- a/apps/web/src/app/(signing)/sign/[token]/signature-field.tsx
+++ b/apps/web/src/app/(signing)/sign/[token]/signature-field.tsx
@@ -127,7 +127,7 @@ export const SignatureField = ({ field, recipient }: SignatureFieldProps) => {
};
return (
-
+
{isLoading && (
diff --git a/apps/web/src/app/(signing)/sign/[token]/signing-field-container.tsx b/apps/web/src/app/(signing)/sign/[token]/signing-field-container.tsx
index 046e5b3df..b4805fa6b 100644
--- a/apps/web/src/app/(signing)/sign/[token]/signing-field-container.tsx
+++ b/apps/web/src/app/(signing)/sign/[token]/signing-field-container.tsx
@@ -2,8 +2,9 @@
import React from 'react';
-import { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
+import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
import { FieldRootContainer } from '@documenso/ui/components/field/field';
+import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip';
export type SignatureFieldProps = {
field: FieldWithSignature;
@@ -11,6 +12,8 @@ export type SignatureFieldProps = {
children: React.ReactNode;
onSign?: () => Promise
| void;
onRemove?: () => Promise | void;
+ type?: 'Date' | 'Email' | 'Name' | 'Signature';
+ tooltipText?: string | null;
};
export const SigningFieldContainer = ({
@@ -19,6 +22,8 @@ export const SigningFieldContainer = ({
onSign,
onRemove,
children,
+ type,
+ tooltipText,
}: SignatureFieldProps) => {
const onSignFieldClick = async () => {
if (field.inserted) {
@@ -46,7 +51,22 @@ export const SigningFieldContainer = ({
/>
)}
- {field.inserted && !loading && (
+ {type === 'Date' && field.inserted && !loading && (
+
+
+
+
+
+ {tooltipText && {tooltipText}}
+
+ )}
+
+ {type !== 'Date' && field.inserted && !loading && (
-
+
+
-
+
+
No value found.
-
- {allRoles.map((value: string, i: number) => (
- handleSelect(value)}>
+
+
+ {options.map((option, index) => (
+ onOptionSelected(option)}>
- {value}
+
+ {option}
))}
diff --git a/packages/ui/primitives/command.tsx b/packages/ui/primitives/command.tsx
index 67cd3f487..cbc306c66 100644
--- a/packages/ui/primitives/command.tsx
+++ b/packages/ui/primitives/command.tsx
@@ -2,7 +2,7 @@
import * as React from 'react';
-import { DialogProps } from '@radix-ui/react-dialog';
+import type { DialogProps } from '@radix-ui/react-dialog';
import { Command as CommandPrimitive } from 'cmdk';
import { Search } from 'lucide-react';
diff --git a/packages/ui/primitives/constants.ts b/packages/ui/primitives/constants.ts
new file mode 100644
index 000000000..9771eb35a
--- /dev/null
+++ b/packages/ui/primitives/constants.ts
@@ -0,0 +1,5 @@
+export const THEMES_TYPE = {
+ DARK: 'dark',
+ LIGHT: 'light',
+ SYSTEM: 'system'
+};
\ No newline at end of file
diff --git a/packages/ui/primitives/dialog.tsx b/packages/ui/primitives/dialog.tsx
index 8e5ed20e5..47982ab09 100644
--- a/packages/ui/primitives/dialog.tsx
+++ b/packages/ui/primitives/dialog.tsx
@@ -20,7 +20,7 @@ const DialogPortal = ({
}: DialogPrimitive.DialogPortalProps & { position?: 'start' | 'end' | 'center' }) => (
void | Promise
;
type?: 'document' | 'template';
[key: string]: unknown;
@@ -96,6 +97,7 @@ export const DocumentDropzone = ({
className,
onDrop,
disabled,
+ disabledMessage = 'You cannot upload documents at this time.',
type = 'document',
...props
}: DocumentDropzoneProps) => {
@@ -115,11 +117,12 @@ export const DocumentDropzone = ({
return (
*/}
@@ -147,7 +150,7 @@ export const DocumentDropzone = ({
@@ -171,7 +174,9 @@ export const DocumentDropzone = ({
{DocumentDescription[type].headline}
- Drag & drop your document here.
+
+ {disabled ? disabledMessage : 'Drag & drop your document here.'}
+
diff --git a/packages/ui/primitives/document-flow/add-signature.tsx b/packages/ui/primitives/document-flow/add-signature.tsx
index e4e5d9253..5accdca16 100644
--- a/packages/ui/primitives/document-flow/add-signature.tsx
+++ b/packages/ui/primitives/document-flow/add-signature.tsx
@@ -7,11 +7,13 @@ import { DateTime } from 'luxon';
import { useForm } from 'react-hook-form';
import { match } from 'ts-pattern';
+import { DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { sortFieldsByPosition, validateFieldsInserted } from '@documenso/lib/utils/fields';
import type { Field } from '@documenso/prisma/client';
import { FieldType } from '@documenso/prisma/client';
import type { FieldWithSignature } from '@documenso/prisma/types/field-with-signature';
+import type { DocumentFlowStep } from '@documenso/ui/primitives/document-flow/types';
import { FieldToolTip } from '../../components/field/field-tooltip';
import { cn } from '../../lib/utils';
@@ -34,7 +36,6 @@ import {
SinglePlayerModeCustomTextField,
SinglePlayerModeSignatureField,
} from './single-player-mode-fields';
-import type { DocumentFlowStep } from './types';
export type AddSignatureFormProps = {
defaultValues?: TAddSignatureFormSchema;
@@ -140,7 +141,7 @@ export const AddSignatureFormPartial = ({
return match(field.type)
.with(FieldType.DATE, () => ({
...field,
- customText: DateTime.now().toFormat('yyyy-MM-dd hh:mm a'),
+ customText: DateTime.now().toFormat(DEFAULT_DOCUMENT_DATE_FORMAT),
inserted: true,
}))
.with(FieldType.EMAIL, () => ({
diff --git a/packages/ui/primitives/document-flow/add-subject.tsx b/packages/ui/primitives/document-flow/add-subject.tsx
index 881d59c74..d73019732 100644
--- a/packages/ui/primitives/document-flow/add-subject.tsx
+++ b/packages/ui/primitives/document-flow/add-subject.tsx
@@ -1,11 +1,30 @@
'use client';
-import { useForm } from 'react-hook-form';
+import { useEffect } from 'react';
+import { Controller, useForm } from 'react-hook-form';
+
+import { DATE_FORMATS, DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
+import { DEFAULT_DOCUMENT_TIME_ZONE, TIME_ZONES } from '@documenso/lib/constants/time-zones';
import type { Field, Recipient } from '@documenso/prisma/client';
import { DocumentStatus } from '@documenso/prisma/client';
+import { SendStatus } from '@documenso/prisma/client';
import type { DocumentWithData } from '@documenso/prisma/types/document-with-data';
+import {
+ Accordion,
+ AccordionContent,
+ AccordionItem,
+ AccordionTrigger,
+} from '@documenso/ui/primitives/accordion';
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from '@documenso/ui/primitives/select';
+import { Combobox } from '../combobox';
import { FormErrorMessage } from '../form/form-error-message';
import { Input } from '../input';
import { Label } from '../label';
@@ -31,20 +50,25 @@ export type AddSubjectFormProps = {
export const AddSubjectFormPartial = ({
documentFlow,
- recipients: _recipients,
- fields: _fields,
+ recipients: recipients,
+ fields: fields,
document,
onSubmit,
}: AddSubjectFormProps) => {
const {
+ control,
register,
handleSubmit,
- formState: { errors, isSubmitting },
+ formState: { errors, isSubmitting, touchedFields },
+ getValues,
+ setValue,
} = useForm({
defaultValues: {
- email: {
+ meta: {
subject: document.documentMeta?.subject ?? '',
message: document.documentMeta?.message ?? '',
+ timezone: document.documentMeta?.timezone ?? DEFAULT_DOCUMENT_TIME_ZONE,
+ dateFormat: document.documentMeta?.dateFormat ?? DEFAULT_DOCUMENT_DATE_FORMAT,
},
},
});
@@ -52,6 +76,20 @@ export const AddSubjectFormPartial = ({
const onFormSubmit = handleSubmit(onSubmit);
const { currentStep, totalSteps, previousStep } = useStep();
+ const hasDateField = fields.find((field) => field.type === 'DATE');
+
+ const documentHasBeenSent = recipients.some(
+ (recipient) => recipient.sendStatus === SendStatus.SENT,
+ );
+
+ // We almost always want to set the timezone to the user's local timezone to avoid confusion
+ // when the document is signed.
+ useEffect(() => {
+ if (!touchedFields.meta?.timezone && !documentHasBeenSent) {
+ setValue('meta.timezone', Intl.DateTimeFormat().resolvedOptions().timeZone);
+ }
+ }, [documentHasBeenSent, setValue, touchedFields.meta?.timezone]);
+
return (
<>
-
+
@@ -86,14 +124,12 @@ export const AddSubjectFormPartial = ({
id="message"
className="bg-background mt-2 h-32 resize-none"
disabled={isSubmitting}
- {...register('email.message')}
+ {...register('meta.message')}
/>
@@ -123,6 +159,67 @@ export const AddSubjectFormPartial = ({
+
+
+
+
+ Advanced Options
+
+
+
+ {hasDateField && (
+
+
+
+ (
+
+ )}
+ />
+
+ )}
+
+ {hasDateField && (
+
+
+
+ (
+ value && onChange(value)}
+ disabled={documentHasBeenSent}
+ />
+ )}
+ />
+
+ )}
+
+
+
diff --git a/packages/ui/primitives/document-flow/add-subject.types.ts b/packages/ui/primitives/document-flow/add-subject.types.ts
index 33e2dedfb..ea14f4c0f 100644
--- a/packages/ui/primitives/document-flow/add-subject.types.ts
+++ b/packages/ui/primitives/document-flow/add-subject.types.ts
@@ -1,9 +1,14 @@
import { z } from 'zod';
+import { DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats';
+import { DEFAULT_DOCUMENT_TIME_ZONE } from '@documenso/lib/constants/time-zones';
+
export const ZAddSubjectFormSchema = z.object({
- email: z.object({
+ meta: z.object({
subject: z.string(),
message: z.string(),
+ timezone: z.string().optional().default(DEFAULT_DOCUMENT_TIME_ZONE),
+ dateFormat: z.string().optional().default(DEFAULT_DOCUMENT_DATE_FORMAT),
}),
});
diff --git a/packages/ui/primitives/document-flow/add-title.tsx b/packages/ui/primitives/document-flow/add-title.tsx
index 8c2a9dc7a..afce0d9e0 100644
--- a/packages/ui/primitives/document-flow/add-title.tsx
+++ b/packages/ui/primitives/document-flow/add-title.tsx
@@ -64,7 +64,7 @@ export const AddTitleFormPartial = ({
diff --git a/packages/ui/primitives/document-flow/document-flow-root.tsx b/packages/ui/primitives/document-flow/document-flow-root.tsx
index 42b70c58a..74a232e1d 100644
--- a/packages/ui/primitives/document-flow/document-flow-root.tsx
+++ b/packages/ui/primitives/document-flow/document-flow-root.tsx
@@ -22,12 +22,12 @@ export const DocumentFlowFormContainer = ({
);
};
@@ -63,10 +63,7 @@ export const DocumentFlowFormContainerContent = ({
}: DocumentFlowFormContainerContentProps) => {
return (
{children}
diff --git a/packages/ui/primitives/multiselect-combobox.tsx b/packages/ui/primitives/multiselect-combobox.tsx
new file mode 100644
index 000000000..bac87ce0b
--- /dev/null
+++ b/packages/ui/primitives/multiselect-combobox.tsx
@@ -0,0 +1,82 @@
+import * as React from 'react';
+
+import { Check, ChevronsUpDown } from 'lucide-react';
+
+import { Role } from '@documenso/prisma/client';
+import { cn } from '@documenso/ui/lib/utils';
+import { Button } from '@documenso/ui/primitives/button';
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+} from '@documenso/ui/primitives/command';
+import { Popover, PopoverContent, PopoverTrigger } from '@documenso/ui/primitives/popover';
+
+type ComboboxProps = {
+ listValues: string[];
+ onChange: (_values: string[]) => void;
+};
+
+const MultiSelectCombobox = ({ listValues, onChange }: ComboboxProps) => {
+ const [open, setOpen] = React.useState(false);
+ const [selectedValues, setSelectedValues] = React.useState
([]);
+ const dbRoles = Object.values(Role);
+
+ React.useEffect(() => {
+ setSelectedValues(listValues);
+ }, [listValues]);
+
+ const allRoles = [...new Set([...dbRoles, ...selectedValues])];
+
+ const handleSelect = (currentValue: string) => {
+ let newSelectedValues;
+ if (selectedValues.includes(currentValue)) {
+ newSelectedValues = selectedValues.filter((value) => value !== currentValue);
+ } else {
+ newSelectedValues = [...selectedValues, currentValue];
+ }
+
+ setSelectedValues(newSelectedValues);
+ onChange(newSelectedValues);
+ setOpen(false);
+ };
+
+ return (
+
+
+
+
+
+
+
+ No value found.
+
+ {allRoles.map((value: string, i: number) => (
+ handleSelect(value)}>
+
+ {value}
+
+ ))}
+
+
+
+
+ );
+};
+
+export { MultiSelectCombobox };
diff --git a/packages/ui/primitives/select.tsx b/packages/ui/primitives/select.tsx
index 0d4789550..fba05f7ef 100644
--- a/packages/ui/primitives/select.tsx
+++ b/packages/ui/primitives/select.tsx
@@ -42,7 +42,7 @@ const SelectContent = React.forwardRef<
(null);
const [isPressed, setIsPressed] = useState(false);
- const [points, setPoints] = useState([]);
+ const [lines, setLines] = useState([]);
+ const [currentLine, setCurrentLine] = useState([]);
const perfectFreehandOptions = useMemo(() => {
const size = $el.current ? Math.min($el.current.height, $el.current.width) * 0.03 : 10;
@@ -52,26 +54,7 @@ export const SignaturePad = ({
const point = Point.fromEvent(event, DPI, $el.current);
- const newPoints = [...points, point];
-
- setPoints(newPoints);
-
- if ($el.current) {
- const ctx = $el.current.getContext('2d');
-
- if (ctx) {
- ctx.save();
-
- ctx.imageSmoothingEnabled = true;
- ctx.imageSmoothingQuality = 'high';
-
- const pathData = new Path2D(
- getSvgPathFromStroke(getStroke(newPoints, perfectFreehandOptions)),
- );
-
- ctx.fill(pathData);
- }
- }
+ setCurrentLine([point]);
};
const onMouseMove = (event: MouseEvent | PointerEvent | TouchEvent) => {
@@ -85,31 +68,36 @@ export const SignaturePad = ({
const point = Point.fromEvent(event, DPI, $el.current);
- if (point.distanceTo(points[points.length - 1]) > 5) {
- const newPoints = [...points, point];
-
- setPoints(newPoints);
+ if (point.distanceTo(currentLine[currentLine.length - 1]) > 5) {
+ setCurrentLine([...currentLine, point]);
+ // Update the canvas here to draw the lines
if ($el.current) {
const ctx = $el.current.getContext('2d');
if (ctx) {
ctx.restore();
-
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
- const pathData = new Path2D(
- getSvgPathFromStroke(getStroke(points, perfectFreehandOptions)),
- );
+ lines.forEach((line) => {
+ const pathData = new Path2D(
+ getSvgPathFromStroke(getStroke(line, perfectFreehandOptions)),
+ );
+ ctx.fill(pathData);
+ });
+
+ const pathData = new Path2D(
+ getSvgPathFromStroke(getStroke([...currentLine, point], perfectFreehandOptions)),
+ );
ctx.fill(pathData);
}
}
}
};
- const onMouseUp = (event: MouseEvent | PointerEvent | TouchEvent, addPoint = true) => {
+ const onMouseUp = (event: MouseEvent | PointerEvent | TouchEvent, addLine = true) => {
if (event.cancelable) {
event.preventDefault();
}
@@ -118,15 +106,16 @@ export const SignaturePad = ({
const point = Point.fromEvent(event, DPI, $el.current);
- const newPoints = [...points];
+ const newLines = [...lines];
- if (addPoint) {
- newPoints.push(point);
-
- setPoints(newPoints);
+ if (addLine && currentLine.length > 0) {
+ newLines.push([...currentLine, point]);
+ setCurrentLine([]);
}
- if ($el.current && newPoints.length > 0) {
+ setLines(newLines);
+
+ if ($el.current && newLines.length > 0) {
const ctx = $el.current.getContext('2d');
if (ctx) {
@@ -135,19 +124,18 @@ export const SignaturePad = ({
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
- const pathData = new Path2D(
- getSvgPathFromStroke(getStroke(newPoints, perfectFreehandOptions)),
- );
+ newLines.forEach((line) => {
+ const pathData = new Path2D(
+ getSvgPathFromStroke(getStroke(line, perfectFreehandOptions)),
+ );
+ ctx.fill(pathData);
+ });
- ctx.fill(pathData);
+ onChange?.($el.current.toDataURL());
ctx.save();
}
-
- onChange?.($el.current.toDataURL());
}
-
- setPoints([]);
};
const onMouseEnter = (event: MouseEvent | PointerEvent | TouchEvent) => {
@@ -177,7 +165,29 @@ export const SignaturePad = ({
onChange?.(null);
- setPoints([]);
+ setLines([]);
+ setCurrentLine([]);
+ };
+
+ const onUndoClick = () => {
+ if (lines.length === 0) {
+ return;
+ }
+
+ const newLines = [...lines];
+ newLines.pop(); // Remove the last line
+ setLines(newLines);
+
+ // Clear the canvas
+ if ($el.current) {
+ const ctx = $el.current.getContext('2d');
+ ctx?.clearRect(0, 0, $el.current.width, $el.current.height);
+
+ newLines.forEach((line) => {
+ const pathData = new Path2D(getSvgPathFromStroke(getStroke(line, perfectFreehandOptions)));
+ ctx?.fill(pathData);
+ });
+ }
};
useEffect(() => {
@@ -217,15 +227,29 @@ export const SignaturePad = ({
{...props}
/>
-
+
+
+ {lines.length > 0 && (
+
+
+
+ )}
);
};
diff --git a/packages/ui/primitives/theme-switcher.tsx b/packages/ui/primitives/theme-switcher.tsx
index 7aa570749..fcc789404 100644
--- a/packages/ui/primitives/theme-switcher.tsx
+++ b/packages/ui/primitives/theme-switcher.tsx
@@ -4,6 +4,8 @@ import { useTheme } from 'next-themes';
import { useIsMounted } from '@documenso/lib/client-only/hooks/use-is-mounted';
+import { THEMES_TYPE } from './constants';
+
export const ThemeSwitcher = () => {
const { theme, setTheme } = useTheme();
const isMounted = useIsMounted();
@@ -12,9 +14,9 @@ export const ThemeSwitcher = () => {