Skip to content

Commit

Permalink
Merge pull request #1448 from mfts/marc/pm-94-custom-fields-dont-show…
Browse files Browse the repository at this point in the history
…-up-in-dataroom-links-dataroom-view

feat: show custom fields in dataroom analytics
  • Loading branch information
mfts authored Jan 18, 2025
2 parents 3ca8907 + 564fb32 commit 0171da3
Show file tree
Hide file tree
Showing 9 changed files with 312 additions and 84 deletions.
4 changes: 4 additions & 0 deletions components/datarooms/dataroom-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { useState } from "react";
import LinkSheet from "@/components/links/link-sheet";
import { Button } from "@/components/ui/button";

import { useDataroomLinks } from "@/lib/swr/use-dataroom";

export const DataroomHeader = ({
title,
description,
Expand All @@ -13,6 +15,7 @@ export const DataroomHeader = ({
actions?: React.ReactNode[];
}) => {
const [isLinkSheetOpen, setIsLinkSheetOpen] = useState<boolean>(false);
const { links } = useDataroomLinks();

const actionRows: React.ReactNode[][] = [];
if (actions) {
Expand All @@ -38,6 +41,7 @@ export const DataroomHeader = ({
linkType={"DATAROOM_LINK"}
isOpen={isLinkSheetOpen}
setIsOpen={setIsLinkSheetOpen}
existingLinks={links}
/>
</div>
</section>
Expand Down
240 changes: 172 additions & 68 deletions components/sidebar/nav-user.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
"use client";

import { useState } from "react";

import {
BadgeCheck,
Bell,
ChevronsUpDown,
CreditCard,
FileTextIcon,
LifeBuoyIcon,
LogOut,
MailIcon,
Settings2,
Sparkles,
} from "lucide-react";
import { useSession } from "next-auth/react";
import { signOut, useSession } from "next-auth/react";
import { toast } from "sonner";

import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import { Dialog, DialogContent } from "@/components/ui/dialog";
import {
DropdownMenu,
DropdownMenuContent,
Expand All @@ -32,48 +40,56 @@ import {

import { ModeToggle } from "../theme-toggle";

interface Article {
data: {
slug: string;
title: string;
description?: string;
};
}

export function NavUser() {
const { data: session, status } = useSession();
const { isMobile } = useSidebar();

const [searchOpen, setSearchOpen] = useState(false);
const [articles, setArticles] = useState<Article[]>([]);
const [loading, setLoading] = useState(false);

const fetchArticles = async (query?: string) => {
setLoading(true);
try {
const params = new URLSearchParams({
locale: "en", // or get this from your app's locale
...(query && { q: query }),
});

const res = await fetch(`/api/help?${params}`);
const data = await res.json();

if (data.error) {
throw new Error(data.error);
}

setArticles(data.articles || []);
} catch (error) {
console.error("Error fetching articles:", error);
setArticles([]); // Set empty array on error
} finally {
setLoading(false);
}
};

return (
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage
src={session?.user?.image || ""}
alt={session?.user?.name || ""}
/>
<AvatarFallback className="rounded-lg">
{session?.user?.name?.charAt(0) ||
session?.user?.email?.charAt(0)}
</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">
{session?.user?.name || ""}
</span>
<span className="truncate text-xs">
{session?.user?.email || ""}
</span>
</div>
<ChevronsUpDown className="ml-auto size-4" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
side={isMobile ? "bottom" : "right"}
align="end"
sideOffset={4}
>
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<>
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<SidebarMenuButton
size="lg"
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
>
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage
src={session?.user?.image || ""}
Expand All @@ -92,35 +108,123 @@ export function NavUser() {
{session?.user?.email || ""}
</span>
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<ModeToggle />
<DropdownMenuGroup>
<ChevronsUpDown className="ml-auto size-4" />
</SidebarMenuButton>
</DropdownMenuTrigger>
<DropdownMenuContent
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
side={isMobile ? "bottom" : "right"}
align="end"
sideOffset={4}
>
<DropdownMenuLabel className="p-0 font-normal">
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<Avatar className="h-8 w-8 rounded-lg">
<AvatarImage
src={session?.user?.image || ""}
alt={session?.user?.name || ""}
/>
<AvatarFallback className="rounded-lg">
{session?.user?.name?.charAt(0) ||
session?.user?.email?.charAt(0)}
</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-semibold">
{session?.user?.name || ""}
</span>
<span className="truncate text-xs">
{session?.user?.email || ""}
</span>
</div>
</div>
</DropdownMenuLabel>
<DropdownMenuSeparator />
<ModeToggle />
{/* <DropdownMenuGroup>
<DropdownMenuItem>
<Settings2 />
User Settings
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem>
<LifeBuoyIcon />
Help Center
</DropdownMenuItem>
<DropdownMenuItem>
<MailIcon />
Contact Support
</DropdownMenuGroup> */}
<DropdownMenuSeparator />
<DropdownMenuGroup>
<DropdownMenuItem
onClick={() => {
setSearchOpen(true);
fetchArticles();
}}
>
<LifeBuoyIcon />
Help Center
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
navigator.clipboard.writeText("[email protected]");
toast.success("[email protected] copied to clipboard");
}}
>
<MailIcon />
Contact Support
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={() =>
signOut({
callbackUrl: `${window.location.origin}`,
})
}
>
<LogOut />
Log out
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>
<LogOut />
Log out
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>
</DropdownMenuContent>
</DropdownMenu>
</SidebarMenuItem>
</SidebarMenu>

<Dialog open={searchOpen} onOpenChange={setSearchOpen}>
<DialogContent className="max-w-[550px] gap-0 overflow-hidden border-none p-0 shadow-lg">
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
<CommandInput
placeholder="Search help articles..."
className="h-14 border-none px-4 focus:ring-0 focus-visible:ring-0 focus-visible:ring-offset-0"
onValueChange={(search) => fetchArticles(search)}
/>
<CommandList className="max-h-[400px] overflow-y-auto">
<CommandEmpty>No articles found</CommandEmpty>
<CommandGroup heading="All Articles">
{articles.map((article) => (
<CommandItem
key={article.data.slug}
value={article.data.title}
onSelect={() => {
window.open(
`${process.env.NEXT_PUBLIC_MARKETING_URL}/help/article/${article.data.slug}`,
"_blank",
);
setSearchOpen(false);
}}
>
<FileTextIcon className="mr-2 h-4 w-4 text-[#fb7a00]" />
<div className="flex flex-col">
<span className="text-sm font-medium">
{article.data.title}
</span>
{article.data.description && (
<span className="text-xs text-muted-foreground">
{article.data.description}
</span>
)}
</div>
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</DialogContent>
</Dialog>
</>
);
}
43 changes: 43 additions & 0 deletions components/visitors/dataroom-visitor-custom-fields.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Fragment } from "react";

import useSWR from "swr";

import { fetcher } from "@/lib/utils";

type CustomFieldResponse = {
identifier: string;
label: string;
response: string;
};

export default function VisitorCustomFields({
viewId,
teamId,
dataroomId,
}: {
viewId: string;
teamId: string;
dataroomId: string;
}) {
const { data: customFieldResponse } = useSWR<CustomFieldResponse[] | null>(
`/api/teams/${teamId}/datarooms/${dataroomId}/views/${viewId}/custom-fields`,
fetcher,
);

console.log("customFieldResponse", customFieldResponse);

if (!customFieldResponse) return null;

return (
<div className="space-y-2 px-1.5 pb-2 md:px-2">
<dl className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-2">
{customFieldResponse.map((field, index) => (
<Fragment key={index}>
<dt className="text-sm text-muted-foreground">{field.label}</dt>
<dd className="text-sm">{field.response}</dd>
</Fragment>
))}
</dl>
</div>
);
}
8 changes: 7 additions & 1 deletion components/visitors/dataroom-visitors-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ import {
import { BadgeTooltip } from "@/components/ui/tooltip";

import { useDataroomVisits } from "@/lib/swr/use-dataroom";
import { durationFormat, timeAgo } from "@/lib/utils";
import { timeAgo } from "@/lib/utils";

import DataroomVisitorCustomFields from "./dataroom-visitor-custom-fields";
import { DataroomVisitorUserAgent } from "./dataroom-visitor-useragent";
import DataroomVisitHistory from "./dataroom-visitors-history";
import { VisitorAvatar } from "./visitor-avatar";
Expand Down Expand Up @@ -201,6 +202,11 @@ export default function DataroomVisitorsTable({
<>
<TableRow>
<TableCell colSpan={3}>
<DataroomVisitorCustomFields
viewId={view.id}
teamId={view.teamId!}
dataroomId={dataroomId}
/>
<DataroomVisitorUserAgent viewId={view.id} />
</TableCell>
</TableRow>
Expand Down
1 change: 1 addition & 0 deletions pages/api/teams/[teamId]/datarooms/[id]/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export default async function handle(
viewedAt: "desc",
},
},
customFields: true,
_count: {
select: { views: { where: { viewType: "DATAROOM_VIEW" } } },
},
Expand Down
Loading

0 comments on commit 0171da3

Please sign in to comment.