diff --git a/frontend/src/components/AvailabilityHeatmapV2.tsx b/frontend/src/components/AvailabilityHeatmapV2.tsx index 9638307..f4c002d 100644 --- a/frontend/src/components/AvailabilityHeatmapV2.tsx +++ b/frontend/src/components/AvailabilityHeatmapV2.tsx @@ -156,6 +156,23 @@ export const AvailabilityHeatmapV2 = ({ }); }; + // Move hooks to top level to avoid conditional hook execution error + const tzOffsetDiff = useMemo(() => { + try { + const now = new Date(); + const p = parseInt(new Intl.DateTimeFormat('en-US', { timeZone: displayTimezone, hour: 'numeric', hour12: false }).format(now)); + const s = parseInt(new Intl.DateTimeFormat('en-US', { timeZone: secondaryTimezone, hour: 'numeric', hour12: false }).format(now)); + let diff = s - p; + if (diff > 12) diff -= 24; + if (diff < -12) diff += 24; + return diff; + } catch { + return 0; + } + }, [displayTimezone, secondaryTimezone]); + + const timeColWidth = showSecondaryTimezone ? "120px" : "80px"; + if (selectedParticipants.length === 0) { return (
@@ -245,9 +262,15 @@ export const AvailabilityHeatmapV2 = ({
{/* Grid Header */} -
-
- TIME +
+
+ {formatTimezoneDisplay(displayTimezone)} + {showSecondaryTimezone && ( + {formatTimezoneDisplay(secondaryTimezone)} + )}
{weekDates.map(date => { const isToday = new Date().toDateString() === date.toDateString(); @@ -269,27 +292,41 @@ export const AvailabilityHeatmapV2 = ({
{activeHours.map((hour) => { const isNight = hour < 8 || hour >= 18; + + // Calculate secondary time + let secondaryHour = hour + tzOffsetDiff; + if (secondaryHour >= 24) secondaryHour -= 24; + if (secondaryHour < 0) secondaryHour += 24; + return (
{/* Time Label - Sticky Left */}
- {isNight ? ( - - ) : ( - +
+ {isNight ? ( + + ) : ( + + )} + {formatHour(hour)} +
+ {showSecondaryTimezone && ( + + {formatHour(secondaryHour)} + )} - {formatHour(hour)}
{/* Days */} diff --git a/frontend/src/components/ParticipantSelector.tsx b/frontend/src/components/ParticipantSelector.tsx index a3538e4..ad2e4b4 100644 --- a/frontend/src/components/ParticipantSelector.tsx +++ b/frontend/src/components/ParticipantSelector.tsx @@ -145,6 +145,20 @@ export const ParticipantSelector = ({ className="flex-1 min-w-[100px] h-7 border-none shadow-none focus-visible:ring-0 p-0 text-sm bg-transparent placeholder:text-muted-foreground/70" /> + {isDropdownOpen && ( + + )} + {isDropdownOpen && filteredParticipants.length > 0 && (
{filteredParticipants.map((participant, index) => ( diff --git a/frontend/src/components/TimezoneSelector.tsx b/frontend/src/components/TimezoneSelector.tsx index f6430cf..692405a 100644 --- a/frontend/src/components/TimezoneSelector.tsx +++ b/frontend/src/components/TimezoneSelector.tsx @@ -11,11 +11,12 @@ interface TimezoneSelectorProps { // Get all IANA timezones const getAllTimezones = (): string[] => { + let timezones: string[] = []; try { - return Intl.supportedValuesOf('timeZone'); + timezones = Intl.supportedValuesOf('timeZone'); } catch { // Fallback for older browsers - return [ + timezones = [ 'UTC', 'America/New_York', 'America/Chicago', @@ -23,6 +24,7 @@ const getAllTimezones = (): string[] => { 'America/Los_Angeles', 'America/Toronto', 'America/Vancouver', + 'America/Montreal', 'Europe/London', 'Europe/Paris', 'Europe/Berlin', @@ -33,6 +35,17 @@ const getAllTimezones = (): string[] => { 'Pacific/Auckland', ]; } + + // Prioritize Montreal as requested + const priorityTimezone = 'America/Montreal'; + if (!timezones.includes(priorityTimezone)) { + timezones.push(priorityTimezone); + } + + return [ + priorityTimezone, + ...timezones.filter((tz) => tz !== priorityTimezone), + ]; }; // Get UTC offset for a timezone @@ -158,7 +171,7 @@ export const TimezoneSelector = ({ No timezones found
) : ( - filteredTimezones.slice(0, 50).map((timezone) => { + filteredTimezones.map((timezone) => { const isSelected = timezone === value; const offset = getTimezoneOffset(timezone); const label = formatTimezoneLabel(timezone); @@ -204,12 +217,6 @@ export const TimezoneSelector = ({ }) )}
- - {filteredTimezones.length > 50 && ( -
- Showing 50 of {filteredTimezones.length} results -
- )}
)}
diff --git a/frontend/src/components/ui/sheet.tsx b/frontend/src/components/ui/sheet.tsx index c414714..04a3a97 100644 --- a/frontend/src/components/ui/sheet.tsx +++ b/frontend/src/components/ui/sheet.tsx @@ -1,17 +1,17 @@ -import * as SheetPrimitive from "@radix-ui/react-dialog"; -import { cva, type VariantProps } from "class-variance-authority"; -import { X } from "lucide-react"; -import * as React from "react"; +import * as React from "react" +import * as SheetPrimitive from "@radix-ui/react-dialog" +import { cva, type VariantProps } from "class-variance-authority" +import { X } from "lucide-react" -import { cn } from "@/lib/utils"; +import { cn } from "@/lib/utils" -const Sheet = SheetPrimitive.Root; +const Sheet = SheetPrimitive.Root -const SheetTrigger = SheetPrimitive.Trigger; +const SheetTrigger = SheetPrimitive.Trigger -const SheetClose = SheetPrimitive.Close; +const SheetClose = SheetPrimitive.Close -const SheetPortal = SheetPrimitive.Portal; +const SheetPortal = SheetPrimitive.Portal const SheetOverlay = React.forwardRef< React.ElementRef, @@ -19,14 +19,14 @@ const SheetOverlay = React.forwardRef< >(({ className, ...props }, ref) => ( -)); -SheetOverlay.displayName = SheetPrimitive.Overlay.displayName; +)) +SheetOverlay.displayName = SheetPrimitive.Overlay.displayName const sheetVariants = cva( "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500", @@ -38,70 +38,101 @@ const sheetVariants = cva( "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", right: - "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", + "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", }, }, defaultVariants: { side: "right", }, - }, -); + } +) interface SheetContentProps extends React.ComponentPropsWithoutRef, - VariantProps {} + VariantProps { } -const SheetContent = React.forwardRef, SheetContentProps>( - ({ side = "right", className, children, ...props }, ref) => ( - - - - {children} - - - Close - - - - ), -); -SheetContent.displayName = SheetPrimitive.Content.displayName; +const SheetContent = React.forwardRef< + React.ElementRef, + SheetContentProps +>(({ side = "right", className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)) +SheetContent.displayName = SheetPrimitive.Content.displayName -const SheetHeader = ({ className, ...props }: React.HTMLAttributes) => ( -
-); -SheetHeader.displayName = "SheetHeader"; +const SheetHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +SheetHeader.displayName = "SheetHeader" -const SheetFooter = ({ className, ...props }: React.HTMLAttributes) => ( -
-); -SheetFooter.displayName = "SheetFooter"; +const SheetFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +SheetFooter.displayName = "SheetFooter" const SheetTitle = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)); -SheetTitle.displayName = SheetPrimitive.Title.displayName; + +)) +SheetTitle.displayName = SheetPrimitive.Title.displayName const SheetDescription = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - -)); -SheetDescription.displayName = SheetPrimitive.Description.displayName; + +)) +SheetDescription.displayName = SheetPrimitive.Description.displayName export { Sheet, + SheetPortal, + SheetOverlay, + SheetTrigger, SheetClose, SheetContent, - SheetDescription, - SheetFooter, SheetHeader, - SheetOverlay, - SheetPortal, + SheetFooter, SheetTitle, - SheetTrigger, -}; + SheetDescription, +} diff --git a/frontend/src/pages/Index.tsx b/frontend/src/pages/Index.tsx index b9805ae..622fd60 100644 --- a/frontend/src/pages/Index.tsx +++ b/frontend/src/pages/Index.tsx @@ -16,6 +16,11 @@ import { PopoverContent, PopoverTrigger, } from '@/components/ui/popover'; +import { + Sheet, + SheetContent, + SheetTrigger, +} from '@/components/ui/sheet'; import { AlertDialog, AlertDialogAction, @@ -62,8 +67,8 @@ interface SettingsState { const defaultSettings: SettingsState = { showPartialAvailability: false, displayTimezone: getUserTimezone(), - showSecondaryTimezone: false, - secondaryTimezone: 'America/Toronto', // Company timezone as default secondary + showSecondaryTimezone: true, + secondaryTimezone: 'America/Montreal', // Company timezone as default secondary }; function apiToParticipant(p: ParticipantAPI): Participant { @@ -90,7 +95,7 @@ const Index = ({ defaultTab = 'schedule' }: IndexProps) => { const [availabilitySlots, setAvailabilitySlots] = useState([]); const [selectedSlot, setSelectedSlot] = useState(null); const [isModalOpen, setIsModalOpen] = useState(false); - const [useRedesign, setUseRedesign] = useState(false); + const [useRedesign, setUseRedesign] = useState(true); const [settings, setSettings] = useState(defaultSettings); const [weekOffset, setWeekOffset] = useState(0); const [isLoading, setIsLoading] = useState(false); @@ -316,110 +321,111 @@ const Index = ({ defaultTab = 'schedule' }: IndexProps) => { > - - + + - - -
-

Settings

- -
- - + + +
+
+

Settings

+

+ Configure your calendar preferences. +

-
- - - setSettings((prev) => ({ ...prev, showPartialAvailability: checked })) - } - /> -
-

- When enabled, shows time slots where only some participants are available. -

- -
-
-
- - + +

Schedule a Meeting @@ -482,7 +488,7 @@ const Index = ({ defaultTab = 'schedule' }: IndexProps) => {

- + { displayTimezone={settings.displayTimezone} onSuccess={loadAvailability} /> -
+
); };