11"use client" ;
22
33import dayjs from "@calcom/dayjs" ;
4+ import { downloadAsCsv } from "@calcom/lib/csvUtils" ;
45import { useLocale } from "@calcom/lib/hooks/useLocale" ;
56import type { RouterOutputs } from "@calcom/trpc/react" ;
67import { trpc } from "@calcom/trpc/react" ;
78import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery" ;
8-
9- import { CsvDownloadButton } from "@lib/components/CsvDownloadButton" ;
9+ import { Button } from "@calcom/ui/components/button" ;
10+ import { hideProgressToast , showProgressToast , showToast } from "@calcom/ui/components/toast" ;
11+ import { useState } from "react" ;
1012import { useBookingFilters } from "~/bookings/hooks/useBookingFilters" ;
1113import type { BookingListingStatus } from "../types" ;
1214
@@ -36,6 +38,7 @@ function transformBookingToCsv(booking: BookingOutput, t: TranslationFunction) {
3638export function BookingsCsvDownload ( { status } : BookingsCsvDownloadProps ) {
3739 const { t } = useLocale ( ) ;
3840 const { data : user , isPending : isUserPending } = useMeQuery ( ) ;
41+ const [ isDownloading , setIsDownloading ] = useState ( false ) ;
3942 const utils = trpc . useUtils ( ) ;
4043
4144 const { eventTypeIds, teamIds, userIds, dateRange, attendeeName, attendeeEmail, bookingUid } =
@@ -48,30 +51,75 @@ export function BookingsCsvDownload({ status }: BookingsCsvDownloadProps) {
4851 return null ;
4952 }
5053
54+ const fetchBatch = async ( offset : number ) => {
55+ const result = await utils . viewer . bookings . get . fetch ( {
56+ limit : BATCH_SIZE ,
57+ offset,
58+ filters : {
59+ statuses : [ status ] ,
60+ eventTypeIds,
61+ teamIds,
62+ userIds,
63+ attendeeName,
64+ attendeeEmail,
65+ bookingUid,
66+ afterStartDate : dateRange ?. startDate
67+ ? dayjs ( dateRange ?. startDate ) . startOf ( "day" ) . toISOString ( )
68+ : undefined ,
69+ beforeEndDate : dateRange ?. endDate ? dayjs ( dateRange ?. endDate ) . endOf ( "day" ) . toISOString ( ) : undefined ,
70+ } ,
71+ } ) ;
72+
73+ return {
74+ bookings : result . bookings ,
75+ totalCount : result . totalCount ,
76+ } ;
77+ } ;
78+
79+ const handleDownload = async ( ) => {
80+ try {
81+ setIsDownloading ( true ) ;
82+ showProgressToast ( 0 ) ;
83+
84+ // Fetch first batch to get total count
85+ const firstBatch = await fetchBatch ( 0 ) ;
86+ let allBookings = firstBatch . bookings ;
87+ const totalCount = firstBatch . totalCount ;
88+
89+ // Continue fetching remaining batches
90+ while ( allBookings . length < totalCount ) {
91+ const offset = allBookings . length ;
92+ const batch = await fetchBatch ( offset ) ;
93+ if ( batch . bookings . length === 0 ) break ; // Prevent infinite loop if batch returns empty
94+ allBookings = [ ...allBookings , ...batch . bookings ] ;
95+
96+ const currentProgress = Math . min ( Math . round ( ( allBookings . length / totalCount ) * 100 ) , 99 ) ;
97+ showProgressToast ( currentProgress ) ;
98+ }
99+
100+ showProgressToast ( 100 ) ;
101+
102+ // Transform and download
103+ const csvData = allBookings . map ( ( booking ) => transformBookingToCsv ( booking , t ) ) ;
104+ const filename = `${ t ( "bookings" ) . toLowerCase ( ) } -${ status } -${ dayjs ( ) . format ( "YYYY-MM-DD" ) } .csv` ;
105+ downloadAsCsv ( csvData , filename ) ;
106+ } catch {
107+ showToast ( t ( "unexpected_error_try_again" ) , "error" ) ;
108+ } finally {
109+ setIsDownloading ( false ) ;
110+ hideProgressToast ( ) ;
111+ }
112+ } ;
113+
51114 return (
52- < CsvDownloadButton
53- fetchBatch = { async ( offset ) => {
54- const result = await utils . viewer . bookings . get . fetch ( {
55- limit : BATCH_SIZE ,
56- offset,
57- filters : {
58- statuses : [ status ] ,
59- eventTypeIds,
60- teamIds,
61- userIds,
62- attendeeName,
63- attendeeEmail,
64- bookingUid,
65- afterStartDate : dateRange ?. startDate
66- ? dayjs ( dateRange ?. startDate ) . startOf ( "day" ) . toISOString ( )
67- : undefined ,
68- beforeEndDate : dateRange ?. endDate ? dayjs ( dateRange ?. endDate ) . endOf ( "day" ) . toISOString ( ) : undefined ,
69- } ,
70- } ) ;
71- return { data : result . bookings , total : result . totalCount } ;
72- } }
73- transformData = { ( bookings ) => bookings . map ( ( booking ) => transformBookingToCsv ( booking , t ) ) }
74- filename = { `${ t ( "bookings" ) . toLowerCase ( ) } -${ status } -${ dayjs ( ) . format ( "YYYY-MM-DD" ) } .csv` }
75- />
115+ < Button
116+ color = "secondary"
117+ StartIcon = "download"
118+ loading = { isDownloading }
119+ onClick = { handleDownload }
120+ size = "sm"
121+ className = "h-full" >
122+ { t ( "download" ) }
123+ </ Button >
76124 ) ;
77125}
0 commit comments