@@ -8,6 +8,7 @@ import { useLimits } from "@/ee/limits/swr-handler";
8
8
import { Document , DocumentVersion } from "@prisma/client" ;
9
9
import {
10
10
BetweenHorizontalStartIcon ,
11
+ FileDownIcon ,
11
12
PlusIcon ,
12
13
SheetIcon ,
13
14
Sparkles ,
@@ -38,6 +39,7 @@ import { DocumentWithLinksAndLinkCountAndViewCount } from "@/lib/types";
38
39
import { cn , getExtension } from "@/lib/utils" ;
39
40
import { fileIcon } from "@/lib/utils/get-file-icon" ;
40
41
42
+ import PlanBadge from "../billing/plan-badge" ;
41
43
import { UpgradePlanModal } from "../billing/upgrade-plan-modal" ;
42
44
import { AddDataroomModal } from "../datarooms/add-dataroom-modal" ;
43
45
import { DataroomTrialModal } from "../datarooms/dataroom-trial-modal" ;
@@ -84,6 +86,7 @@ export default function DocumentHeader({
84
86
const numDatarooms = dataRooms ?. length ?? 0 ;
85
87
const limitDatarooms = limits ?. datarooms ?? 1 ;
86
88
89
+ const isFree = plan === "free" ;
87
90
const isBusiness = plan === "business" ;
88
91
const isDatarooms = plan === "datarooms" ;
89
92
const isTrialDatarooms = trial === "drtrial" ;
@@ -97,6 +100,19 @@ export default function DocumentHeader({
97
100
}
98
101
}
99
102
103
+ const currentTime = new Date ( ) ;
104
+ const formattedTime =
105
+ currentTime . getFullYear ( ) +
106
+ "-" +
107
+ String ( currentTime . getMonth ( ) + 1 ) . padStart ( 2 , "0" ) +
108
+ "-" +
109
+ String ( currentTime . getDate ( ) ) . padStart ( 2 , "0" ) +
110
+ "_" +
111
+ String ( currentTime . getHours ( ) ) . padStart ( 2 , "0" ) +
112
+ "-" +
113
+ String ( currentTime . getMinutes ( ) ) . padStart ( 2 , "0" ) ;
114
+ "-" + String ( currentTime . getSeconds ( ) ) . padStart ( 2 , "0" ) ;
115
+
100
116
const plausible = usePlausible ( ) ;
101
117
102
118
// https://github.com/radix-ui/primitives/issues/1241#issuecomment-1888232392
@@ -271,6 +287,76 @@ export default function DocumentHeader({
271
287
}
272
288
} ;
273
289
290
+ // export method to fetch the visits data and convert to csv.
291
+ const exportVisitCounts = async ( document : Document ) => {
292
+ if ( isFree ) {
293
+ toast . error ( "This feature is not available for your plan" ) ;
294
+ return ;
295
+ }
296
+ try {
297
+ const response = await fetch (
298
+ `/api/teams/${ teamId } /documents/${ document . id } /export-visits` ,
299
+ { method : "GET" } ,
300
+ ) ;
301
+ if ( ! response . ok ) {
302
+ throw new Error ( `HTTP error! status: ${ response . status } ` ) ;
303
+ }
304
+ const data = await response . json ( ) ;
305
+
306
+ // Converting the json Array into CSV without using parser.
307
+ const csvString = [
308
+ [
309
+ "Viewed at" ,
310
+ "Name" ,
311
+ "Email" ,
312
+ "Link Name" ,
313
+ "Total Visit Duration (s)" ,
314
+ "Total Document Completion (%)" ,
315
+ "Document version" ,
316
+ "Downloaded at" ,
317
+ "Verified" ,
318
+ "Agreement accepted" ,
319
+ "Viewed from dataroom" ,
320
+ ] ,
321
+ ...data . visits . map ( ( item : any ) => [
322
+ item . viewedAt ,
323
+ item . viewerName ,
324
+ item . viewerEmail ,
325
+ item . linkName ,
326
+ item . totalVisitDuration / 1000.0 ,
327
+ item . visitCompletion ,
328
+ item . documentVersion ,
329
+ item . downloadedAt ,
330
+ item . verified ,
331
+ item . agreement ,
332
+ item . dataroom ,
333
+ ] ) ,
334
+ ]
335
+ . map ( ( row ) => row . join ( "," ) )
336
+ . join ( "\n" ) ;
337
+
338
+ // Creating csv as per the time stamp.
339
+ const blob = new Blob ( [ csvString ] , { type : "text/csv;charset=utf-8;" } ) ;
340
+ const url = URL . createObjectURL ( blob ) ;
341
+ const link = window . document . createElement ( "a" ) ;
342
+ link . href = url ;
343
+ link . setAttribute (
344
+ "download" ,
345
+ `${ data . documentName } _visits_${ formattedTime } .csv` ,
346
+ ) ;
347
+ window . document . body . appendChild ( link ) ;
348
+ link . click ( ) ;
349
+ window . document . body . removeChild ( link ) ;
350
+ URL . revokeObjectURL ( url ) ;
351
+ toast . success ( "CSV file downloaded successfully" ) ;
352
+ } catch ( error ) {
353
+ console . error ( "Error:" , error ) ;
354
+ toast . error (
355
+ "An error occurred while downloading the CSV. Please try again." ,
356
+ ) ;
357
+ }
358
+ } ;
359
+
274
360
useEffect ( ( ) => {
275
361
function handleClickOutside ( event : { target : any } ) {
276
362
if ( dropdownRef . current && ! dropdownRef . current . contains ( event . target ) ) {
@@ -574,14 +660,25 @@ export default function DocumentHeader({
574
660
) }
575
661
{ renderDropdownMenuItem ( ) }
576
662
< DropdownMenuSeparator />
663
+
664
+ { /* Export views in CSV */ }
665
+ < DropdownMenuItem
666
+ onClick = { ( ) => exportVisitCounts ( prismaDocument ) }
667
+ disabled = { isFree }
668
+ >
669
+ < FileDownIcon className = "mr-2 h-4 w-4" />
670
+ Export visits { isFree ? < PlanBadge plan = "pro" /> : "" }
671
+ </ DropdownMenuItem >
672
+
673
+ < DropdownMenuSeparator />
674
+
577
675
< DropdownMenuItem
578
676
className = "text-destructive focus:bg-destructive focus:text-destructive-foreground"
579
677
onClick = { ( event ) => handleButtonClick ( event , prismaDocument . id ) }
580
678
>
581
679
< TrashIcon className = "mr-2 h-4 w-4" />
582
680
{ isFirstClick ? "Really delete?" : "Delete document" }
583
681
</ DropdownMenuItem >
584
- { /* create a dropdownmenuitem that onclick calls a post request to /api/assistants with the documentId */ }
585
682
</ DropdownMenuContent >
586
683
</ DropdownMenu >
587
684
</ div >
0 commit comments