Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
115 changes: 115 additions & 0 deletions app/(ee)/api/faqs/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { NextRequest, NextResponse } from "next/server";

import { verifyDataroomSession } from "@/lib/auth/dataroom-auth";
import prisma from "@/lib/prisma";

export interface VisitorFAQResponse {
id: string;
editedQuestion: string;
answer: string;
documentPageNumber?: number;
documentVersionNumber?: number;
createdAt: string;
document?: {
name: string;
};
}

// GET /api/faqs?linkId=xxx&dataroomId=xxx - List published FAQs for visitors
export async function GET(req: NextRequest) {
try {
const searchParams = req.nextUrl.searchParams;
const linkId = searchParams.get("linkId");
const dataroomId = searchParams.get("dataroomId");
const documentId = searchParams.get("documentId");

if (!linkId || !dataroomId) {
return NextResponse.json(
{ error: "linkId and dataroomId are required" },
{ status: 400 },
);
}

// Verify dataroom session
const session = await verifyDataroomSession(req, linkId, dataroomId);
if (!session) {
return NextResponse.json(
{ error: "Unauthorized - invalid or expired session" },
{ status: 401 },
);
}

// Build where clause based on visibility filters
const whereClause: any = {
dataroomId,
status: "PUBLISHED",
};

// Apply visibility filters
const visibilityFilters: any[] = [
{ visibilityMode: "PUBLIC_DATAROOM" }, // Always include dataroom-wide FAQs
];

if (linkId) {
visibilityFilters.push({
visibilityMode: "PUBLIC_LINK",
linkId: linkId,
});
}

if (documentId) {
visibilityFilters.push({
visibilityMode: "PUBLIC_DOCUMENT",
dataroomDocumentId: documentId,
});
}

whereClause.OR = visibilityFilters;

// Fetch published FAQs
const faqs = await prisma.dataroomFaqItem.findMany({
where: whereClause,
select: {
id: true,
editedQuestion: true,
answer: true,
documentPageNumber: true,
documentVersionNumber: true,
createdAt: true,
dataroomDocument: {
select: {
document: {
select: {
name: true,
},
},
},
},
},
orderBy: { createdAt: "desc" },
});

// Format response
const response: VisitorFAQResponse[] = faqs.map((faq: any) => ({
id: faq.id,
editedQuestion: faq.editedQuestion,
answer: faq.answer,
documentPageNumber: faq.documentPageNumber || undefined,
documentVersionNumber: faq.documentVersionNumber || undefined,
createdAt: faq.createdAt.toISOString(),
document: faq.dataroomDocument?.document
? {
name: faq.dataroomDocument.document.name,
}
: undefined,
}));

return NextResponse.json(response);
} catch (error) {
console.error("Error fetching visitor FAQs:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 },
);
}
}
2 changes: 1 addition & 1 deletion components/datarooms/dataroom-navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const DataroomNavigation = ({ dataroomId }: { dataroomId?: string }) => {
segment: "analytics",
},
{
label: "Q&A Conversations",
label: "Q&A",
href: `/datarooms/${dataroomId}/conversations`,
segment: "conversations",
limited: !limits?.conversationsInDataroom,
Expand Down
3 changes: 3 additions & 0 deletions components/layouts/breadcrumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,10 @@ const SingleDataroomBreadcrumb = ({ path }: { path: string }) => {
return "Permissions";
case "/datarooms/[id]/analytics":
return "Analytics";
case "/datarooms/[id]/conversations/faqs":
return "FAQ";
case "/datarooms/[id]/conversations":
case "/datarooms/[id]/conversations/[conversationId]":
return "Conversations";
case "/datarooms/[id]/settings/notifications":
return "Notifications";
Expand Down
2 changes: 2 additions & 0 deletions components/ui/badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ const badgeVariants = cva(
"border-transparent bg-orange-50 text-orange-700 hover:bg-orange-100 dark:bg-orange-900/30 dark:text-orange-400 dark:hover:bg-orange-900/50",
watermark:
"border-transparent bg-indigo-50 text-indigo-700 hover:bg-indigo-100 dark:bg-indigo-900/30 dark:text-indigo-400 dark:hover:bg-indigo-900/50",
notification:
"px-1.5 rounded-sm bg-secondary text-secondary-foreground border-transparent",
},
},
defaultVariants: {
Expand Down
21 changes: 4 additions & 17 deletions components/view/dataroom/nav-dataroom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Link from "next/link";
import React, { useEffect, useState } from "react";

import { DataroomBrand } from "@prisma/client";
import { BadgeInfoIcon, Download, MessagesSquareIcon } from "lucide-react";
import { BadgeInfoIcon, Download } from "lucide-react";
import { toast } from "sonner";

import { formatDate } from "@/lib/utils";
Expand Down Expand Up @@ -172,22 +172,9 @@ export default function DataroomNav({
</TooltipProvider>
) : null}
{conversationsEnabled && (
<TooltipProvider delayDuration={100}>
<Tooltip>
<TooltipTrigger asChild>
<Button
onClick={() => setShowConversations(!showConversations)}
className="size-8 bg-gray-900 text-white hover:bg-gray-900/80 sm:size-10"
size="icon"
>
<MessagesSquareIcon className="h-5 w-5" />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">
<p>Toggle conversations</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Button onClick={() => setShowConversations(!showConversations)}>
View FAQ
</Button>
)}
{allowDownload && allowBulkDownload ? (
<ButtonTooltip content="Download Dataroom">
Expand Down
22 changes: 6 additions & 16 deletions components/view/nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -291,22 +291,12 @@ export default function Nav({
)}
{/* Conversation toggle button for dataroom documents */}
{isDataroom && conversationsEnabled && (
<TooltipProvider delayDuration={100}>
<Tooltip>
<TooltipTrigger asChild>
<Button
onClick={() => setShowConversations(!showConversations)}
className="size-8 bg-gray-900 text-white hover:bg-gray-900/80 sm:size-10"
size="icon"
>
<MessageCircle className="h-5 w-5" />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">
<p>Toggle conversations</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
<Button
onClick={() => setShowConversations(!showConversations)}
className="bg-gray-900 text-white hover:bg-gray-900/80"
>
View FAQ
</Button>
)}
{embeddedLinks && embeddedLinks.length > 0 ? (
<DropdownMenu>
Expand Down
Loading