Skip to content

Commit b7e20b0

Browse files
authored
Merge pull request #452 from mfts/feat/add-nda-upload
feat: add upload agreement
2 parents 6e1fb18 + fa8b6ca commit b7e20b0

6 files changed

Lines changed: 282 additions & 48 deletions

File tree

components/document-upload.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useMemo, useState } from "react";
1+
import { useMemo } from "react";
22

33
import {
44
Upload as ArrowUpTrayIcon,

components/links/link-sheet/agreement-panel/index.tsx

Lines changed: 109 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { useRouter } from "next/router";
2-
31
import {
42
Dispatch,
53
FormEvent,
@@ -11,13 +9,11 @@ import {
119
import { useTeam } from "@/context/team-context";
1210
import { toast } from "sonner";
1311
import { mutate } from "swr";
14-
import { set } from "ts-pattern/dist/patterns";
1512

13+
import DocumentUpload from "@/components/document-upload";
1614
import { Button } from "@/components/ui/button";
1715
import { Input } from "@/components/ui/input";
1816
import { Label } from "@/components/ui/label";
19-
import { ScrollArea } from "@/components/ui/scroll-area";
20-
import { Separator } from "@/components/ui/separator";
2117
import {
2218
Sheet,
2319
SheetContent,
@@ -27,12 +23,12 @@ import {
2723
SheetTitle,
2824
} from "@/components/ui/sheet";
2925

30-
import { useAnalytics } from "@/lib/analytics";
31-
import { usePlan } from "@/lib/swr/use-billing";
32-
import { useDocumentLinks } from "@/lib/swr/use-document";
33-
import { useDomains } from "@/lib/swr/use-domains";
34-
import { LinkWithViews } from "@/lib/types";
35-
import { convertDataUrlToFile, uploadImage } from "@/lib/utils";
26+
import {
27+
DocumentData,
28+
createAgreementDocument,
29+
} from "@/lib/documents/create-document";
30+
import { putFile } from "@/lib/files/put-file";
31+
import { getSupportedContentType } from "@/lib/utils/get-content-type";
3632

3733
export default function AgreementSheet({
3834
isOpen,
@@ -45,6 +41,62 @@ export default function AgreementSheet({
4541
const teamId = teamInfo?.currentTeam?.id;
4642
const [data, setData] = useState({ name: "", link: "" });
4743
const [isLoading, setIsLoading] = useState<boolean>(false);
44+
const [currentFile, setCurrentFile] = useState<File | null>(null);
45+
const [currentLink, setCurrentLink] = useState<string | null>(null);
46+
47+
const handleBrowserUpload = async () => {
48+
// event.preventDefault();
49+
// event.stopPropagation();
50+
51+
// Check if the file is chosen
52+
if (!currentFile) {
53+
toast.error("Please select a file to upload.");
54+
return; // prevent form from submitting
55+
}
56+
57+
try {
58+
setIsLoading(true);
59+
60+
const contentType = getSupportedContentType(currentFile.type);
61+
62+
if (!contentType || currentFile.type !== "application/pdf") {
63+
toast.error("Unsupported file format. Please upload a PDF file.");
64+
return;
65+
}
66+
67+
const { type, data, numPages } = await putFile({
68+
file: currentFile,
69+
teamId: teamId!,
70+
});
71+
72+
const documentData: DocumentData = {
73+
name: currentFile.name,
74+
key: data!,
75+
storageType: type!,
76+
contentType: contentType,
77+
};
78+
// create a document in the database
79+
const response = await createAgreementDocument({
80+
documentData,
81+
teamId: teamId!,
82+
numPages,
83+
});
84+
85+
if (response) {
86+
const document = await response.json();
87+
const linkId = document.links[0].id;
88+
setData((prevData) => ({
89+
...prevData,
90+
link: "https://www.papermark.io/view/" + linkId,
91+
}));
92+
}
93+
} catch (error) {
94+
console.error("An error occurred while uploading the file: ", error);
95+
} finally {
96+
setCurrentFile(null);
97+
setIsLoading(false);
98+
}
99+
};
48100

49101
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
50102
e.preventDefault();
@@ -77,9 +129,16 @@ export default function AgreementSheet({
77129
} finally {
78130
setIsLoading(false);
79131
setIsOpen(false);
132+
setData({ name: "", link: "" });
80133
}
81134
};
82135

136+
useEffect(() => {
137+
if (currentFile) {
138+
handleBrowserUpload();
139+
}
140+
}, [currentFile]);
141+
83142
return (
84143
<Sheet open={isOpen} onOpenChange={setIsOpen}>
85144
<SheetContent className="flex w-[85%] flex-col justify-between bg-background px-4 text-foreground sm:w-[500px] md:px-5">
@@ -114,31 +173,45 @@ export default function AgreementSheet({
114173
/>
115174
</div>
116175

117-
<div className="w-full space-y-2">
118-
<Label htmlFor="link">Link</Label>
119-
<Input
120-
className="flex w-full rounded-md border-0 bg-background py-1.5 text-foreground shadow-sm ring-1 ring-inset ring-input placeholder:text-muted-foreground focus:ring-2 focus:ring-inset focus:ring-gray-400 sm:text-sm sm:leading-6"
121-
id="link"
122-
type="url"
123-
pattern="https://.*"
124-
name="link"
125-
required
126-
autoComplete="off"
127-
data-1p-ignore
128-
placeholder="https://www.papermark.io/nda"
129-
value={data.link || ""}
130-
onChange={(e) =>
131-
setData({
132-
...data,
133-
link: e.target.value,
134-
})
135-
}
136-
onInvalid={(e) =>
137-
e.currentTarget.setCustomValidity(
138-
"Please enter a valid URL starting with https://",
139-
)
140-
}
141-
/>
176+
<div className="space-y-4">
177+
<div className="w-full space-y-2">
178+
<Label htmlFor="link">Link to an agreement</Label>
179+
<Input
180+
className="flex w-full rounded-md border-0 bg-background py-1.5 text-foreground shadow-sm ring-1 ring-inset ring-input placeholder:text-muted-foreground focus:ring-2 focus:ring-inset focus:ring-gray-400 sm:text-sm sm:leading-6"
181+
id="link"
182+
type="url"
183+
pattern="https://.*"
184+
name="link"
185+
required
186+
autoComplete="off"
187+
data-1p-ignore
188+
placeholder="https://www.papermark.io/nda"
189+
value={data.link || ""}
190+
onChange={(e) =>
191+
setData({
192+
...data,
193+
link: e.target.value,
194+
})
195+
}
196+
onInvalid={(e) =>
197+
e.currentTarget.setCustomValidity(
198+
"Please enter a valid URL starting with https://",
199+
)
200+
}
201+
/>
202+
</div>
203+
204+
<div className="space-y-12">
205+
<div className="space-y-2 pb-6">
206+
<Label>Or upload an agreement</Label>
207+
<div className="grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
208+
<DocumentUpload
209+
currentFile={currentFile}
210+
setCurrentFile={setCurrentFile}
211+
/>
212+
</div>
213+
</div>
214+
</div>
142215
</div>
143216
</div>
144217

components/links/link-sheet/agreement-section.tsx

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,7 @@
1-
import {
2-
Dispatch,
3-
SetStateAction,
4-
useCallback,
5-
useEffect,
6-
useState,
7-
} from "react";
1+
import { Dispatch, SetStateAction, useEffect, useState } from "react";
82

9-
import { Button } from "@react-email/components";
103
import { motion } from "framer-motion";
114

12-
import { Label } from "@/components/ui/label";
135
import {
146
Select,
157
SelectContent,

lib/documents/create-document.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,40 @@ export const createDocument = async ({
4141
return response;
4242
};
4343

44+
export const createAgreementDocument = async ({
45+
documentData,
46+
teamId,
47+
numPages,
48+
folderPathName,
49+
}: {
50+
documentData: DocumentData;
51+
teamId: string;
52+
numPages?: number;
53+
folderPathName?: string;
54+
}) => {
55+
// create a document in the database with the blob url
56+
const response = await fetch(`/api/teams/${teamId}/documents/agreement`, {
57+
method: "POST",
58+
headers: {
59+
"Content-Type": "application/json",
60+
},
61+
body: JSON.stringify({
62+
name: documentData.name,
63+
url: documentData.key,
64+
storageType: documentData.storageType,
65+
numPages: numPages,
66+
folderPathName: folderPathName,
67+
type: documentData.contentType,
68+
}),
69+
});
70+
71+
if (!response.ok) {
72+
throw new Error(`HTTP error! status: ${response.status}`);
73+
}
74+
75+
return response;
76+
};
77+
4478
// create a new version in the database
4579
export const createNewDocumentVersion = async ({
4680
documentData,

pages/api/teams/[teamId]/agreements/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,15 @@ export default async function handle(
8282

8383
const { name, link } = req.body as { name: string; link: string };
8484

85-
const response = await prisma.agreement.create({
85+
const agreement = await prisma.agreement.create({
8686
data: {
8787
teamId,
8888
name,
8989
content: link,
9090
},
9191
});
9292

93-
return res.status(201).json(response);
93+
return res.status(201).json(agreement);
9494
} catch (error) {
9595
log({
9696
message: `Failed to add agreement. \n\n ${error} \n\n*Metadata*: \`{teamId: ${teamId}, userId: ${userId}}\``,

0 commit comments

Comments
 (0)