Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion components/links/link-sheet/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export const DEFAULT_LINK_PROPS = (
enableAgreement: false,
agreementId: null,
showBanner: linkType === LinkType.DOCUMENT_LINK ? showBanner : false,
securedByPapermark: linkType === LinkType.DATAROOM_LINK ? true : false,
enableWatermark: false,
watermarkConfig: null,
audienceType: groupId ? LinkAudienceType.GROUP : LinkAudienceType.GENERAL,
Expand Down Expand Up @@ -120,6 +121,7 @@ export type DEFAULT_LINK_TYPE = {
enableAgreement: boolean; // agreement
agreementId: string | null;
showBanner: boolean;
securedByPapermark: boolean;
enableWatermark: boolean;
watermarkConfig: WatermarkConfig | null;
audienceType: LinkAudienceType;
Expand Down Expand Up @@ -765,4 +767,4 @@ export default function LinkSheet({
</SheetContent>
</Sheet>
);
}
}
8 changes: 7 additions & 1 deletion components/links/link-sheet/link-options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import CustomFieldsSection from "./custom-fields-section";
import IndexFileSection from "./index-file-section";
import QuestionSection from "./question-section";
import ScreenshotProtectionSection from "./screenshot-protection-section";
import SecuredByPapermarkSection from "./secured-by-papermark-section";
import UploadSection from "./upload-section";
import WatermarkSection from "./watermark-section";

Expand Down Expand Up @@ -301,7 +302,12 @@ export const LinkOptions = ({
handleUpgradeStateChange={handleUpgradeStateChange}
/>
) : null}

{linkType === LinkType.DATAROOM_LINK && (
<SecuredByPapermarkSection
{...{ data, setData }}
presets={currentPreset}
/>
)}
<UpgradePlanModal
clickedPlan={upgradePlan}
open={openUpgradeModal}
Expand Down
51 changes: 51 additions & 0 deletions components/links/link-sheet/secured-by-papermark-section.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { useEffect, useState } from "react";

import { LinkPreset } from "@prisma/client";

import { DEFAULT_LINK_TYPE } from ".";
import LinkItem from "./link-item";

export default function SecuredByPapermarkSection({
data,
setData,
presets,
}: {
data: DEFAULT_LINK_TYPE;
setData: React.Dispatch<React.SetStateAction<DEFAULT_LINK_TYPE>>;
presets: LinkPreset | null;
}) {
const { securedByPapermark } = data;
const [enabled, setEnabled] = useState<boolean>(false);

useEffect(() => {
setEnabled(securedByPapermark ?? false);
}, [securedByPapermark]);

useEffect(() => {
if (presets?.securedByPapermark) {
setEnabled(true);
setData((prevData) => ({
...prevData,
securedByPapermark: true,
}));
}
}, [presets, setData]);

const handleSecuredByPapermarkToggle = () => {
const updatedSecuredByPapermark = !enabled;
setData({ ...data, securedByPapermark: updatedSecuredByPapermark });
setEnabled(updatedSecuredByPapermark);
};

return (
<div className="pb-5">
<LinkItem
title="Secured by Papermark"
tooltipContent="Show a subtle footer with 'Secured by Papermark' branding in your dataroom"
enabled={enabled}
action={handleSecuredByPapermarkToggle}
isAllowed={true}
/>
</div>
);
}
1 change: 1 addition & 0 deletions components/links/links-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ export default function LinksTable({
enableAgreement: link.enableAgreement ? link.enableAgreement : false,
agreementId: link.agreementId,
showBanner: link.showBanner ?? false,
securedByPapermark: link.securedByPapermark ?? false,
enableWatermark: link.enableWatermark ?? false,
watermarkConfig: link.watermarkConfig as WatermarkConfig | null,
audienceType: link.audienceType,
Expand Down
2 changes: 2 additions & 0 deletions components/view/dataroom/dataroom-document-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import AccessForm, {
import { useDisablePrint } from "@/lib/hooks/use-disable-print";

import EmailVerificationMessage from "../access-form/email-verification-form";
import { SecuredByPapermark } from "../secured-by-papermark";
import ViewData, { TViewDocumentData } from "../view-data";

type RowData = { [key: string]: any };
Expand Down Expand Up @@ -357,6 +358,7 @@ export default function DataroomDocumentView({
<LoadingSpinner className="h-20 w-20" />
</div>
)}
{link.securedByPapermark && <SecuredByPapermark linkId={link.id} />}
</div>
);
}
1 change: 1 addition & 0 deletions components/view/dataroom/dataroom-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ export default function DataroomView({
userEmail ??
undefined
}
securedByPapermark={!!link.securedByPapermark}
/>
</div>
);
Expand Down
45 changes: 45 additions & 0 deletions components/view/secured-by-papermark.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useState } from "react";

import { CircleXIcon } from "lucide-react";
import { createPortal } from "react-dom";

export const SecuredByPapermark = ({
linkId,
isClosable = true,
}: {
linkId: string;
isClosable?: boolean;
}) => {
const [visible, setVisible] = useState(true);
if (!visible) return null;
return createPortal(
<div className="fixed bottom-0 left-0 right-0 z-[100] border-t border-gray-200 bg-white dark:border-gray-800 dark:bg-black">
<div className="flex items-center justify-between px-12 py-2 text-xs text-gray-500 dark:text-gray-400">
<div className="flex items-center space-x-2">
<span>© {new Date().getFullYear()} All rights reserved</span>
</div>
<div className="flex items-center space-x-1.5">
<span>Secured by</span>
<a
href={`https://www.papermark.com?utm_campaign=securedby&utm_medium=securedby&utm_source=papermark-${linkId}`}
target="_blank"
rel="noopener noreferrer"
className="font-semibold text-gray-700 transition-colors hover:text-gray-900 dark:text-gray-300 dark:hover:text-gray-100"
>
Papermark
</a>
</div>
{isClosable && (
<button
onClick={() => setVisible(false)}
className="absolute right-4 rounded text-lg text-gray-400 hover:text-gray-700 dark:hover:text-gray-200"
aria-label="Close"
>
<CircleXIcon className="h-4 w-4" />
</button>
)}
</div>
</div>,
document.body,
);
Comment on lines +15 to +44
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add SSR safety check for document.body access

The component looks well-implemented with proper portal usage and styling. However, accessing document.body directly can cause issues during server-side rendering.

Add a safety check to ensure the component only renders on the client side:

  if (!visible) return null;
+ if (typeof window === 'undefined') return null;
  return createPortal(

This prevents SSR errors when document.body is not available.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return createPortal(
<div className="fixed bottom-0 left-0 right-0 z-[100] border-t border-gray-200 bg-white dark:border-gray-800 dark:bg-black">
<div className="flex items-center justify-between px-12 py-2 text-xs text-gray-500 dark:text-gray-400">
<div className="flex items-center space-x-2">
<span>© {new Date().getFullYear()} All rights reserved</span>
</div>
<div className="flex items-center space-x-1.5">
<span>Secured by</span>
<a
href={`https://www.papermark.com?utm_campaign=securedby&utm_medium=securedby&utm_source=papermark-${linkId}`}
target="_blank"
rel="noopener noreferrer"
className="font-semibold text-gray-700 transition-colors hover:text-gray-900 dark:text-gray-300 dark:hover:text-gray-100"
>
Papermark
</a>
</div>
{isClosable && (
<button
onClick={() => setVisible(false)}
className="absolute right-4 rounded text-lg text-gray-400 hover:text-gray-700 dark:hover:text-gray-200"
aria-label="Close"
>
<CircleXIcon className="h-4 w-4" />
</button>
)}
</div>
</div>,
document.body,
);
if (!visible) return null;
if (typeof window === 'undefined') return null;
return createPortal(
<div className="fixed bottom-0 left-0 right-0 z-[100] border-t border-gray-200 bg-white dark:border-gray-800 dark:bg-black">
<div className="flex items-center justify-between px-12 py-2 text-xs text-gray-500 dark:text-gray-400">
<div className="flex items-center space-x-2">
<span>© {new Date().getFullYear()} All rights reserved</span>
</div>
<div className="flex items-center space-x-1.5">
<span>Secured by</span>
<a
href={`https://www.papermark.com?utm_campaign=securedby&utm_medium=securedby&utm_source=papermark-${linkId}`}
target="_blank"
rel="noopener noreferrer"
className="font-semibold text-gray-700 transition-colors hover:text-gray-900 dark:text-gray-300 dark:hover:text-gray-100"
>
Papermark
</a>
</div>
{isClosable && (
<button
onClick={() => setVisible(false)}
className="absolute right-4 rounded text-lg text-gray-400 hover:text-gray-700 dark:hover:text-gray-200"
aria-label="Close"
>
<CircleXIcon className="h-4 w-4" />
</button>
)}
</div>
</div>,
document.body,
);
🤖 Prompt for AI Agents
In components/view/secured-by-papermark.tsx around lines 15 to 44, the code
directly accesses document.body for createPortal, which causes errors during
server-side rendering because document is undefined on the server. To fix this,
add a client-side check to ensure the component only attempts to render the
portal when running in the browser, such as by using a state variable that is
set to true inside a useEffect hook or by checking if typeof window !==
'undefined' before accessing document.body. This will prevent SSR errors by
avoiding document access on the server.

};
12 changes: 11 additions & 1 deletion components/view/viewer/dataroom-viewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import { DocumentUploadModal } from "../dataroom/document-upload-modal";
import FolderCard from "../dataroom/folder-card";
import IndexFileDialog from "../dataroom/index-file-dialog";
import DataroomNav from "../dataroom/nav-dataroom";
import { SecuredByPapermark } from "../secured-by-papermark";

type FolderOrDocument =
| (DataroomFolder & { allowDownload: boolean })
Expand Down Expand Up @@ -97,6 +98,7 @@ export default function DataroomViewer({
enableIndexFile,
isEmbedded,
viewerEmail,
securedByPapermark,
}: {
brand: Partial<DataroomBrand>;
viewId?: string;
Expand All @@ -112,6 +114,7 @@ export default function DataroomViewer({
enableIndexFile?: boolean;
isEmbedded?: boolean;
viewerEmail?: string;
securedByPapermark?: boolean;
}) {
const { documents, folders } = dataroom as {
documents: DataroomDocument[];
Expand Down Expand Up @@ -336,7 +339,11 @@ export default function DataroomViewer({
isTeamMember={viewData.isTeamMember}
/>
<div
style={{ height: "calc(100vh - 64px)" }}
style={{
height: securedByPapermark
? "calc(100vh - 64px - 40px)"
: "calc(100vh - 64px)",
}}
className="relative flex items-center bg-white dark:bg-black"
>
<div className="relative mx-auto flex h-full w-full items-start justify-center">
Expand Down Expand Up @@ -484,6 +491,9 @@ export default function DataroomViewer({
</ScrollArea>
</div>
</div>
{securedByPapermark && (
<SecuredByPapermark linkId={linkId} isClosable={false} />
)}
</>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import PasswordSection from "@/components/links/link-sheet/password-section";
import { ProBannerSection } from "@/components/links/link-sheet/pro-banner-section";
import QuestionSection from "@/components/links/link-sheet/question-section";
import ScreenshotProtectionSection from "@/components/links/link-sheet/screenshot-protection-section";
import SecuredByPapermarkSection from "@/components/links/link-sheet/secured-by-papermark-section";
import WatermarkSection from "@/components/links/link-sheet/watermark-section";
import ChevronDown from "@/components/shared/icons/chevron-down";

Expand Down Expand Up @@ -86,6 +87,11 @@ export const OnboardingDataroomLinkOptions = ({
handleUpgradeStateChange={() => {}}
presets={currentPreset}
/>
<SecuredByPapermarkSection
data={data}
setData={setData}
presets={currentPreset}
/>
<div className="mb-4 mt-2">
<button
type="button"
Expand Down
1 change: 1 addition & 0 deletions pages/api/links/[id]/documents/[documentId].ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export default async function handle(
data: true,
},
},
securedByPapermark: true,
enableAgreement: true,
agreement: true,
showBanner: true,
Expand Down
2 changes: 2 additions & 0 deletions pages/api/links/[id]/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export default async function handle(
metaTitle: true,
metaDescription: true,
metaImage: true,
securedByPapermark: true,
metaFavicon: true,
enableQuestion: true,
linkType: true,
Expand Down Expand Up @@ -330,6 +331,7 @@ export default async function handle(
enableAgreement: linkData.enableAgreement,
agreementId: linkData.agreementId || null,
showBanner: linkData.showBanner,
securedByPapermark: linkData.securedByPapermark || false,
enableWatermark: linkData.enableWatermark || false,
watermarkConfig: linkData.watermarkConfig || null,
groupId: linkData.groupId || null,
Expand Down
1 change: 1 addition & 0 deletions pages/api/links/domains/[...domainSlug].ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export default async function handle(
enableScreenshotProtection: true,
enableIndexFile: true,
metaTitle: true,
securedByPapermark: true,
metaDescription: true,
metaImage: true,
metaFavicon: true,
Expand Down
1 change: 1 addition & 0 deletions pages/api/links/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ export default async function handler(
uploadFolderId: linkData.uploadFolderId,
}),
showBanner: linkData.showBanner,
securedByPapermark: linkData.securedByPapermark,
...(linkData.customFields && {
customFields: {
createMany: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- DropIndex
DROP INDEX "Viewer_teamId_createdAt_idx";

-- AlterTable
ALTER TABLE "Link" ADD COLUMN "securedByPapermark" BOOLEAN DEFAULT false;

-- AlterTable
ALTER TABLE "LinkPreset" ADD COLUMN "securedByPapermark" BOOLEAN DEFAULT false;
3 changes: 3 additions & 0 deletions prisma/schema/link.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ model Link {
agreement Agreement? @relation(fields: [agreementId], references: [id], onDelete: SetNull)
agreementId String? // This can be nullable, representing links without agreements
showBanner Boolean? @default(false) // Optional give user a option to show the banner and end of document signup form
securedByPapermark Boolean? @default(false)
enableWatermark Boolean? @default(false) // Optional give user a option to enable the watermark
watermarkConfig Json? // This will store the watermark configuration: {text: "Confidential", isTiled: false, color: "#000000", fontSize: 12, opacity: 0.5, rotation: 30, position: "top-right"}

Expand Down Expand Up @@ -117,6 +118,8 @@ model LinkPreset {
enableAgreement Boolean? @default(false)
agreementId String?

securedByPapermark Boolean? @default(false)

enableCustomFields Boolean? @default(false)
customFields Json? //[{type: "SHORT_TEXT", identifier: "name", label: "Name", required: true}]

Expand Down