imporvements
This commit is contained in:
@@ -6,15 +6,16 @@ import {
|
||||
PopoverTrigger,
|
||||
} from '@/components/ui/popover';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Check, X, Loader2 } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { Check, X, Loader2, ChevronLeft, ChevronRight, ChevronsRight } from 'lucide-react';
|
||||
|
||||
const TIMEZONE = 'America/Toronto';
|
||||
|
||||
const dayNames = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'];
|
||||
const hours = [9, 10, 11, 12, 13, 14, 15, 16, 17];
|
||||
|
||||
// Get the dates for Mon-Fri of the current week in a specific timezone
|
||||
const getWeekDates = (timezone: string): string[] => {
|
||||
// Get the dates for Mon-Fri of a week in a specific timezone, offset by N weeks
|
||||
const getWeekDates = (timezone: string, weekOffset: number = 0): string[] => {
|
||||
// Get "now" in the target timezone
|
||||
const now = new Date();
|
||||
const formatter = new Intl.DateTimeFormat('en-CA', {
|
||||
@@ -33,7 +34,7 @@ const getWeekDates = (timezone: string): string[] => {
|
||||
const dayOfWeek = todayDate.getDay();
|
||||
const daysToMonday = dayOfWeek === 0 ? -6 : 1 - dayOfWeek;
|
||||
|
||||
const mondayDate = new Date(year, month - 1, day + daysToMonday);
|
||||
const mondayDate = new Date(year, month - 1, day + daysToMonday + weekOffset * 7);
|
||||
|
||||
return dayNames.map((_, i) => {
|
||||
const d = new Date(mondayDate);
|
||||
@@ -73,12 +74,18 @@ const toUTCDate = (dateStr: string, hour: number, timezone: string): Date => {
|
||||
return utcDate;
|
||||
};
|
||||
|
||||
const MIN_WEEK_OFFSET = 0;
|
||||
const DEFAULT_MAX_WEEK_OFFSET = 1;
|
||||
const EXPANDED_MAX_WEEK_OFFSET = 4;
|
||||
|
||||
interface AvailabilityHeatmapProps {
|
||||
slots: TimeSlot[];
|
||||
selectedParticipants: Participant[];
|
||||
onSlotSelect: (slot: TimeSlot) => void;
|
||||
showPartialAvailability?: boolean;
|
||||
isLoading?: boolean;
|
||||
weekOffset?: number;
|
||||
onWeekOffsetChange?: (offset: number) => void;
|
||||
}
|
||||
|
||||
export const AvailabilityHeatmap = ({
|
||||
@@ -87,8 +94,12 @@ export const AvailabilityHeatmap = ({
|
||||
onSlotSelect,
|
||||
showPartialAvailability = false,
|
||||
isLoading = false,
|
||||
weekOffset = 0,
|
||||
onWeekOffsetChange,
|
||||
}: AvailabilityHeatmapProps) => {
|
||||
const weekDates = getWeekDates(TIMEZONE);
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const maxWeekOffset = expanded ? EXPANDED_MAX_WEEK_OFFSET : DEFAULT_MAX_WEEK_OFFSET;
|
||||
const weekDates = getWeekDates(TIMEZONE, weekOffset);
|
||||
|
||||
// Find a slot that matches the given display timezone date/hour
|
||||
const getSlot = (dateStr: string, hour: number): TimeSlot | undefined => {
|
||||
@@ -167,13 +178,67 @@ export const AvailabilityHeatmap = ({
|
||||
<div className="bg-card rounded-xl shadow-card p-6 animate-slide-up">
|
||||
<div className="mb-6 flex justify-between items-start">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-foreground">
|
||||
Common Availability — Week of {getWeekDateRange()}
|
||||
</h3>
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="text-lg font-semibold text-foreground">
|
||||
Common Availability — Week of {getWeekDateRange()}
|
||||
</h3>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
{selectedParticipants.length} participant{selectedParticipants.length > 1 ? 's' : ''}: {selectedParticipants.map(p => p.name.split(' ')[0]).join(', ')}
|
||||
</p>
|
||||
</div>
|
||||
{onWeekOffsetChange && (
|
||||
<div className="flex items-center gap-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
disabled={weekOffset <= MIN_WEEK_OFFSET}
|
||||
onClick={() => onWeekOffsetChange(weekOffset - 1)}
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4" />
|
||||
</Button>
|
||||
{weekOffset !== 0 && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-8 text-xs"
|
||||
onClick={() => onWeekOffsetChange(0)}
|
||||
>
|
||||
This week
|
||||
</Button>
|
||||
)}
|
||||
{weekOffset < maxWeekOffset ? (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={() => onWeekOffsetChange(weekOffset + 1)}
|
||||
>
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
</Button>
|
||||
) : !expanded ? (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="h-8 text-xs gap-1"
|
||||
onClick={() => setExpanded(true)}
|
||||
>
|
||||
<ChevronsRight className="w-3.5 h-3.5" />
|
||||
Look further ahead
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
disabled
|
||||
>
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="overflow-x-auto">
|
||||
|
||||
Reference in New Issue
Block a user