2022-10-19 09:45:44 +00:00
import * as Popover from "@radix-ui/react-popover" ;
import {
formatTime ,
isNextDayInTimezone ,
isPreviousDayInTimezone ,
sortByTimezone ,
2023-07-18 13:49:23 +00:00
isSupportedTimeZone ,
2022-10-19 09:45:44 +00:00
} from "@calcom/lib/date-fns" ;
2023-04-12 15:26:31 +00:00
import { Globe } from "../icon" ;
2023-01-26 22:51:03 +00:00
type Attendee = {
id : number ;
email : string ;
name : string ;
timeZone : string ;
locale : string | null ;
bookingId : number | null ;
} ;
2022-10-19 09:45:44 +00:00
interface MeetingTimeInTimezonesProps {
attendees : Attendee [ ] ;
userTimezone? : string ;
timeFormat? : number | null ;
startTime : string ;
endTime : string ;
}
const MeetingTimeInTimezones = ( {
attendees ,
userTimezone ,
timeFormat ,
startTime ,
endTime ,
} : MeetingTimeInTimezonesProps ) = > {
if ( ! userTimezone || ! attendees . length ) return null ;
2023-07-18 15:58:32 +00:00
2023-07-18 13:49:23 +00:00
// If attendeeTimezone is unsupported, we fallback to host timezone. Unsupported Attendee timezone can be used due to bad API booking request in the past | backward-compatibility
const attendeeTimezones = attendees . map ( ( attendee ) = > {
return isSupportedTimeZone ( attendee . timeZone ) ? attendee.timeZone : userTimezone ;
} ) ;
2022-10-19 09:45:44 +00:00
const uniqueTimezones = [ userTimezone , . . . attendeeTimezones ] . filter (
( value , index , self ) = > self . indexOf ( value ) === index
) ;
// Convert times to time in timezone, and then sort from earliest to latest time in timezone.
const times = uniqueTimezones
. map ( ( timezone ) = > {
const isPreviousDay = isPreviousDayInTimezone ( startTime , userTimezone , timezone ) ;
const isNextDay = isNextDayInTimezone ( startTime , userTimezone , timezone ) ;
return {
startTime : formatTime ( startTime , timeFormat , timezone ) ,
endTime : formatTime ( endTime , timeFormat , timezone ) ,
timezone ,
isPreviousDay ,
isNextDay ,
} ;
} )
. sort ( ( timeA , timeB ) = > sortByTimezone ( timeA . timezone , timeB . timezone ) ) ;
// We don't show the popover if there's only one timezone.
if ( times . length === 1 ) return null ;
return (
< Popover.Root >
< Popover.Trigger
onClick = { preventBubbling }
2023-04-05 18:14:46 +00:00
className = "popover-button text-emphasis hover:bg-emphasis focus:bg-emphasis invisible ml-2 inline-flex h-5 w-5 items-center justify-center rounded-sm transition-colors group-hover:visible" >
2023-04-12 15:26:31 +00:00
< Globe className = "h-3.5 w-3.5" / >
2022-10-19 09:45:44 +00:00
< / Popover.Trigger >
< Popover.Portal >
< Popover.Content
onClick = { preventBubbling }
side = "top"
2023-04-05 18:14:46 +00:00
className = "popover-content slideInBottom border-5 bg-brand-default text-inverted border-subtle rounded-md p-3 text-sm shadow-sm" >
2022-10-19 09:45:44 +00:00
{ times . map ( ( time ) = > (
< span className = "mt-2 block first:mt-0" key = { time . timezone } >
< span className = "inline-flex align-baseline" >
{ time . startTime } - { time . endTime }
{ ( time . isNextDay || time . isPreviousDay ) && (
< span className = "text-medium ml-2 inline-flex h-5 w-5 items-center justify-center rounded-full bg-gray-700 text-[10px]" >
{ time . isNextDay ? "+1" : "-1" }
< / span >
) }
< / span >
< br / >
2023-04-05 18:14:46 +00:00
< span className = "text-muted" > { time . timezone } < / span >
2022-10-19 09:45:44 +00:00
< / span >
) ) }
< / Popover.Content >
< / Popover.Portal >
< / Popover.Root >
) ;
} ;
MeetingTimeInTimezones . displayName = "MeetingTimeInTimezones" ;
// Prevents propagation so the click on eg booking overview won't
// bubble to the row of the table, causing a navigation to the
// detaill page.
const preventBubbling = ( event : React.MouseEvent ) = > {
event . stopPropagation ( ) ;
} ;
export default MeetingTimeInTimezones ;