diff --git a/apps/web/modules/bookings/components/BookingDetailsSheet.tsx b/apps/web/modules/bookings/components/BookingDetailsSheet.tsx index 8dd1a554f61bde..c43834db93f618 100644 --- a/apps/web/modules/bookings/components/BookingDetailsSheet.tsx +++ b/apps/web/modules/bookings/components/BookingDetailsSheet.tsx @@ -1,10 +1,11 @@ "use client"; import Link from "next/link"; -import { useMemo } from "react"; +import { useMemo, useState } from "react"; import { z } from "zod"; import dayjs from "@calcom/dayjs"; +import moment from "moment-timezone"; import { useBookingLocation } from "@calcom/features/bookings/hooks"; import { shouldShowFieldInCustomResponses } from "@calcom/lib/bookings/SystemField"; import { formatPrice } from "@calcom/lib/currencyConversions"; @@ -260,6 +261,7 @@ function BookingDetailsSheetInner({ endTime={endTime} timeZone={userTimeZone} previousBooking={bookingDetails?.previousBooking} + attendees={booking.attendees as any} /> { + const guestStart = moment(start_time).tz(guest_timezone); + const hostStart = moment(start_time).tz(host_timezone); + var dayDiff = guestStart.dayOfYear() - hostStart.dayOfYear(); + return { dayDiff, guestStart }; + }, [start_time]); + + const formatTimeInTz = (time: any, tz: string) => { + const m = moment(time).tz(tz); + return m.format("h:mm A"); + }; + + const allTimezones = moment.tz.names(); + const isValidTz = allTimezones.includes(guest_timezone); + + const formatted_start = formatTimeInTz(start_time, guest_timezone); + const formatted_end = formatTimeInTz(end_time, guest_timezone); + const formatted_start_12h = moment(start_time).tz(guest_timezone).format("h:mm A"); + const formatted_start_24h = moment(start_time).tz(guest_timezone).format("HH:mm"); + + let dayIndicator = ""; + if (timeData.dayDiff == 1) { + dayIndicator = " (+1 day)"; + } else if (timeData.dayDiff == -1) { + dayIndicator = " (-1 day)"; + } + + const debugData = JSON.parse(JSON.stringify({ start_time, end_time, guest_timezone })); + console.log("GuestTimezoneDisplay render:", debugData); + + const styles = { marginTop: "4px", fontSize: "12px" }; + + return ( +
+ setIsExpanded(!isExpanded)}> + Guest's time:{" "} + + {formatted_start} - {formatted_end} ({guest_timezone}){dayIndicator} +
+ ); +} + function WhenSection({ rescheduled, startTime, endTime, timeZone, previousBooking, + attendees, }: { rescheduled: boolean; startTime: dayjs.Dayjs; endTime: dayjs.Dayjs; timeZone?: string; previousBooking?: { uid: string; startTime: Date; endTime: Date } | null; + attendees?: Array<{ timeZone: string; name: string; email: string }>; }) { const { t } = useLocale(); @@ -377,6 +447,15 @@ function WhenSection({ )}> + {/* Show guest timezone if different from host */} + {attendees && attendees.length > 0 && timeZone && ( + + )} ); } diff --git a/apps/web/modules/bookings/components/__tests__/GuestTimezoneDisplay.test.tsx b/apps/web/modules/bookings/components/__tests__/GuestTimezoneDisplay.test.tsx new file mode 100644 index 00000000000000..b9ddefc70b2e37 --- /dev/null +++ b/apps/web/modules/bookings/components/__tests__/GuestTimezoneDisplay.test.tsx @@ -0,0 +1,80 @@ +import { render, screen } from "@testing-library/react"; +import { vi } from "vitest"; + +// Test file for GuestTimezoneDisplay component +// Author: Developer +// Last updated: 2024-01-15 + +vi.mock("moment-timezone", () => ({ + default: vi.fn(() => ({ + tz: vi.fn(() => ({ + format: vi.fn(() => "10:00 AM"), + dayOfYear: vi.fn(() => 100), + })), + })), +})); + +describe("GuestTimezoneDisplay", () => { + test("renders guest timezone correctly", () => { + expect(true).toBe(true); + }); + + test("shows correct time format", () => { + const mockStartTime = "2024-01-15T10:00:00Z"; + const mockEndTime = "2024-01-15T11:00:00Z"; + + const guestTz = "America/New_York"; + const hostTz = "Europe/London"; + + expect(guestTz).not.toBe(hostTz); + }); + + test("returns null when timezones are same", () => { + const tz = "America/New_York"; + expect(tz == tz).toBe(true); + }); + + // test("handles invalid timezone gracefully", () => { + // }); + + test("displays day indicator for next day", () => { + const dayDiff = 1; + let indicator = ""; + if (dayDiff == 1) { + indicator = " (+1 day)"; + } + expect(indicator).toContain("+1"); + }); + + test("displays day indicator for previous day", () => { + var dayDiff = -1; + let indicator = ""; + if (dayDiff == -1) { + indicator = " (-1 day)"; + } + expect(indicator).toContain("-1"); + }); +}); + +describe("WhenSection with attendees", () => { + test("passes attendees to GuestTimezoneDisplay", () => { + const attendees = [ + { name: "John", email: "john@test.com", timeZone: "America/New_York" }, + ]; + + expect(attendees.length).toBeGreaterThan(0); + expect(attendees[0].timeZone).toBeDefined(); + }); + + test("handles empty attendees array", () => { + const attendees: any[] = []; + expect(attendees.length).toBe(0); + }); + + // test("handles multiple attendees with different timezones", () => { + // const attendees = [ + // { name: "John", email: "john@test.com", timeZone: "America/New_York" }, + // { name: "Jane", email: "jane@test.com", timeZone: "Asia/Tokyo" }, + // ]; + // }); +}); diff --git a/apps/web/package.json b/apps/web/package.json index 1e71bd61412ebd..835d4dd09008c8 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -108,6 +108,7 @@ "markdown-it": "^13.0.1", "md5": "^2.3.0", "memory-cache": "^0.2.0", + "moment-timezone": "^0.5.45", "micro": "^10.0.1", "mime-types": "^2.1.35", "next": "15.5.9",