mirror of
https://github.com/mfts/papermark.git
synced 2025-12-20 01:03:24 +08:00
Onboarding update
This commit is contained in:
1
.cursorignore
Normal file
1
.cursorignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.env
|
||||||
@@ -74,19 +74,28 @@ export default function DataroomTrial() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { dataroomId } = await response.json(); // Assuming the API returns the created dataroom's ID
|
||||||
|
|
||||||
|
if (!dataroomId) {
|
||||||
|
throw new Error("No dataroom ID returned from the server");
|
||||||
|
}
|
||||||
|
|
||||||
analytics.capture("Dataroom Trial Created", {
|
analytics.capture("Dataroom Trial Created", {
|
||||||
dataroomName: "Dataroom Demo Trial",
|
dataroomName: "Dataroom Demo Trial",
|
||||||
industry,
|
industry,
|
||||||
companySize,
|
companySize,
|
||||||
|
dataroomId,
|
||||||
});
|
});
|
||||||
toast.success("Dataroom successfully created! 🎉");
|
toast.success("Dataroom successfully created! 🎉");
|
||||||
|
|
||||||
await mutate(`/api/teams/${teamInfo?.currentTeam?.id}/datarooms`);
|
await mutate(`/api/teams/${teamInfo?.currentTeam?.id}/datarooms`);
|
||||||
router.push("/datarooms");
|
|
||||||
|
// Instead of redirecting to "/datarooms", we'll navigate to the dataroom-upload page
|
||||||
|
router.push(`/welcome?type=dataroom-upload&dataroomId=${dataroomId}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
toast.error("Error adding dataroom. Please try again.");
|
toast.error("Error adding dataroom. Please try again.");
|
||||||
return;
|
console.error("Error creating dataroom:", error);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -236,9 +245,11 @@ export default function DataroomTrial() {
|
|||||||
<button className="underline">Business</button>
|
<button className="underline">Business</button>
|
||||||
</UpgradePlanModal>{" "}
|
</UpgradePlanModal>{" "}
|
||||||
plan. <br /> */}
|
plan. <br /> */}
|
||||||
After the trial, upgrade to{" "}
|
Credit card is not required. After the trial, upgrade to{" "}
|
||||||
<UpgradePlanModal clickedPlan="Business">
|
<UpgradePlanModal clickedPlan="Business">
|
||||||
<button className="underline">Papermark Business</button>
|
<button className="underline">
|
||||||
|
Papermark Business or Data Rooms
|
||||||
|
</button>
|
||||||
</UpgradePlanModal>{" "}
|
</UpgradePlanModal>{" "}
|
||||||
to continue using data rooms.
|
to continue using data rooms.
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
388
components/welcome/dataroom-upload.tsx
Normal file
388
components/welcome/dataroom-upload.tsx
Normal file
@@ -0,0 +1,388 @@
|
|||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import { useTeam } from "@/context/team-context";
|
||||||
|
import { LinkType } from "@prisma/client";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import { usePlausible } from "next-plausible";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
|
import DocumentUpload from "@/components/document-upload";
|
||||||
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionContent,
|
||||||
|
AccordionItem,
|
||||||
|
AccordionTrigger,
|
||||||
|
} from "@/components/ui/accordion";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
import { useAnalytics } from "@/lib/analytics";
|
||||||
|
import { STAGGER_CHILD_VARIANTS } from "@/lib/constants";
|
||||||
|
import { DocumentData, createDocument } from "@/lib/documents/create-document";
|
||||||
|
import { putFile } from "@/lib/files/put-file";
|
||||||
|
import {
|
||||||
|
convertDataUrlToFile,
|
||||||
|
copyToClipboard,
|
||||||
|
uploadImage,
|
||||||
|
} from "@/lib/utils";
|
||||||
|
import { getSupportedContentType } from "@/lib/utils/get-content-type";
|
||||||
|
|
||||||
|
import Skeleton from "../Skeleton";
|
||||||
|
import { DEFAULT_LINK_PROPS, DEFAULT_LINK_TYPE } from "../links/link-sheet";
|
||||||
|
import { LinkOptions } from "../links/link-sheet/link-options";
|
||||||
|
|
||||||
|
interface DataroomUploadProps {
|
||||||
|
dataroomId: string; // Define the dataroomId prop
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DataroomUpload({ dataroomId }: DataroomUploadProps) {
|
||||||
|
const router = useRouter();
|
||||||
|
const plausible = usePlausible();
|
||||||
|
const analytics = useAnalytics();
|
||||||
|
const [uploading, setUploading] = useState<boolean>(false);
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
const [currentFile, setCurrentFile] = useState<File | null>(null);
|
||||||
|
const [currentBlob, setCurrentBlob] = useState<boolean>(false);
|
||||||
|
const [currentLinkId, setCurrentLinkId] = useState<string | null>(null);
|
||||||
|
const [currentDocId, setCurrentDocId] = useState<string | null>(null);
|
||||||
|
const [linkData, setLinkData] = useState<DEFAULT_LINK_TYPE>(
|
||||||
|
DEFAULT_LINK_PROPS(LinkType.DATAROOM_LINK),
|
||||||
|
);
|
||||||
|
const teamInfo = useTeam();
|
||||||
|
|
||||||
|
const teamId = teamInfo?.currentTeam?.id as string;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (dataroomId && !currentLinkId) {
|
||||||
|
fetchOrCreateDataroomLink();
|
||||||
|
}
|
||||||
|
}, [dataroomId, currentLinkId]);
|
||||||
|
|
||||||
|
const fetchOrCreateDataroomLink = async () => {
|
||||||
|
try {
|
||||||
|
const linkResponse = await fetch(
|
||||||
|
`/api/teams/${teamId}/datarooms/${dataroomId}/links`,
|
||||||
|
);
|
||||||
|
if (linkResponse.ok) {
|
||||||
|
const links = await linkResponse.json();
|
||||||
|
if (links.length > 0) {
|
||||||
|
setCurrentLinkId(links[0].id);
|
||||||
|
} else {
|
||||||
|
const createLinkResponse = await fetch(
|
||||||
|
`/api/teams/${teamId}/datarooms/${dataroomId}/links`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ linkType: "DATAROOM_LINK" }),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (createLinkResponse.ok) {
|
||||||
|
const newLink = await createLinkResponse.json();
|
||||||
|
setCurrentLinkId(newLink.id);
|
||||||
|
} else {
|
||||||
|
const errorData = await createLinkResponse.json();
|
||||||
|
toast.error(errorData.message || "Failed to create link.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const errorData = await linkResponse.json();
|
||||||
|
toast.error(errorData.message || "Failed to fetch links.");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching or creating dataroom link:", error);
|
||||||
|
toast.error("Failed to generate dataroom link. Please try again.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFileUpload = async (event: any) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (!currentFile) {
|
||||||
|
toast.error("Please select a file to upload.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setUploading(true);
|
||||||
|
|
||||||
|
const contentType = getSupportedContentType(currentFile.type);
|
||||||
|
|
||||||
|
if (!contentType) {
|
||||||
|
setUploading(false);
|
||||||
|
toast.error(
|
||||||
|
"Unsupported file format. Please upload a PDF or Excel file.",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { type, data, numPages } = await putFile({
|
||||||
|
file: currentFile,
|
||||||
|
teamId,
|
||||||
|
});
|
||||||
|
|
||||||
|
setCurrentFile(null);
|
||||||
|
setCurrentBlob(true);
|
||||||
|
|
||||||
|
const documentData: DocumentData = {
|
||||||
|
name: currentFile.name,
|
||||||
|
key: data!,
|
||||||
|
storageType: type!,
|
||||||
|
contentType: contentType,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await createDocument({ documentData, teamId, numPages });
|
||||||
|
|
||||||
|
if (response) {
|
||||||
|
const document = await response.json();
|
||||||
|
|
||||||
|
// Add document to dataroom
|
||||||
|
await fetch(`/api/teams/${teamId}/datarooms/${dataroomId}/documents`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ documentId: document.id }),
|
||||||
|
});
|
||||||
|
|
||||||
|
plausible("documentUploadedToDataroom");
|
||||||
|
analytics.capture("Document Added to Dataroom", {
|
||||||
|
documentId: document.id,
|
||||||
|
name: document.name,
|
||||||
|
numPages: document.numPages,
|
||||||
|
path: router.asPath,
|
||||||
|
type: document.type,
|
||||||
|
teamId: teamInfo?.currentTeam?.id,
|
||||||
|
dataroomId: dataroomId,
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setCurrentDocId(document.id);
|
||||||
|
setUploading(false);
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("An error occurred while uploading the file: ", error);
|
||||||
|
setCurrentFile(null);
|
||||||
|
setUploading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async (event: any) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
let blobUrl: string | null =
|
||||||
|
linkData.metaImage && linkData.metaImage.startsWith("data:")
|
||||||
|
? null
|
||||||
|
: linkData.metaImage;
|
||||||
|
if (linkData.metaImage && linkData.metaImage.startsWith("data:")) {
|
||||||
|
const blob = convertDataUrlToFile({ dataUrl: linkData.metaImage });
|
||||||
|
blobUrl = await uploadImage(blob);
|
||||||
|
setLinkData({ ...linkData, metaImage: blobUrl });
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`/api/links/${currentLinkId}`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
...linkData,
|
||||||
|
metaImage: blobUrl,
|
||||||
|
targetId: dataroomId,
|
||||||
|
linkType: "DATAROOM_LINK",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const { error } = await response.json();
|
||||||
|
toast.error(error);
|
||||||
|
setIsLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
copyToClipboard(
|
||||||
|
`${process.env.NEXT_PUBLIC_MARKETING_URL}/view/${currentLinkId}`,
|
||||||
|
"Link copied to clipboard. Redirecting to dataroom page...",
|
||||||
|
);
|
||||||
|
|
||||||
|
router.push(`/datarooms/${dataroomId}`);
|
||||||
|
setIsLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{!currentBlob && (
|
||||||
|
<motion.div
|
||||||
|
className="z-10 flex flex-col space-y-10 text-center"
|
||||||
|
variants={{
|
||||||
|
hidden: { opacity: 0, scale: 0.95 },
|
||||||
|
show: {
|
||||||
|
opacity: 1,
|
||||||
|
scale: 1,
|
||||||
|
transition: {
|
||||||
|
staggerChildren: 0.2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
initial="hidden"
|
||||||
|
animate="show"
|
||||||
|
exit="hidden"
|
||||||
|
transition={{ duration: 0.3, type: "spring" }}
|
||||||
|
>
|
||||||
|
<motion.div
|
||||||
|
variants={STAGGER_CHILD_VARIANTS}
|
||||||
|
className="flex flex-col items-center space-y-10 text-center"
|
||||||
|
>
|
||||||
|
<h1 className="font-display max-w-lg text-3xl font-semibold text-foreground transition-colors sm:text-4xl">
|
||||||
|
Upload first document to your data room
|
||||||
|
</h1>
|
||||||
|
</motion.div>
|
||||||
|
<motion.div variants={STAGGER_CHILD_VARIANTS}>
|
||||||
|
<main className="mt-8">
|
||||||
|
<form
|
||||||
|
encType="multipart/form-data"
|
||||||
|
onSubmit={handleFileUpload}
|
||||||
|
className="flex flex-col"
|
||||||
|
>
|
||||||
|
<div className="space-y-12">
|
||||||
|
<div className="pb-6">
|
||||||
|
<div className="mt-10 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
|
||||||
|
<DocumentUpload
|
||||||
|
currentFile={currentFile}
|
||||||
|
setCurrentFile={setCurrentFile}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
className="w-full"
|
||||||
|
loading={uploading}
|
||||||
|
disabled={!currentFile}
|
||||||
|
>
|
||||||
|
{uploading ? "Uploading..." : "Upload Document"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div className="text-xs text-muted-foreground">
|
||||||
|
<span>Use our</span>{" "}
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
className="px-0 text-xs font-normal text-muted-foreground underline hover:text-gray-700"
|
||||||
|
onClick={async () => {
|
||||||
|
const response = await fetch(
|
||||||
|
"/_example/papermark-example-document.pdf",
|
||||||
|
);
|
||||||
|
const blob = await response.blob();
|
||||||
|
const file = new File(
|
||||||
|
[blob],
|
||||||
|
"papermark-example-document.pdf",
|
||||||
|
{
|
||||||
|
type: "application/pdf",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
setCurrentFile(file);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
sample document
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{currentBlob && (
|
||||||
|
<motion.div
|
||||||
|
className="z-10 flex flex-col space-y-10 text-center"
|
||||||
|
variants={{
|
||||||
|
hidden: { opacity: 0, scale: 0.95 },
|
||||||
|
show: {
|
||||||
|
opacity: 1,
|
||||||
|
scale: 1,
|
||||||
|
transition: {
|
||||||
|
staggerChildren: 0.2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
initial="hidden"
|
||||||
|
animate="show"
|
||||||
|
exit="hidden"
|
||||||
|
transition={{ duration: 0.3, type: "spring" }}
|
||||||
|
>
|
||||||
|
<motion.div
|
||||||
|
variants={STAGGER_CHILD_VARIANTS}
|
||||||
|
className="flex flex-col items-center space-y-10 text-center"
|
||||||
|
>
|
||||||
|
<h1 className="font-display text-3xl font-semibold text-foreground transition-colors sm:text-4xl">
|
||||||
|
Share your dataroom link
|
||||||
|
</h1>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<motion.div variants={STAGGER_CHILD_VARIANTS}>
|
||||||
|
{!currentLinkId && (
|
||||||
|
<main className="min-h-[300px]">
|
||||||
|
<div className="flex flex-col justify-center">
|
||||||
|
<div className="flex py-8">
|
||||||
|
<div className="flex w-full focus-within:z-10">
|
||||||
|
<Skeleton className="h-6 w-full" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
)}
|
||||||
|
{currentLinkId && (
|
||||||
|
<main className="min-h-[300px]">
|
||||||
|
<div className="flex flex-col justify-center">
|
||||||
|
<div className="relative">
|
||||||
|
<div className="flex py-8">
|
||||||
|
<div className="flex w-full max-w-xs focus-within:z-10 sm:max-w-lg">
|
||||||
|
<p className="block w-full overflow-y-scroll rounded-md border-0 bg-secondary px-4 py-1.5 text-left leading-6 text-secondary-foreground md:min-w-[500px]">
|
||||||
|
{`${process.env.NEXT_PUBLIC_MARKETING_URL}/view/${currentLinkId}`}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="w-full max-w-xs pb-8 sm:max-w-lg">
|
||||||
|
<Accordion type="single" collapsible>
|
||||||
|
<AccordionItem value="item-1" className="border-none">
|
||||||
|
<AccordionTrigger className="space-x-2 rounded-lg py-0">
|
||||||
|
<span className="text-sm font-medium leading-6 text-foreground">
|
||||||
|
Configure Link Options
|
||||||
|
</span>
|
||||||
|
</AccordionTrigger>
|
||||||
|
<AccordionContent className="text-left first:pt-5">
|
||||||
|
<LinkOptions
|
||||||
|
data={linkData}
|
||||||
|
setData={setLinkData}
|
||||||
|
linkType={LinkType.DATAROOM_LINK}
|
||||||
|
/>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
</Accordion>
|
||||||
|
</div>
|
||||||
|
<div className="mb-4 flex items-center justify-center">
|
||||||
|
<Button onClick={handleSubmit} loading={isLoading}>
|
||||||
|
Share Dataroom
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-muted-foreground">
|
||||||
|
<span> One link to share multiple files</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
)}
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -55,7 +55,7 @@ export default function Dataroom() {
|
|||||||
controls
|
controls
|
||||||
>
|
>
|
||||||
<source
|
<source
|
||||||
src="https://assets.papermark.io/short-video.mp4"
|
src="https://assets.papermark.io/upload/file_A4qNV68jr3MAUayMNi3WmY-Data-Room-demo-2.mp4"
|
||||||
type="video/mp4"
|
type="video/mp4"
|
||||||
/>
|
/>
|
||||||
</video>
|
</video>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { motion } from "framer-motion";
|
|||||||
import {
|
import {
|
||||||
File as DocumentIcon,
|
File as DocumentIcon,
|
||||||
Presentation as PresentationChartBarIcon,
|
Presentation as PresentationChartBarIcon,
|
||||||
|
ServerIcon,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
import NotionIcon from "@/components/shared/icons/notion";
|
import NotionIcon from "@/components/shared/icons/notion";
|
||||||
@@ -38,58 +39,44 @@ export default function Next() {
|
|||||||
Papermark
|
Papermark
|
||||||
</p>
|
</p>
|
||||||
<h1 className="font-display max-w-md text-3xl font-semibold transition-colors sm:text-4xl">
|
<h1 className="font-display max-w-md text-3xl font-semibold transition-colors sm:text-4xl">
|
||||||
Which document do you want to share today?
|
What do you want to share today?
|
||||||
</h1>
|
</h1>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
<motion.div
|
<motion.div
|
||||||
variants={STAGGER_CHILD_VARIANTS}
|
variants={STAGGER_CHILD_VARIANTS}
|
||||||
className="grid w-full grid-cols-1 divide-y divide-border rounded-md border border-border text-foreground md:grid-cols-3 md:divide-x"
|
className="grid w-full grid-cols-1 divide-y divide-border rounded-md border border-border text-foreground md:grid-cols-2 md:divide-x"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
router.push({
|
router.push({
|
||||||
pathname: "/welcome",
|
pathname: "/welcome",
|
||||||
query: {
|
query: {
|
||||||
type: "pitchdeck",
|
type: "select",
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
className="flex min-h-[200px] flex-col items-center justify-center space-y-5 overflow-hidden p-5 transition-colors hover:bg-gray-200 hover:dark:bg-gray-800 md:p-10"
|
|
||||||
>
|
|
||||||
<PresentationChartBarIcon className="pointer-events-none h-auto w-12 sm:w-12" />
|
|
||||||
<p>Pitchdeck</p>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() =>
|
|
||||||
router.push({
|
|
||||||
pathname: "/welcome",
|
|
||||||
query: {
|
|
||||||
type: "document",
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
className="flex min-h-[200px] flex-col items-center justify-center space-y-5 overflow-hidden p-5 transition-colors hover:bg-gray-200 hover:dark:bg-gray-800 md:p-10"
|
className="flex min-h-[200px] flex-col items-center justify-center space-y-5 overflow-hidden p-5 transition-colors hover:bg-gray-200 hover:dark:bg-gray-800 md:p-10"
|
||||||
>
|
>
|
||||||
<DocumentIcon className="pointer-events-none h-auto w-12 sm:w-12" />
|
<DocumentIcon className="pointer-events-none h-auto w-12 sm:w-12" />
|
||||||
<p>Another document</p>
|
<p>Document</p>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
router.push({
|
router.push({
|
||||||
pathname: "/welcome",
|
pathname: "/welcome",
|
||||||
query: {
|
query: {
|
||||||
type: "notion",
|
type: "dataroom",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
className="flex min-h-[200px] flex-col items-center justify-center space-y-5 overflow-hidden p-5 transition-colors hover:bg-gray-200 hover:dark:bg-gray-800 md:p-10"
|
className="flex min-h-[200px] flex-col items-center justify-center space-y-5 overflow-hidden p-5 transition-colors hover:bg-gray-200 hover:dark:bg-gray-800 md:p-10"
|
||||||
>
|
>
|
||||||
<NotionIcon className="pointer-events-none h-auto w-12 sm:w-12" />
|
<ServerIcon className="pointer-events-none h-auto w-12 sm:w-12" />
|
||||||
<p>Notion Page</p>
|
<p>Data Room</p>
|
||||||
</button>
|
</button>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
<motion.div variants={STAGGER_CHILD_VARIANTS} className="text-center">
|
{/* <motion.div variants={STAGGER_CHILD_VARIANTS} className="text-center">
|
||||||
<button
|
<button
|
||||||
className="text-center text-sm text-muted-foreground underline-offset-4 transition-all hover:text-gray-800 hover:underline hover:dark:text-muted-foreground/80"
|
className="text-center text-sm text-muted-foreground underline-offset-4 transition-all hover:text-gray-800 hover:underline hover:dark:text-muted-foreground/80"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
@@ -101,9 +88,9 @@ export default function Next() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Looking to create and share a full dataroom?
|
Sharing Data Room is possible in 7 day free trial
|
||||||
</button>
|
</button>
|
||||||
</motion.div>
|
</motion.div> */}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
126
components/welcome/select.tsx
Normal file
126
components/welcome/select.tsx
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import {
|
||||||
|
File as DocumentIcon,
|
||||||
|
Presentation as PresentationChartBarIcon,
|
||||||
|
} from "lucide-react";
|
||||||
|
|
||||||
|
import NotionIcon from "@/components/shared/icons/notion";
|
||||||
|
|
||||||
|
import { STAGGER_CHILD_VARIANTS } from "@/lib/constants";
|
||||||
|
|
||||||
|
export default function Next() {
|
||||||
|
const router = useRouter();
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
className="z-10 mx-5 flex flex-col items-center space-y-10 text-center sm:mx-auto"
|
||||||
|
variants={{
|
||||||
|
hidden: { opacity: 0, scale: 0.95 },
|
||||||
|
show: {
|
||||||
|
opacity: 1,
|
||||||
|
scale: 1,
|
||||||
|
transition: {
|
||||||
|
staggerChildren: 0.2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
initial="hidden"
|
||||||
|
animate="show"
|
||||||
|
exit="hidden"
|
||||||
|
transition={{ duration: 0.3, type: "spring" }}
|
||||||
|
>
|
||||||
|
<motion.div
|
||||||
|
variants={STAGGER_CHILD_VARIANTS}
|
||||||
|
className="flex flex-col items-center space-y-10 text-center"
|
||||||
|
>
|
||||||
|
<p className="text-2xl font-bold tracking-tighter text-foreground">
|
||||||
|
Papermark
|
||||||
|
</p>
|
||||||
|
<h1 className="font-display max-w-md text-3xl font-semibold transition-colors sm:text-4xl">
|
||||||
|
Which document do you want to share today?
|
||||||
|
</h1>
|
||||||
|
</motion.div>
|
||||||
|
<motion.div
|
||||||
|
variants={STAGGER_CHILD_VARIANTS}
|
||||||
|
className="grid w-full grid-cols-1 divide-y divide-border rounded-md border border-border text-foreground md:grid-cols-4 md:divide-x"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
router.push({
|
||||||
|
pathname: "/welcome",
|
||||||
|
query: {
|
||||||
|
type: "pitchdeck",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="flex min-h-[200px] flex-col items-center justify-center space-y-5 overflow-hidden p-5 transition-colors hover:bg-gray-200 hover:dark:bg-gray-800 md:p-10"
|
||||||
|
>
|
||||||
|
<PresentationChartBarIcon className="pointer-events-none h-auto w-12 sm:w-12" />
|
||||||
|
<p>Pitchdeck</p>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
router.push({
|
||||||
|
pathname: "/welcome",
|
||||||
|
query: {
|
||||||
|
type: "document",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="flex min-h-[200px] flex-col items-center justify-center space-y-5 overflow-hidden p-5 transition-colors hover:bg-gray-200 hover:dark:bg-gray-800 md:p-10"
|
||||||
|
>
|
||||||
|
<DocumentIcon className="pointer-events-none h-auto w-12 sm:w-12" />
|
||||||
|
<p>Sales document</p>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
router.push({
|
||||||
|
pathname: "/welcome",
|
||||||
|
query: {
|
||||||
|
type: "notion",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="flex min-h-[200px] flex-col items-center justify-center space-y-5 overflow-hidden p-5 transition-colors hover:bg-gray-200 hover:dark:bg-gray-800 md:p-10"
|
||||||
|
>
|
||||||
|
<NotionIcon className="pointer-events-none h-auto w-12 sm:w-12" />
|
||||||
|
<p>Notion Page</p>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
router.push({
|
||||||
|
pathname: "/welcome",
|
||||||
|
query: {
|
||||||
|
type: "document",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="flex min-h-[200px] flex-col items-center justify-center space-y-5 overflow-hidden p-5 transition-colors hover:bg-gray-200 hover:dark:bg-gray-800 md:p-10"
|
||||||
|
>
|
||||||
|
<DocumentIcon className="pointer-events-none h-auto w-12 sm:w-12" />
|
||||||
|
<p>Another document</p>
|
||||||
|
</button>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<motion.div variants={STAGGER_CHILD_VARIANTS} className="text-center">
|
||||||
|
<button
|
||||||
|
className="text-center text-sm text-muted-foreground underline-offset-4 transition-all hover:text-gray-800 hover:underline hover:dark:text-muted-foreground/80"
|
||||||
|
onClick={() =>
|
||||||
|
router.push({
|
||||||
|
pathname: "/welcome",
|
||||||
|
query: {
|
||||||
|
type: "dataroom",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
You can start by sharing documents an switch to data room creation
|
||||||
|
later.
|
||||||
|
</button>
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -315,7 +315,7 @@ export default function Upload() {
|
|||||||
Configure Link Options
|
Configure Link Options
|
||||||
</span>
|
</span>
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent className="first:pt-5">
|
<AccordionContent className="text-left first:pt-5">
|
||||||
<LinkOptions
|
<LinkOptions
|
||||||
data={linkData}
|
data={linkData}
|
||||||
setData={setLinkData}
|
setData={setLinkData}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ export default async function handler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dataroomLink) {
|
if (dataroomLink) {
|
||||||
|
console.log("Creating dataroom link for dataroomId:", targetId);
|
||||||
const dataroom = await prisma.dataroom.findUnique({
|
const dataroom = await prisma.dataroom.findUnique({
|
||||||
where: {
|
where: {
|
||||||
id: targetId,
|
id: targetId,
|
||||||
@@ -68,8 +69,11 @@ export default async function handler(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!dataroom) {
|
if (!dataroom) {
|
||||||
|
console.error("Dataroom not found for id:", targetId);
|
||||||
return res.status(400).json({ error: "Dataroom not found." });
|
return res.status(400).json({ error: "Dataroom not found." });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("Dataroom found:", dataroom);
|
||||||
}
|
}
|
||||||
|
|
||||||
const hashedPassword =
|
const hashedPassword =
|
||||||
@@ -122,64 +126,67 @@ export default async function handler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the link and its related document from the database
|
// Fetch the link and its related document from the database
|
||||||
|
console.log("Creating link with data:", {
|
||||||
|
dataroomId: dataroomLink ? targetId : undefined,
|
||||||
|
linkType,
|
||||||
|
// ... other link properties
|
||||||
|
});
|
||||||
|
|
||||||
const link = await prisma.link.create({
|
const link = await prisma.link.create({
|
||||||
data: {
|
data: {
|
||||||
documentId: documentLink ? targetId : null,
|
|
||||||
dataroomId: dataroomLink ? targetId : null,
|
|
||||||
linkType,
|
linkType,
|
||||||
password: hashedPassword,
|
password: hashedPassword,
|
||||||
name: linkData.name || null,
|
name: linkDomainData.name || null,
|
||||||
emailProtected: linkData.emailProtected,
|
emailProtected: linkDomainData.emailProtected,
|
||||||
emailAuthenticated: linkData.emailAuthenticated,
|
emailAuthenticated: linkDomainData.emailAuthenticated,
|
||||||
expiresAt: exat,
|
expiresAt: exat,
|
||||||
allowDownload: linkData.allowDownload,
|
allowDownload: linkDomainData.allowDownload,
|
||||||
domainId: domainObj?.id || null,
|
|
||||||
domainSlug: domain || null,
|
domainSlug: domain || null,
|
||||||
slug: slug || null,
|
slug: slug || null,
|
||||||
enableNotification: linkData.enableNotification,
|
enableNotification: linkDomainData.enableNotification,
|
||||||
enableFeedback: linkData.enableFeedback,
|
enableFeedback: linkDomainData.enableFeedback,
|
||||||
enableScreenshotProtection: linkData.enableScreenshotProtection,
|
enableScreenshotProtection: linkDomainData.enableScreenshotProtection,
|
||||||
enableCustomMetatag: linkData.enableCustomMetatag,
|
enableCustomMetatag: linkDomainData.enableCustomMetatag,
|
||||||
metaTitle: linkData.metaTitle || null,
|
metaTitle: linkDomainData.metaTitle || null,
|
||||||
metaDescription: linkData.metaDescription || null,
|
metaDescription: linkDomainData.metaDescription || null,
|
||||||
metaImage: linkData.metaImage || null,
|
metaImage: linkDomainData.metaImage || null,
|
||||||
allowList: linkData.allowList,
|
allowList: linkDomainData.allowList,
|
||||||
denyList: linkData.denyList,
|
denyList: linkDomainData.denyList,
|
||||||
...(linkData.enableQuestion && {
|
...(dataroomLink
|
||||||
enableQuestion: linkData.enableQuestion,
|
? {
|
||||||
feedback: {
|
dataroom: {
|
||||||
create: {
|
connect: { id: targetId },
|
||||||
data: {
|
|
||||||
question: linkData.questionText,
|
|
||||||
type: linkData.questionType,
|
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
},
|
: {}),
|
||||||
}),
|
...(documentLink
|
||||||
...(linkData.enableAgreement && {
|
? {
|
||||||
enableAgreement: linkData.enableAgreement,
|
document: {
|
||||||
agreementId: linkData.agreementId,
|
connect: { id: targetId },
|
||||||
}),
|
},
|
||||||
...(linkData.enableWatermark && {
|
}
|
||||||
enableWatermark: linkData.enableWatermark,
|
: {}),
|
||||||
watermarkConfig: linkData.watermarkConfig,
|
...(domainObj
|
||||||
}),
|
? {
|
||||||
showBanner: linkData.showBanner,
|
domain: {
|
||||||
|
connect: { id: domainObj.id },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log("Link created:", link);
|
||||||
|
|
||||||
const linkWithView = {
|
const linkWithView = {
|
||||||
...link,
|
...link,
|
||||||
_count: { views: 0 },
|
_count: { views: 0 },
|
||||||
views: [],
|
views: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!linkWithView) {
|
|
||||||
return res.status(404).json({ error: "Link not found" });
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.status(200).json(linkWithView);
|
return res.status(200).json(linkWithView);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error("Error creating link:", error);
|
||||||
errorhandler(error, res);
|
errorhandler(error, res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,10 @@ export default async function handle(
|
|||||||
|
|
||||||
waitUntil(sendDataroomTrialWelcome({ fullName, to: email! }));
|
waitUntil(sendDataroomTrialWelcome({ fullName, to: email! }));
|
||||||
|
|
||||||
res.status(201).json(dataroomWithCount);
|
res.status(201).json({
|
||||||
|
...dataroomWithCount,
|
||||||
|
dataroomId: dataroom.id, // Add this line to include the dataroomId
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Request error", error);
|
console.error("Request error", error);
|
||||||
res.status(500).json({ error: "Error creating dataroom" });
|
res.status(500).json({ error: "Error creating dataroom" });
|
||||||
|
|||||||
@@ -6,14 +6,26 @@ import { ArrowLeft as ArrowLeftIcon } from "lucide-react";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import Dataroom from "@/components/welcome/dataroom";
|
import Dataroom from "@/components/welcome/dataroom";
|
||||||
import DataroomTrial from "@/components/welcome/dataroom-trial";
|
import DataroomTrial from "@/components/welcome/dataroom-trial";
|
||||||
|
import DataroomUpload from "@/components/welcome/dataroom-upload";
|
||||||
import Intro from "@/components/welcome/intro";
|
import Intro from "@/components/welcome/intro";
|
||||||
import Next from "@/components/welcome/next";
|
import Next from "@/components/welcome/next";
|
||||||
import NotionForm from "@/components/welcome/notion-form";
|
import NotionForm from "@/components/welcome/notion-form";
|
||||||
|
import Select from "@/components/welcome/select";
|
||||||
import Upload from "@/components/welcome/upload";
|
import Upload from "@/components/welcome/upload";
|
||||||
|
|
||||||
export default function Welcome() {
|
export default function Welcome() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
const isDataroomUpload = router.query.type === "dataroom-upload";
|
||||||
|
|
||||||
|
const skipButtonText = isDataroomUpload
|
||||||
|
? "Skip to dataroom"
|
||||||
|
: "Skip to dashboard";
|
||||||
|
const skipButtonPath =
|
||||||
|
isDataroomUpload && router.query.dataroomId
|
||||||
|
? `/datarooms/${router.query.dataroomId}`
|
||||||
|
: "/documents";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto flex h-screen max-w-3xl flex-col items-center justify-center overflow-x-hidden">
|
<div className="mx-auto flex h-screen max-w-3xl flex-col items-center justify-center overflow-x-hidden">
|
||||||
<div
|
<div
|
||||||
@@ -40,16 +52,17 @@ export default function Welcome() {
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant={"link"}
|
variant={"link"}
|
||||||
onClick={() => router.push("/documents")}
|
onClick={() => router.push(skipButtonPath)}
|
||||||
className="absolute right-2 top-10 z-40 p-2 text-muted-foreground sm:right-10"
|
className="absolute right-2 top-10 z-40 p-2 text-muted-foreground sm:right-10"
|
||||||
>
|
>
|
||||||
Skip to dashboard
|
{skipButtonText}
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<Intro key="intro" />
|
<Intro key="intro" />
|
||||||
)}
|
)}
|
||||||
{router.query.type === "next" && <Next key="next" />}
|
{router.query.type === "next" && <Next key="next" />}
|
||||||
|
{router.query.type === "select" && <Select key="select" />}
|
||||||
{router.query.type === "pitchdeck" && <Upload key="pitchdeck" />}
|
{router.query.type === "pitchdeck" && <Upload key="pitchdeck" />}
|
||||||
{router.query.type === "document" && <Upload key="document" />}
|
{router.query.type === "document" && <Upload key="document" />}
|
||||||
{router.query.type === "notion" && <NotionForm key="notion" />}
|
{router.query.type === "notion" && <NotionForm key="notion" />}
|
||||||
@@ -57,6 +70,12 @@ export default function Welcome() {
|
|||||||
{router.query.type === "dataroom-trial" && (
|
{router.query.type === "dataroom-trial" && (
|
||||||
<DataroomTrial key="dataroom-trial" />
|
<DataroomTrial key="dataroom-trial" />
|
||||||
)}
|
)}
|
||||||
|
{router.query.type === "dataroom-upload" && router.query.dataroomId && (
|
||||||
|
<DataroomUpload
|
||||||
|
key="dataroom-upload"
|
||||||
|
dataroomId={router.query.dataroomId as string}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user