This commit is contained in:
Joyce
2026-02-05 13:45:32 -05:00
parent 7a0f11ee88
commit 26e553bfd0
10 changed files with 569 additions and 57 deletions

View File

@@ -5,6 +5,7 @@ import { ParticipantSelector } from '@/components/ParticipantSelector';
import { ParticipantManager } from '@/components/ParticipantManager';
import { AvailabilityHeatmap } from '@/components/AvailabilityHeatmap';
import { ScheduleModal } from '@/components/ScheduleModal';
import { TimezoneSelector } from '@/components/TimezoneSelector';
import { Participant, TimeSlot } from '@/types/calendar';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Switch } from '@/components/ui/switch';
@@ -14,6 +15,17 @@ import {
PopoverContent,
PopoverTrigger,
} from '@/components/ui/popover';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from '@/components/ui/alert-dialog';
import { Button } from '@/components/ui/button';
import { Users, CalendarDays, Settings, RefreshCw } from 'lucide-react';
import { useToast } from '@/hooks/use-toast';
@@ -24,17 +36,33 @@ import {
deleteParticipant,
fetchAvailability,
syncCalendars,
clearAllBookings,
ParticipantAPI,
} from '@/api/client';
const SETTINGS_KEY = 'calendar-settings';
// Get user's local timezone
const getUserTimezone = (): string => {
try {
return Intl.DateTimeFormat().resolvedOptions().timeZone;
} catch {
return 'America/Toronto';
}
};
interface SettingsState {
showPartialAvailability: boolean;
displayTimezone: string;
showSecondaryTimezone: boolean;
secondaryTimezone: string;
}
const defaultSettings: SettingsState = {
showPartialAvailability: false,
displayTimezone: getUserTimezone(),
showSecondaryTimezone: false,
secondaryTimezone: 'America/Toronto', // Company timezone as default secondary
};
function apiToParticipant(p: ParticipantAPI): Participant {
@@ -210,6 +238,25 @@ const Index = ({ defaultTab = 'schedule' }: IndexProps) => {
setIsModalOpen(true);
};
const handleClearBookings = async () => {
try {
await clearAllBookings();
if (selectedParticipants.length > 0) {
await loadAvailability();
}
toast({
title: 'Bookings cleared',
description: 'All scheduled meetings have been removed',
});
} catch (error) {
toast({
title: 'Error clearing bookings',
description: error instanceof Error ? error.message : 'Unknown error',
variant: 'destructive',
});
}
};
return (
<div className="min-h-screen bg-background">
<Header />
@@ -255,6 +302,10 @@ const Index = ({ defaultTab = 'schedule' }: IndexProps) => {
<div className="space-y-8">
<div className="text-center relative">
<div className="absolute right-0 top-0 flex items-center gap-2">
<TimezoneSelector
value={settings.displayTimezone}
onChange={(tz) => setSettings((prev) => ({ ...prev, displayTimezone: tz }))}
/>
<Button
variant="ghost"
size="icon"
@@ -269,7 +320,7 @@ const Index = ({ defaultTab = 'schedule' }: IndexProps) => {
<Settings className="w-5 h-5" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-72" align="end">
<PopoverContent className="w-80" align="end">
<div className="space-y-4">
<h4 className="font-medium">Settings</h4>
<div className="flex items-center justify-between gap-4">
@@ -287,6 +338,71 @@ const Index = ({ defaultTab = 'schedule' }: IndexProps) => {
<p className="text-xs text-muted-foreground">
When enabled, shows time slots where only some participants are available.
</p>
<div className="border-t border-border pt-4">
<div className="flex items-center justify-between gap-4">
<Label htmlFor="secondary-timezone" className="text-sm cursor-pointer">
Show secondary timezone
</Label>
<Switch
id="secondary-timezone"
checked={settings.showSecondaryTimezone}
onCheckedChange={(checked) =>
setSettings((prev) => ({ ...prev, showSecondaryTimezone: checked }))
}
/>
</div>
{settings.showSecondaryTimezone && (
<div className="mt-3">
<Label className="text-xs text-muted-foreground mb-2 block">
Secondary timezone
</Label>
<TimezoneSelector
value={settings.secondaryTimezone}
onChange={(tz) => setSettings((prev) => ({ ...prev, secondaryTimezone: tz }))}
/>
</div>
)}
<p className="text-xs text-muted-foreground mt-2">
Display times in two timezones side by side.
</p>
</div>
<div className="border-t border-border pt-4">
<AlertDialog>
<AlertDialogTrigger asChild>
<Button
variant="destructive"
size="sm"
className="w-full"
>
Clear All Bookings
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Clear all bookings?</AlertDialogTitle>
<AlertDialogDescription>
This will remove all scheduled meetings from the system.
This action cannot be undone. Calendar invites already
sent will not be affected.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={handleClearBookings}
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
>
Clear All
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<p className="text-xs text-muted-foreground mt-2">
Remove all scheduled meetings from the system.
</p>
</div>
</div>
</PopoverContent>
</Popover>
@@ -328,6 +444,9 @@ const Index = ({ defaultTab = 'schedule' }: IndexProps) => {
isLoading={isLoading}
weekOffset={weekOffset}
onWeekOffsetChange={setWeekOffset}
displayTimezone={settings.displayTimezone}
showSecondaryTimezone={settings.showSecondaryTimezone}
secondaryTimezone={settings.secondaryTimezone}
/>
</>
)}
@@ -344,6 +463,8 @@ const Index = ({ defaultTab = 'schedule' }: IndexProps) => {
}}
slot={selectedSlot}
participants={selectedParticipants}
displayTimezone={settings.displayTimezone}
onSuccess={loadAvailability}
/>
</div>
);