Skip to content

Commit 815d767

Browse files
authored
Merge pull request #1419 from mfts/feat/dataroom-location
chore: improvements
2 parents 6b52cee + 0d2eb64 commit 815d767

File tree

10 files changed

+211
-43
lines changed

10 files changed

+211
-43
lines changed

components/links/link-sheet/link-options.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export const LinkOptions = ({
5252
const allowAdvancedLinkControls = limits
5353
? limits?.advancedLinkControlsOnPro
5454
: false;
55+
const allowWatermarkOnBusiness = limits?.watermarkOnBusiness ?? false;
5556

5657
const [openUpgradeModal, setOpenUpgradeModal] = useState<boolean>(false);
5758
const [trigger, setTrigger] = useState<string>("");
@@ -136,7 +137,7 @@ export const LinkOptions = ({
136137
/>
137138
<WatermarkSection
138139
{...{ data, setData }}
139-
isAllowed={isTrial || isDatarooms}
140+
isAllowed={isTrial || isDatarooms || allowWatermarkOnBusiness}
140141
handleUpgradeStateChange={handleUpgradeStateChange}
141142
/>
142143
<AgreementSection
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { useDataroomVisitorUserAgent } from "@/lib/swr/use-dataroom-stats";
2+
3+
import VisitorUserAgentBase from "./visitor-useragent-base";
4+
5+
export function DataroomVisitorUserAgent({ viewId }: { viewId: string }) {
6+
const { userAgent, error } = useDataroomVisitorUserAgent(viewId);
7+
8+
if (error) {
9+
return <div>No useragent info</div>;
10+
}
11+
12+
if (!userAgent) {
13+
return <div>Loading...</div>;
14+
}
15+
16+
return <VisitorUserAgentBase userAgent={userAgent} />;
17+
}

components/visitors/dataroom-visitors-table.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { BadgeTooltip } from "@/components/ui/tooltip";
2626
import { useDataroomVisits } from "@/lib/swr/use-dataroom";
2727
import { durationFormat, timeAgo } from "@/lib/utils";
2828

29+
import { DataroomVisitorUserAgent } from "./dataroom-visitor-useragent";
2930
import DataroomVisitHistory from "./dataroom-visitors-history";
3031
import { VisitorAvatar } from "./visitor-avatar";
3132

@@ -155,6 +156,11 @@ export default function DataroomVisitorsTable({
155156

156157
<CollapsibleContent asChild>
157158
<>
159+
<TableRow>
160+
<TableCell colSpan={3}>
161+
<DataroomVisitorUserAgent viewId={view.id} />
162+
</TableCell>
163+
</TableRow>
158164
<TableRow key={view.id}>
159165
<TableCell>
160166
<div className="flex items-center gap-x-4 overflow-visible">
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { COUNTRIES } from "@/lib/constants";
2+
3+
import UAIcon from "../user-agent-icon";
4+
5+
export default function VisitorUserAgentBase({
6+
userAgent,
7+
}: {
8+
userAgent: {
9+
device: string;
10+
browser: string;
11+
os: string;
12+
country?: string;
13+
city?: string;
14+
};
15+
}) {
16+
const { device, browser, os, country, city } = userAgent;
17+
18+
return (
19+
<div>
20+
<div className="pb-0.5 pl-0.5 md:pb-1 md:pl-1">
21+
{city && country ? (
22+
<div className="flex items-center">
23+
<div className="flex items-center gap-x-1 px-1">
24+
<img
25+
alt={country}
26+
src={`https://flag.vercel.app/m/${country}.svg`}
27+
className="h-3 w-4"
28+
/>
29+
<span>{city},</span>
30+
<span>{COUNTRIES[country]}</span>
31+
</div>
32+
</div>
33+
) : null}
34+
</div>
35+
<div className="pb-0.5 pl-0.5 md:pb-1 md:pl-1">
36+
<div className="flex items-center">
37+
<div className="flex items-center gap-x-1 px-1">
38+
<UAIcon display={device} type="devices" className="size-4" />{" "}
39+
{device},
40+
</div>
41+
<div className="flex items-center gap-x-1 px-1">
42+
<UAIcon display={browser} type="browsers" className="size-4" />{" "}
43+
{browser},
44+
</div>
45+
<div className="flex items-center gap-x-1 px-1">
46+
<UAIcon display={os} type="os" className="size-4" /> {os}
47+
</div>
48+
</div>
49+
</div>
50+
</div>
51+
);
52+
}
Lines changed: 4 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,17 @@
1-
import ErrorPage from "next/error";
2-
3-
import { COUNTRIES } from "@/lib/constants";
41
import { useVisitorUserAgent } from "@/lib/swr/use-stats";
52

6-
import UAIcon from "../user-agent-icon";
3+
import VisitorUserAgentBase from "./visitor-useragent-base";
74

85
export default function VisitorUserAgent({ viewId }: { viewId: string }) {
96
const { userAgent, error } = useVisitorUserAgent(viewId);
107

11-
if (error && error.status === 404) {
12-
return <ErrorPage statusCode={404} />;
8+
if (error) {
9+
return <div>No useragent info</div>;
1310
}
1411

1512
if (!userAgent) {
1613
return <div>Loading...</div>;
1714
}
1815

19-
const { device, browser, os, country, city } = userAgent;
20-
21-
return (
22-
<div>
23-
<div className="pb-0.5 pl-0.5 md:pb-1 md:pl-1">
24-
{city && country ? (
25-
<div className="flex items-center">
26-
<div className="flex items-center gap-x-1 px-1">
27-
<img
28-
alt={country}
29-
src={`https://flag.vercel.app/m/${country}.svg`}
30-
className="h-3 w-4"
31-
/>
32-
<span>{city},</span>
33-
<span>{COUNTRIES[country]}</span>
34-
</div>
35-
</div>
36-
) : null}
37-
</div>
38-
<div className="pb-0.5 pl-0.5 md:pb-1 md:pl-1">
39-
<div className="flex items-center">
40-
<div className="flex items-center gap-x-1 px-1">
41-
<UAIcon display={device} type="devices" className="size-4" />{" "}
42-
{device},
43-
</div>
44-
<div className="flex items-center gap-x-1 px-1">
45-
<UAIcon display={browser} type="browsers" className="size-4" />{" "}
46-
{browser},
47-
</div>
48-
<div className="flex items-center gap-x-1 px-1">
49-
<UAIcon display={os} type="os" className="size-4" /> {os}
50-
</div>
51-
</div>
52-
</div>
53-
</div>
54-
);
16+
return <VisitorUserAgentBase userAgent={userAgent} />;
5517
}

ee/limits/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export type TPlanLimits = {
99
customDomainOnPro: boolean;
1010
customDomainInDataroom: boolean;
1111
advancedLinkControlsOnPro: boolean | null;
12+
watermarkOnBusiness?: boolean | null;
1213
};
1314

1415
export const FREE_PLAN_LIMITS = {

ee/limits/server.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const configSchema = z.object({
3838
customDomainOnPro: z.boolean(),
3939
customDomainInDataroom: z.boolean(),
4040
advancedLinkControlsOnPro: z.boolean().nullish(),
41+
watermarkOnBusiness: z.boolean().nullish(),
4142
});
4243

4344
export async function getLimits({

ee/limits/swr-handler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export type LimitProps = {
1212
customDomainOnPro: boolean;
1313
customDomainInDataroom: boolean;
1414
advancedLinkControlsOnPro: boolean | undefined | null;
15+
watermarkOnBusiness: boolean | undefined | null;
1516
usage: {
1617
documents: number;
1718
links: number;

lib/swr/use-dataroom-stats.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { useRouter } from "next/router";
33
import { useTeam } from "@/context/team-context";
44
import { View } from "@prisma/client";
55
import useSWR from "swr";
6+
import useSWRImmutable from "swr/immutable";
67

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

@@ -79,3 +80,32 @@ export function useDataroomStats({
7980
// error,
8081
// };
8182
// }
83+
84+
export function useDataroomVisitorUserAgent(viewId: string) {
85+
const router = useRouter();
86+
const teamInfo = useTeam();
87+
const teamId = teamInfo?.currentTeam?.id;
88+
89+
const { id: dataroomId } = router.query as {
90+
id: string;
91+
};
92+
93+
const { data: userAgent, error } = useSWRImmutable<{
94+
country: string;
95+
city: string;
96+
os: string;
97+
browser: string;
98+
device: string;
99+
}>(
100+
dataroomId &&
101+
viewId &&
102+
`/api/teams/${teamId}/datarooms/${dataroomId}/views/${viewId}/user-agent`,
103+
fetcher,
104+
);
105+
106+
return {
107+
userAgent,
108+
loading: !error && !userAgent,
109+
error,
110+
};
111+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { NextApiRequest, NextApiResponse } from "next";
2+
3+
import { authOptions } from "@/pages/api/auth/[...nextauth]";
4+
import { getServerSession } from "next-auth/next";
5+
6+
import { errorhandler } from "@/lib/errorHandler";
7+
import prisma from "@/lib/prisma";
8+
import { getViewUserAgent } from "@/lib/tinybird";
9+
import { CustomUser } from "@/lib/types";
10+
11+
export default async function handle(
12+
req: NextApiRequest,
13+
res: NextApiResponse,
14+
) {
15+
if (req.method === "GET") {
16+
// GET /api/teams/:teamId/datarooms/:id/views/:viewId/user-agent
17+
const session = await getServerSession(req, res, authOptions);
18+
if (!session) {
19+
return res.status(401).end("Unauthorized");
20+
}
21+
22+
const {
23+
teamId,
24+
id: dataroomId,
25+
viewId,
26+
} = req.query as {
27+
teamId: string;
28+
id: string;
29+
viewId: string;
30+
};
31+
32+
const userId = (session.user as CustomUser).id;
33+
34+
try {
35+
const team = await prisma.team.findUnique({
36+
where: {
37+
id: teamId,
38+
users: {
39+
some: {
40+
userId: userId,
41+
},
42+
},
43+
},
44+
select: {
45+
id: true,
46+
plan: true,
47+
},
48+
});
49+
50+
if (!team) {
51+
return res.status(401).end("Unauthorized");
52+
}
53+
54+
if (team.plan.includes("free")) {
55+
return res.status(403).end("Forbidden");
56+
}
57+
58+
const view = await prisma.view.findMany({
59+
where: {
60+
dataroomId: dataroomId,
61+
dataroomViewId: viewId,
62+
},
63+
select: {
64+
id: true,
65+
documentId: true,
66+
},
67+
take: 1,
68+
});
69+
70+
if (!view || !view[0].documentId) {
71+
return res.status(404).end("Not Found");
72+
}
73+
74+
const userAgent = await getViewUserAgent({
75+
documentId: view[0].documentId,
76+
viewId: view[0].id,
77+
since: 0,
78+
});
79+
80+
const userAgentData = userAgent.data[0];
81+
// Include country and city for business and datarooms plans
82+
if (team.plan.includes("business") || team.plan.includes("datarooms")) {
83+
return res.status(200).json(userAgentData);
84+
} else {
85+
// For other plans, exclude country and city
86+
const { country, city, ...remainingResponse } = userAgentData;
87+
return res.status(200).json(remainingResponse);
88+
}
89+
} catch (error) {
90+
errorhandler(error, res);
91+
}
92+
} else {
93+
// We only allow GET and POST requests
94+
res.setHeader("Allow", ["GET"]);
95+
return res.status(405).end(`Method ${req.method} Not Allowed`);
96+
}
97+
}

0 commit comments

Comments
 (0)