Compare commits
37 Commits
docs/rende
...
feat/marke
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf84844d9b | ||
|
|
37a2ae87a6 | ||
|
|
f964cda8ec | ||
|
|
2ea5ff2c94 | ||
|
|
565602f8e1 | ||
|
|
73a445035c | ||
|
|
e0271cace3 | ||
|
|
cc8c4b8297 | ||
|
|
a287aab4f4 | ||
|
|
b5ed703553 | ||
|
|
f49880125a | ||
|
|
8380c357d9 | ||
|
|
4e010c5624 | ||
|
|
f53cdbace9 | ||
|
|
b4d04e2ce9 | ||
|
|
2470aeee1f | ||
|
|
fd07b47325 | ||
|
|
9257a05831 | ||
|
|
1faa6f2944 | ||
|
|
5584bbe9ca | ||
|
|
cc65537ea3 | ||
|
|
04a80b7c03 | ||
|
|
c71a89d1b7 | ||
|
|
e2abfd2312 | ||
|
|
49d55227e8 | ||
|
|
0dadec3b8d | ||
|
|
e2d8591d66 | ||
|
|
eac7aa84b0 | ||
|
|
bd941202c8 | ||
|
|
b854f0eedc | ||
|
|
1814bd4167 | ||
|
|
7c54913bf5 | ||
|
|
ddf097ede3 | ||
|
|
1bad85e1d6 | ||
|
|
68458b50d2 | ||
|
|
4cc34ec50a | ||
|
|
f637381198 |
@@ -9,10 +9,5 @@ npm install
|
||||
# Copy the env file
|
||||
cp .env.example .env
|
||||
|
||||
# Source the env file, export the variables
|
||||
set -a
|
||||
source .env
|
||||
set +a
|
||||
|
||||
# Run the migrations
|
||||
npm run -w @documenso/prisma prisma:migrate-dev
|
||||
npm run prisma:migrate-dev
|
||||
|
||||
50
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
50
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Create a bug report to help us improve
|
||||
---
|
||||
|
||||
<!--- Please provide a general summary of the issue in the Title above -->
|
||||
|
||||
## Issue Description
|
||||
|
||||
<!--- Please provide a clear and concise description of the problem. -->
|
||||
|
||||
## Steps to Reproduce
|
||||
|
||||
<!--- Please provide step-by-step instructions to reproduce the issue. -->
|
||||
<!--- Include code snippets, error messages, and any other relevant information. -->
|
||||
|
||||
1. Step 1
|
||||
2. Step 2
|
||||
3. ...
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
<!--- Describe what you expected to happen. -->
|
||||
|
||||
## Current Behavior
|
||||
|
||||
<!--- Describe what is currently happening. -->
|
||||
|
||||
## Screenshots (optional)
|
||||
|
||||
<!--- If applicable, add screenshots to help explain the issue. -->
|
||||
|
||||
## Environment
|
||||
|
||||
<!--- Please provide information about your environment, such as operating system, browser, version, etc. -->
|
||||
|
||||
- OS: [e.g., Windows 10]
|
||||
- Browser: [e.g., Chrome, Firefox]
|
||||
- Version: [e.g., 2.0.1]
|
||||
|
||||
## Checklist
|
||||
|
||||
<!--- Please check the boxes that apply to this issue report. -->
|
||||
<!--- You can add or remove items as needed. -->
|
||||
|
||||
- [ ] I have searched the existing issues to make sure this is not a duplicate.
|
||||
- [ ] I have provided steps to reproduce the issue.
|
||||
- [ ] I have included relevant environment information.
|
||||
- [ ] I have included any relevant screenshots.
|
||||
- [ ] I understand that this is a voluntary contribution and that there is no guarantee of resolution.
|
||||
41
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
41
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest a new idea or enhancement for this project
|
||||
---
|
||||
|
||||
<!--- Please provide a clear and concise title for your feature request -->
|
||||
|
||||
## Feature Description
|
||||
|
||||
<!--- Describe the feature you are requesting in detail. -->
|
||||
<!--- Explain what problem it solves or what value it adds to the project. -->
|
||||
|
||||
## Use Case
|
||||
|
||||
<!--- Provide a scenario or use case where this feature would be beneficial. -->
|
||||
<!--- Explain how users would interact with this feature and why it's important. -->
|
||||
|
||||
## Proposed Solution
|
||||
|
||||
<!--- If you have an idea of how this feature could be implemented, describe it here. -->
|
||||
<!--- Include any technical details, UI/UX considerations, or design suggestions. -->
|
||||
|
||||
## Alternatives (optional)
|
||||
|
||||
<!--- Are there any alternative ways to achieve the same goal? -->
|
||||
<!--- Describe other approaches that could be considered if this feature is not implemented. -->
|
||||
|
||||
## Additional Context
|
||||
|
||||
<!--- Add any additional context or information that might be relevant to the feature request. -->
|
||||
|
||||
## Checklist
|
||||
|
||||
<!--- Please check the boxes that apply to this feature request. -->
|
||||
<!--- You can add or remove items as needed. -->
|
||||
|
||||
- [ ] I have searched the existing feature requests to make sure this is not a duplicate.
|
||||
- [ ] I have provided a detailed description of the requested feature.
|
||||
- [ ] I have explained the use case or scenario for this feature.
|
||||
- [ ] I have included any relevant technical details or design suggestions.
|
||||
- [ ] I understand that this is a suggestion and that there is no guarantee of implementation.
|
||||
41
.github/ISSUE_TEMPLATE/improvement.md
vendored
Normal file
41
.github/ISSUE_TEMPLATE/improvement.md
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: General Improvement
|
||||
about: Suggest a minor enhancement or improvement for this project
|
||||
---
|
||||
|
||||
<!--- Please provide a clear and concise title for your improvement suggestion -->
|
||||
|
||||
## Improvement Description
|
||||
|
||||
<!--- Describe the improvement you are suggesting in detail. -->
|
||||
<!--- Explain what specific aspect of the project it addresses or enhances. -->
|
||||
|
||||
## Rationale
|
||||
|
||||
<!--- Explain why this improvement would be beneficial. -->
|
||||
<!--- Share any context, pain points, or reasons for suggesting this change. -->
|
||||
|
||||
## Proposed Solution
|
||||
|
||||
<!--- If you have a suggestion for how this improvement could be implemented, describe it here. -->
|
||||
<!--- Include any technical details, design suggestions, or other relevant information. -->
|
||||
|
||||
## Alternatives (optional)
|
||||
|
||||
<!--- Are there any alternative approaches to achieve the same improvement? -->
|
||||
<!--- Describe other ways to address the issue or enhance the project. -->
|
||||
|
||||
## Additional Context
|
||||
|
||||
<!--- Add any additional context or information that might be relevant to the improvement suggestion. -->
|
||||
|
||||
## Checklist
|
||||
|
||||
<!--- Please check the boxes that apply to this improvement suggestion. -->
|
||||
<!--- You can add or remove items as needed. -->
|
||||
|
||||
- [ ] I have searched the existing issues and improvement suggestions to avoid duplication.
|
||||
- [ ] I have provided a clear description of the improvement being suggested.
|
||||
- [ ] I have explained the rationale behind this improvement.
|
||||
- [ ] I have included any relevant technical details or design suggestions.
|
||||
- [ ] I understand that this is a suggestion and that there is no guarantee of implementation.
|
||||
49
.github/PULL_REQUEST_TEMPLATE/generic.md
vendored
Normal file
49
.github/PULL_REQUEST_TEMPLATE/generic.md
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
name: Pull Request
|
||||
about: Submit changes to the project for review and inclusion
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!--- Describe the changes introduced by this pull request. -->
|
||||
<!--- Explain what problem it solves or what feature/fix it adds. -->
|
||||
|
||||
## Related Issue
|
||||
|
||||
<!--- If this pull request is related to a specific issue, reference it here using #issue_number. -->
|
||||
<!--- For example, "Fixes #123" or "Addresses #456". -->
|
||||
|
||||
## Changes Made
|
||||
|
||||
<!--- Provide a summary of the changes made in this pull request. -->
|
||||
<!--- Include any relevant technical details or architecture changes. -->
|
||||
|
||||
- Change 1
|
||||
- Change 2
|
||||
- ...
|
||||
|
||||
## Testing Performed
|
||||
|
||||
<!--- Describe the testing that you have performed to validate these changes. -->
|
||||
<!--- Include information about test cases, testing environments, and results. -->
|
||||
|
||||
- Tested feature X in scenario Y.
|
||||
- Ran unit tests for component Z.
|
||||
- Tested on browsers A, B, and C.
|
||||
- ...
|
||||
|
||||
## Checklist
|
||||
|
||||
<!--- Please check the boxes that apply to this pull request. -->
|
||||
<!--- You can add or remove items as needed. -->
|
||||
|
||||
- [ ] I have tested these changes locally and they work as expected.
|
||||
- [ ] I have added/updated tests that prove the effectiveness of these changes.
|
||||
- [ ] I have updated the documentation to reflect these changes, if applicable.
|
||||
- [ ] I have followed the project's coding style guidelines.
|
||||
- [ ] I have addressed the code review feedback from the previous submission, if applicable.
|
||||
|
||||
## Additional Notes
|
||||
|
||||
<!--- Provide any additional context or notes for the reviewers. -->
|
||||
<!--- This might include details about design decisions, potential concerns, or anything else relevant. -->
|
||||
40
.github/PULL_REQUEST_TEMPLATE/test-addition.md
vendored
Normal file
40
.github/PULL_REQUEST_TEMPLATE/test-addition.md
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
name: Test Addition
|
||||
about: Submit a new test, either unit or end-to-end (E2E), for review and inclusion
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!--- Provide a clear and concise description of the new test you are adding. -->
|
||||
<!--- Explain the purpose of the test and what it aims to validate. -->
|
||||
|
||||
## Related Issue
|
||||
|
||||
<!--- If this test addition is related to a specific issue, reference it here using #issue_number. -->
|
||||
<!--- For example, "Fixes #123" or "Addresses #456". -->
|
||||
|
||||
## Test Details
|
||||
|
||||
<!--- Describe the details of the test you're adding. -->
|
||||
<!--- Include information about inputs, expected outputs, and any specific scenarios. -->
|
||||
|
||||
- Test Name: Name of the test
|
||||
- Type: [Unit / E2E]
|
||||
- Description: Brief description of what the test checks
|
||||
- Inputs: What inputs the test uses (if applicable)
|
||||
- Expected Output: What output or behavior the test expects
|
||||
|
||||
## Checklist
|
||||
|
||||
<!--- Please check the boxes that apply to this pull request. -->
|
||||
<!--- You can add or remove items as needed. -->
|
||||
|
||||
- [ ] I have written the new test and ensured it works as intended.
|
||||
- [ ] I have added necessary documentation to explain the purpose of the test.
|
||||
- [ ] I have followed the project's testing guidelines and coding style.
|
||||
- [ ] I have addressed any review feedback from previous submissions, if applicable.
|
||||
|
||||
## Additional Notes
|
||||
|
||||
<!--- Provide any additional context or notes for the reviewers. -->
|
||||
<!--- This might include explanations about the testing approach or any potential concerns. -->
|
||||
6
.github/dependabot.yml
vendored
6
.github/dependabot.yml
vendored
@@ -9,7 +9,7 @@ updates:
|
||||
labels:
|
||||
- "ci dependencies"
|
||||
- "ci"
|
||||
open-pull-requests-limit: 2
|
||||
open-pull-requests-limit: 0
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/apps/marketing"
|
||||
@@ -19,7 +19,7 @@ updates:
|
||||
labels:
|
||||
- "npm dependencies"
|
||||
- "frontend"
|
||||
open-pull-requests-limit: 2
|
||||
open-pull-requests-limit: 0
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/apps/web"
|
||||
@@ -29,4 +29,4 @@ updates:
|
||||
labels:
|
||||
- "npm dependencies"
|
||||
- "frontend"
|
||||
open-pull-requests-limit: 2
|
||||
open-pull-requests-limit: 0
|
||||
|
||||
55
.gitpod.yml
Normal file
55
.gitpod.yml
Normal file
@@ -0,0 +1,55 @@
|
||||
tasks:
|
||||
- init: |
|
||||
npm i &&
|
||||
npm run dx:up &&
|
||||
cp .env.example .env &&
|
||||
set -a; source .env &&
|
||||
export NEXTAUTH_URL="$(gp url 3000)" &&
|
||||
export NEXT_PUBLIC_WEBAPP_URL="$(gp url 3000)" &&
|
||||
export NEXT_PUBLIC_MARKETING_URL="$(gp url 3001)"
|
||||
command: npm run d
|
||||
|
||||
ports:
|
||||
- port: 3000
|
||||
visibility: public
|
||||
onOpen: open-preview
|
||||
- port: 3001
|
||||
visibility: public
|
||||
onOpen: open-preview
|
||||
- port: 9000
|
||||
visibility: public
|
||||
onOpen: ignore
|
||||
- port: 1100
|
||||
visibility: private
|
||||
onOpen: ignore
|
||||
- port: 2500
|
||||
visibility: private
|
||||
onOpen: ignore
|
||||
- port: 54320
|
||||
visibility: private
|
||||
onOpen: ignore
|
||||
|
||||
|
||||
github:
|
||||
prebuilds:
|
||||
master: true
|
||||
pullRequests: true
|
||||
pullRequestsFromForks: true
|
||||
addCheck: true
|
||||
addComment: true
|
||||
addBadge: true
|
||||
|
||||
vscode:
|
||||
extensions:
|
||||
- aaron-bond.better-comments
|
||||
- bradlc.vscode-tailwindcss
|
||||
- dbaeumer.vscode-eslint
|
||||
- esbenp.prettier-vscode
|
||||
- mikestead.dotenv
|
||||
- unifiedjs.vscode-mdx
|
||||
- GitHub.copilot-chat
|
||||
- GitHub.copilot-labs
|
||||
- GitHub.copilot
|
||||
- GitHub.vscode-pull-request-github
|
||||
- Prisma.prisma
|
||||
- VisualStudioExptTeam.vscodeintellicode
|
||||
18
README.md
18
README.md
@@ -179,7 +179,7 @@ git clone https://github.com/documenso/documenso
|
||||
- NEXT_PRIVATE_SMTP_FROM_NAME
|
||||
- NEXT_PRIVATE_SMTP_FROM_ADDRESS
|
||||
|
||||
5. Create the database schema by running `npm run prisma:migrate-dev -w @documenso/prisma`
|
||||
5. Create the database schema by running `npm run prisma:migrate-dev`
|
||||
|
||||
6. Run `npm run dev` root directory to start
|
||||
|
||||
@@ -254,6 +254,22 @@ containers:
|
||||
- '::'
|
||||
```
|
||||
|
||||
### I can't see environment variables in my package scripts
|
||||
|
||||
Wrap your package script with the `with:env` script like such:
|
||||
|
||||
```
|
||||
npm run with:env -- npm run myscript
|
||||
```
|
||||
|
||||
The same can be done when using `npx` for one of bin scripts:
|
||||
|
||||
```
|
||||
npm run with:env -- npx myscript
|
||||
```
|
||||
|
||||
This will load environment variables from your `.env` and `.env.local` files.
|
||||
|
||||
## Repo Activity
|
||||
|
||||

|
||||
|
||||
@@ -4,6 +4,7 @@ import { Caveat, Inter } from 'next/font/google';
|
||||
|
||||
import { FeatureFlagProvider } from '@documenso/lib/client-only/providers/feature-flag';
|
||||
import { getAllAnonymousFlags } from '@documenso/lib/universal/get-feature-flag';
|
||||
import { TrpcProvider } from '@documenso/trpc/react';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { Toaster } from '@documenso/ui/primitives/toaster';
|
||||
|
||||
@@ -63,7 +64,9 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
||||
<body>
|
||||
<FeatureFlagProvider initialFlags={flags}>
|
||||
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
||||
<PlausibleProvider>{children}</PlausibleProvider>
|
||||
<PlausibleProvider>
|
||||
<TrpcProvider>{children}</TrpcProvider>
|
||||
</PlausibleProvider>
|
||||
</ThemeProvider>
|
||||
</FeatureFlagProvider>
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
import { useCopyToClipboard } from '~/hooks/use-copy-to-clipboard';
|
||||
|
||||
export type PasswordRevealProps = {
|
||||
password: string;
|
||||
};
|
||||
|
||||
@@ -4,14 +4,13 @@ import { useEffect, useState } from 'react';
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
import { Share } from 'lucide-react';
|
||||
|
||||
import { useFeatureFlags } from '@documenso/lib/client-only/providers/feature-flag';
|
||||
import { base64 } from '@documenso/lib/universal/base64';
|
||||
import { getFile } from '@documenso/lib/universal/upload/get-file';
|
||||
import { DocumentWithRecipient } from '@documenso/prisma/types/document-with-recipient';
|
||||
import DocumentDialog from '@documenso/ui/components/document/document-dialog';
|
||||
import { DocumentDownloadButton } from '@documenso/ui/components/document/document-download-button';
|
||||
import { DocumentShareButton } from '@documenso/ui/components/document/document-share-button';
|
||||
import { SigningCard3D } from '@documenso/ui/components/signing-card';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
@@ -87,11 +86,11 @@ export const SinglePlayerModeSuccess = ({ className, document }: SinglePlayerMod
|
||||
<div className="relative mt-8 w-full">
|
||||
<div className={cn('flex flex-col items-center', className)}>
|
||||
<div className="grid w-full max-w-sm grid-cols-2 gap-4">
|
||||
{/* TODO: Hook this up */}
|
||||
<Button variant="outline" className="flex-1 bg-transparent backdrop-blur-sm" disabled>
|
||||
<Share className="mr-2 h-5 w-5" />
|
||||
Share
|
||||
</Button>
|
||||
<DocumentShareButton
|
||||
documentId={document.id}
|
||||
token={document.Recipient.token}
|
||||
className="flex-1 bg-transparent backdrop-blur-sm"
|
||||
/>
|
||||
|
||||
<DocumentDownloadButton
|
||||
className="flex-1 bg-transparent backdrop-blur-sm"
|
||||
@@ -103,7 +102,7 @@ export const SinglePlayerModeSuccess = ({ className, document }: SinglePlayerMod
|
||||
<Button
|
||||
onClick={async () => onShowDocumentClick()}
|
||||
loading={isFetchingDocumentFile}
|
||||
className="col-span-2"
|
||||
className="z-10 col-span-2"
|
||||
>
|
||||
Show document
|
||||
</Button>
|
||||
|
||||
@@ -377,7 +377,7 @@ export const Widget = ({ className, children, ...props }: WidgetProps) => {
|
||||
</Card>
|
||||
|
||||
<Dialog open={showSigningDialog} onOpenChange={setShowSigningDialog}>
|
||||
<DialogContent>
|
||||
<DialogContent position="center">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Add your signature</DialogTitle>
|
||||
</DialogHeader>
|
||||
@@ -391,6 +391,7 @@ export const Widget = ({ className, children, ...props }: WidgetProps) => {
|
||||
|
||||
<SignaturePad
|
||||
className="aspect-video w-full rounded-md border"
|
||||
defaultValue={signatureDataUrl || ''}
|
||||
onChange={setDraftSignatureDataUrl}
|
||||
/>
|
||||
|
||||
|
||||
8
apps/marketing/src/pages/api/trpc/[trpc].ts
Normal file
8
apps/marketing/src/pages/api/trpc/[trpc].ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import * as trpcNext from '@documenso/trpc/server/adapters/next';
|
||||
import { createTrpcContext } from '@documenso/trpc/server/context';
|
||||
import { appRouter } from '@documenso/trpc/server/router';
|
||||
|
||||
export default trpcNext.createNextApiHandler({
|
||||
router: appRouter,
|
||||
createContext: async ({ req, res }) => createTrpcContext({ req, res }),
|
||||
});
|
||||
@@ -6,13 +6,12 @@ import { Edit, Pencil, Share } from 'lucide-react';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard';
|
||||
import { Document, DocumentStatus, Recipient, SigningStatus, User } from '@documenso/prisma/client';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
import { useCopyToClipboard } from '~/hooks/use-copy-to-clipboard';
|
||||
|
||||
export type DataTableActionButtonProps = {
|
||||
row: Document & {
|
||||
User: Pick<User, 'id' | 'name' | 'email'>;
|
||||
@@ -47,7 +46,7 @@ export const DataTableActionButton = ({ row }: DataTableActionButtonProps) => {
|
||||
documentId: row.id,
|
||||
});
|
||||
|
||||
await copyToClipboard(`${window.location.origin}/share/${slug}`).catch(() => null);
|
||||
await copyToClipboard(`${process.env.NEXT_PUBLIC_WEBAPP_URL}/share/${slug}`).catch(() => null);
|
||||
|
||||
toast({
|
||||
title: 'Copied to clipboard',
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
import Link from 'next/link';
|
||||
|
||||
import {
|
||||
@@ -16,6 +18,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { useSession } from 'next-auth/react';
|
||||
|
||||
import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard';
|
||||
import { getFile } from '@documenso/lib/universal/upload/get-file';
|
||||
import { Document, DocumentStatus, Recipient, User } from '@documenso/prisma/client';
|
||||
import { DocumentWithData } from '@documenso/prisma/types/document-with-data';
|
||||
@@ -30,7 +33,7 @@ import {
|
||||
} from '@documenso/ui/primitives/dropdown-menu';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
import { useCopyToClipboard } from '~/hooks/use-copy-to-clipboard';
|
||||
import { DeleteDraftDocumentDialog } from './delete-draft-document-dialog';
|
||||
|
||||
export type DataTableActionDropdownProps = {
|
||||
row: Document & {
|
||||
@@ -44,6 +47,8 @@ export const DataTableActionDropdown = ({ row }: DataTableActionDropdownProps) =
|
||||
const { toast } = useToast();
|
||||
const [, copyToClipboard] = useCopyToClipboard();
|
||||
|
||||
const [isDeleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||
|
||||
if (!session) {
|
||||
return null;
|
||||
}
|
||||
@@ -59,6 +64,7 @@ export const DataTableActionDropdown = ({ row }: DataTableActionDropdownProps) =
|
||||
// const isPending = row.status === DocumentStatus.PENDING;
|
||||
const isComplete = row.status === DocumentStatus.COMPLETED;
|
||||
// const isSigned = recipient?.signingStatus === SigningStatus.SIGNED;
|
||||
const isDocumentDeletable = isOwner && row.status === DocumentStatus.DRAFT;
|
||||
|
||||
const onShareClick = async () => {
|
||||
const { slug } = await createOrGetShareLink({
|
||||
@@ -66,7 +72,7 @@ export const DataTableActionDropdown = ({ row }: DataTableActionDropdownProps) =
|
||||
documentId: row.id,
|
||||
});
|
||||
|
||||
await copyToClipboard(`${window.location.origin}/share/${slug}`).catch(() => null);
|
||||
await copyToClipboard(`${process.env.NEXT_PUBLIC_WEBAPP_URL}/share/${slug}`).catch(() => null);
|
||||
|
||||
toast({
|
||||
title: 'Copied to clipboard',
|
||||
@@ -147,7 +153,7 @@ export const DataTableActionDropdown = ({ row }: DataTableActionDropdownProps) =
|
||||
Void
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuItem disabled>
|
||||
<DropdownMenuItem onClick={() => setDeleteDialogOpen(true)} disabled={!isDocumentDeletable}>
|
||||
<Trash2 className="mr-2 h-4 w-4" />
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
@@ -168,6 +174,14 @@ export const DataTableActionDropdown = ({ row }: DataTableActionDropdownProps) =
|
||||
Share
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
|
||||
{isDocumentDeletable && (
|
||||
<DeleteDraftDocumentDialog
|
||||
id={row.id}
|
||||
open={isDeleteDialogOpen}
|
||||
onOpenChange={setDeleteDialogOpen}
|
||||
/>
|
||||
)}
|
||||
</DropdownMenu>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
import { trpc as trpcReact } from '@documenso/trpc/react';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@documenso/ui/primitives/dialog';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
type DeleteDraftDocumentDialogProps = {
|
||||
id: number;
|
||||
open: boolean;
|
||||
onOpenChange: (_open: boolean) => void;
|
||||
};
|
||||
|
||||
export const DeleteDraftDocumentDialog = ({
|
||||
id,
|
||||
open,
|
||||
onOpenChange,
|
||||
}: DeleteDraftDocumentDialogProps) => {
|
||||
const router = useRouter();
|
||||
|
||||
const { toast } = useToast();
|
||||
|
||||
const { mutateAsync: deleteDocument, isLoading } =
|
||||
trpcReact.document.deleteDraftDocument.useMutation({
|
||||
onSuccess: () => {
|
||||
router.refresh();
|
||||
|
||||
toast({
|
||||
title: 'Document deleted',
|
||||
description: 'Your document has been successfully deleted.',
|
||||
duration: 5000,
|
||||
});
|
||||
|
||||
onOpenChange(false);
|
||||
},
|
||||
});
|
||||
|
||||
const onDraftDelete = async () => {
|
||||
try {
|
||||
await deleteDocument({ id });
|
||||
} catch {
|
||||
toast({
|
||||
title: 'Something went wrong',
|
||||
description: 'This document could not be deleted at this time. Please try again.',
|
||||
variant: 'destructive',
|
||||
duration: 7500,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={(value) => !isLoading && onOpenChange(value)}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Do you want to delete this document?</DialogTitle>
|
||||
|
||||
<DialogDescription>
|
||||
Please note that this action is irreversible. Once confirmed, your document will be
|
||||
permanently deleted.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<DialogFooter>
|
||||
<div className="flex w-full flex-1 flex-nowrap gap-4">
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={() => onOpenChange(false)}
|
||||
className="flex-1"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
<Button type="button" loading={isLoading} onClick={onDraftDelete} className="flex-1">
|
||||
Delete
|
||||
</Button>
|
||||
</div>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -66,7 +66,7 @@ export default async function DocumentsPage({ searchParams = {} }: DocumentsPage
|
||||
<div className="mt-12 flex flex-wrap items-center justify-between gap-x-4 gap-y-8">
|
||||
<h1 className="text-4xl font-semibold">Documents</h1>
|
||||
|
||||
<div className="flex flex-wrap gap-x-4 gap-y-6 overflow-hidden">
|
||||
<div className="-m-1 flex flex-wrap gap-x-4 gap-y-6 overflow-hidden p-1">
|
||||
<Tabs defaultValue={status} className="overflow-x-auto">
|
||||
<TabsList>
|
||||
{[
|
||||
|
||||
@@ -9,12 +9,11 @@ import { getFieldsForToken } from '@documenso/lib/server-only/field/get-fields-f
|
||||
import { getRecipientByToken } from '@documenso/lib/server-only/recipient/get-recipient-by-token';
|
||||
import { DocumentStatus, FieldType } from '@documenso/prisma/client';
|
||||
import { DocumentDownloadButton } from '@documenso/ui/components/document/document-download-button';
|
||||
import { DocumentShareButton } from '@documenso/ui/components/document/document-share-button';
|
||||
import { SigningCard3D } from '@documenso/ui/components/signing-card';
|
||||
|
||||
import signingCelebration from '~/assets/signing-celebration.png';
|
||||
|
||||
import { ShareButton } from './share-button';
|
||||
|
||||
export type CompletedSigningPageProps = {
|
||||
params: {
|
||||
token?: string;
|
||||
@@ -89,7 +88,7 @@ export default async function CompletedSigningPage({
|
||||
))}
|
||||
|
||||
<div className="mt-8 flex w-full max-w-sm items-center justify-center gap-4">
|
||||
<ShareButton documentId={document.id} token={recipient.token} />
|
||||
<DocumentShareButton documentId={document.id} token={recipient.token} />
|
||||
|
||||
<DocumentDownloadButton
|
||||
className="flex-1"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useMemo, useState, useTransition } from 'react';
|
||||
import { useEffect, useMemo, useState, useTransition } from 'react';
|
||||
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
@@ -48,6 +48,7 @@ export const SignatureField = ({ field, recipient }: SignatureFieldProps) => {
|
||||
|
||||
const [showSignatureModal, setShowSignatureModal] = useState(false);
|
||||
const [localSignature, setLocalSignature] = useState<string | null>(null);
|
||||
const [isLocalSignatureSet, setIsLocalSignatureSet] = useState(false);
|
||||
|
||||
const state = useMemo<SignatureFieldState>(() => {
|
||||
if (!field.inserted) {
|
||||
@@ -61,9 +62,16 @@ export const SignatureField = ({ field, recipient }: SignatureFieldProps) => {
|
||||
return 'signed-text';
|
||||
}, [field.inserted, signature?.signatureImageAsBase64]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!showSignatureModal && !isLocalSignatureSet) {
|
||||
setLocalSignature(null);
|
||||
}
|
||||
}, [showSignatureModal, isLocalSignatureSet]);
|
||||
|
||||
const onSign = async (source: 'local' | 'provider' = 'provider') => {
|
||||
try {
|
||||
if (!providedSignature && !localSignature) {
|
||||
setIsLocalSignatureSet(false);
|
||||
setShowSignatureModal(true);
|
||||
return;
|
||||
}
|
||||
@@ -178,6 +186,7 @@ export const SignatureField = ({ field, recipient }: SignatureFieldProps) => {
|
||||
disabled={!localSignature}
|
||||
onClick={() => {
|
||||
setShowSignatureModal(false);
|
||||
setIsLocalSignatureSet(true);
|
||||
void onSign('local');
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
export type CopiedValue = string | null;
|
||||
export type CopyFn = (_text: string) => Promise<boolean>;
|
||||
|
||||
export function useCopyToClipboard(): [CopiedValue, CopyFn] {
|
||||
const [copiedText, setCopiedText] = useState<CopiedValue>(null);
|
||||
|
||||
const copy: CopyFn = async (text) => {
|
||||
if (!navigator?.clipboard) {
|
||||
console.warn('Clipboard not supported');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to save to clipboard then save it in the state if worked
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
setCopiedText(text);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.warn('Copy failed', error);
|
||||
setCopiedText(null);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
return [copiedText, copy];
|
||||
}
|
||||
17
package-lock.json
generated
17
package-lock.json
generated
@@ -15,8 +15,8 @@
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.7.1",
|
||||
"@commitlint/config-conventional": "^17.7.0",
|
||||
"dotenv": "^16.0.3",
|
||||
"dotenv-cli": "^7.2.1",
|
||||
"dotenv": "^16.3.1",
|
||||
"dotenv-cli": "^7.3.0",
|
||||
"eslint": "^8.40.0",
|
||||
"eslint-config-custom": "*",
|
||||
"husky": "^8.0.0",
|
||||
@@ -9103,7 +9103,6 @@
|
||||
"version": "16.3.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
|
||||
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -9112,13 +9111,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv-cli": {
|
||||
"version": "7.2.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-7.2.1.tgz",
|
||||
"integrity": "sha512-ODHbGTskqRtXAzZapDPvgNuDVQApu4oKX8lZW7Y0+9hKA6le1ZJlyRS687oU9FXjOVEDU/VFV6zI125HzhM1UQ==",
|
||||
"dev": true,
|
||||
"version": "7.3.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-7.3.0.tgz",
|
||||
"integrity": "sha512-314CA4TyK34YEJ6ntBf80eUY+t1XaFLyem1k9P0sX1gn30qThZ5qZr/ZwE318gEnzyYP9yj9HJk6SqwE0upkfw==",
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.3",
|
||||
"dotenv": "^16.0.0",
|
||||
"dotenv": "^16.3.0",
|
||||
"dotenv-expand": "^10.0.0",
|
||||
"minimist": "^1.2.6"
|
||||
},
|
||||
@@ -9130,7 +9128,6 @@
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz",
|
||||
"integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -19889,6 +19886,8 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@prisma/client": "5.3.1",
|
||||
"dotenv": "^16.3.1",
|
||||
"dotenv-cli": "^7.3.0",
|
||||
"prisma": "5.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
11
package.json
11
package.json
@@ -11,9 +11,12 @@
|
||||
"commitlint": "commitlint --edit",
|
||||
"clean": "turbo run clean && rimraf node_modules",
|
||||
"d": "npm run dx && npm run dev",
|
||||
"dx": "npm i && npm run dx:up && npm run prisma:migrate-dev -w @documenso/prisma",
|
||||
"dx": "npm i && npm run dx:up && npm run prisma:migrate-dev",
|
||||
"dx:up": "docker compose -f docker/compose-services.yml up -d",
|
||||
"dx:down": "docker compose -f docker/compose-services.yml down"
|
||||
"dx:down": "docker compose -f docker/compose-services.yml down",
|
||||
"prisma:migrate-dev": "npm run with:env -- npm run prisma:migrate-dev -w @documenso/prisma",
|
||||
"prisma:migrate-deploy": "npm run with:env -- npm run prisma:migrate-deploy -w @documenso/prisma",
|
||||
"with:env": "dotenv -e .env -e .env.local --"
|
||||
},
|
||||
"engines": {
|
||||
"npm": ">=8.6.0",
|
||||
@@ -22,8 +25,8 @@
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.7.1",
|
||||
"@commitlint/config-conventional": "^17.7.0",
|
||||
"dotenv": "^16.0.3",
|
||||
"dotenv-cli": "^7.2.1",
|
||||
"dotenv": "^16.3.1",
|
||||
"dotenv-cli": "^7.3.0",
|
||||
"eslint": "^8.40.0",
|
||||
"eslint-config-custom": "*",
|
||||
"husky": "^8.0.0",
|
||||
|
||||
13
packages/lib/server-only/document/delete-draft-document.ts
Normal file
13
packages/lib/server-only/document/delete-draft-document.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
'use server';
|
||||
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { DocumentStatus } from '@documenso/prisma/client';
|
||||
|
||||
export type DeleteDraftDocumentOptions = {
|
||||
id: number;
|
||||
userId: number;
|
||||
};
|
||||
|
||||
export const deleteDraftDocument = async ({ id, userId }: DeleteDraftDocumentOptions) => {
|
||||
return await prisma.document.delete({ where: { id, userId, status: DocumentStatus.DRAFT } });
|
||||
};
|
||||
@@ -18,6 +18,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "5.3.1",
|
||||
"dotenv": "^16.3.1",
|
||||
"dotenv-cli": "^7.3.0",
|
||||
"prisma": "5.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -12,12 +12,16 @@ export const authRouter = router({
|
||||
|
||||
return await createUser({ name, email, password, signature });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
let message =
|
||||
'We were unable to create your account. Please review the information you provided and try again.';
|
||||
|
||||
if (err instanceof Error && err.message === 'User already exists') {
|
||||
message = 'User with this email already exists. Please use a different email address.';
|
||||
}
|
||||
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message:
|
||||
'We were unable to create your account. Please review the information you provided and try again.',
|
||||
message,
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { TRPCError } from '@trpc/server';
|
||||
|
||||
import { createDocument } from '@documenso/lib/server-only/document/create-document';
|
||||
import { deleteDraftDocument } from '@documenso/lib/server-only/document/delete-draft-document';
|
||||
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
|
||||
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
|
||||
import { sendDocument } from '@documenso/lib/server-only/document/send-document';
|
||||
@@ -10,6 +11,7 @@ import { setRecipientsForDocument } from '@documenso/lib/server-only/recipient/s
|
||||
import { authenticatedProcedure, procedure, router } from '../trpc';
|
||||
import {
|
||||
ZCreateDocumentMutationSchema,
|
||||
ZDeleteDraftDocumentMutationSchema,
|
||||
ZGetDocumentByIdQuerySchema,
|
||||
ZGetDocumentByTokenQuerySchema,
|
||||
ZSendDocumentMutationSchema,
|
||||
@@ -76,6 +78,25 @@ export const documentRouter = router({
|
||||
}
|
||||
}),
|
||||
|
||||
deleteDraftDocument: authenticatedProcedure
|
||||
.input(ZDeleteDraftDocumentMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
try {
|
||||
const { id } = input;
|
||||
|
||||
const userId = ctx.user.id;
|
||||
|
||||
return await deleteDraftDocument({ id, userId });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: 'We were unable to delete this document. Please try again later.',
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
setRecipientsForDocument: authenticatedProcedure
|
||||
.input(ZSetRecipientsForDocumentMutationSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
|
||||
@@ -61,3 +61,9 @@ export const ZSendDocumentMutationSchema = z.object({
|
||||
});
|
||||
|
||||
export type TSendDocumentMutationSchema = z.infer<typeof ZSendDocumentMutationSchema>;
|
||||
|
||||
export const ZDeleteDraftDocumentMutationSchema = z.object({
|
||||
id: z.number().min(1),
|
||||
});
|
||||
|
||||
export type TDeleteDraftDocumentMutationSchema = z.infer<typeof ZDeleteDraftDocumentMutationSchema>;
|
||||
|
||||
@@ -5,8 +5,10 @@ import { HTMLAttributes, useState } from 'react';
|
||||
import { Copy, Share } from 'lucide-react';
|
||||
import { FaXTwitter } from 'react-icons/fa6';
|
||||
|
||||
import { useCopyToClipboard } from '@documenso/lib/client-only/hooks/use-copy-to-clipboard';
|
||||
import { generateTwitterIntent } from '@documenso/lib/universal/generate-twitter-intent';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
Dialog,
|
||||
@@ -18,14 +20,12 @@ import {
|
||||
} from '@documenso/ui/primitives/dialog';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
import { useCopyToClipboard } from '~/hooks/use-copy-to-clipboard';
|
||||
|
||||
export type ShareButtonProps = HTMLAttributes<HTMLButtonElement> & {
|
||||
export type DocumentShareButtonProps = HTMLAttributes<HTMLButtonElement> & {
|
||||
token: string;
|
||||
documentId: number;
|
||||
};
|
||||
|
||||
export const ShareButton = ({ token, documentId }: ShareButtonProps) => {
|
||||
export const DocumentShareButton = ({ token, documentId, className }: DocumentShareButtonProps) => {
|
||||
const { toast } = useToast();
|
||||
const [, copyToClipboard] = useCopyToClipboard();
|
||||
|
||||
@@ -60,7 +60,7 @@ export const ShareButton = ({ token, documentId }: ShareButtonProps) => {
|
||||
slug = result.slug;
|
||||
}
|
||||
|
||||
await copyToClipboard(`${window.location.origin}/share/${slug}`).catch(() => null);
|
||||
await copyToClipboard(`${process.env.NEXT_PUBLIC_WEBAPP_URL}/share/${slug}`).catch(() => null);
|
||||
|
||||
toast({
|
||||
title: 'Copied to clipboard',
|
||||
@@ -85,7 +85,7 @@ export const ShareButton = ({ token, documentId }: ShareButtonProps) => {
|
||||
window.open(
|
||||
generateTwitterIntent(
|
||||
`I just ${token ? 'signed' : 'sent'} a document with @documenso. Check it out!`,
|
||||
`${window.location.origin}/share/${slug}`,
|
||||
`${process.env.NEXT_PUBLIC_WEBAPP_URL}/share/${slug}`,
|
||||
),
|
||||
'_blank',
|
||||
);
|
||||
@@ -99,7 +99,7 @@ export const ShareButton = ({ token, documentId }: ShareButtonProps) => {
|
||||
<Button
|
||||
variant="outline"
|
||||
disabled={!token || !documentId}
|
||||
className="flex-1"
|
||||
className={cn('flex-1', className)}
|
||||
loading={isLoading}
|
||||
>
|
||||
{!isLoading && <Share className="mr-2 h-5 w-5" />}
|
||||
@@ -120,8 +120,12 @@ export const ShareButton = ({ token, documentId }: ShareButtonProps) => {
|
||||
<span className="font-medium text-blue-400">@documenso</span>
|
||||
. Check it out!
|
||||
<span className="mt-2 block" />
|
||||
<span className="break-all font-medium text-blue-400">
|
||||
{window.location.origin}/share/{shareLink?.slug || '...'}
|
||||
<span
|
||||
className={cn('break-all font-medium text-blue-400', {
|
||||
'animate-pulse': !shareLink?.slug,
|
||||
})}
|
||||
>
|
||||
{process.env.NEXT_PUBLIC_WEBAPP_URL}/share/{shareLink?.slug || '...'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -16,12 +16,13 @@ const DialogPortal = ({
|
||||
children,
|
||||
position = 'start',
|
||||
...props
|
||||
}: DialogPrimitive.DialogPortalProps & { position?: 'start' | 'end' }) => (
|
||||
}: DialogPrimitive.DialogPortalProps & { position?: 'start' | 'end' | 'center' }) => (
|
||||
<DialogPrimitive.Portal className={cn(className)} {...props}>
|
||||
<div
|
||||
className={cn('fixed inset-0 z-50 flex justify-center sm:items-center', {
|
||||
'items-start': position === 'start',
|
||||
'items-end': position === 'end',
|
||||
'items-center': position === 'center',
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
@@ -49,7 +50,9 @@ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
||||
|
||||
const DialogContent = React.forwardRef<
|
||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & { position?: 'start' | 'end' }
|
||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
|
||||
position?: 'start' | 'end' | 'center';
|
||||
}
|
||||
>(({ className, children, position = 'start', ...props }, ref) => (
|
||||
<DialogPortal position={position}>
|
||||
<DialogOverlay />
|
||||
|
||||
@@ -97,10 +97,7 @@ export const DocumentFlowFormContainerStep = ({
|
||||
return (
|
||||
<div>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{title}{' '}
|
||||
<span>
|
||||
({step}/{maxStep})
|
||||
</span>
|
||||
Step <span>{`${step} of ${maxStep}`}</span>
|
||||
</p>
|
||||
|
||||
<div className="bg-muted relative mt-4 h-[2px] rounded-md">
|
||||
|
||||
Reference in New Issue
Block a user