diff --git a/src/components/Index/filters/FilterSortControls.tsx b/src/components/Index/filters/FilterSortControls.tsx index c6787f99..5f64581a 100644 --- a/src/components/Index/filters/FilterSortControls.tsx +++ b/src/components/Index/filters/FilterSortControls.tsx @@ -28,20 +28,20 @@ interface FilterSortControlsProps { onClear: () => void; } -export const FilterSortControls = ({ +export function FilterSortControls({ state, onStateChange, onClear, -}: FilterSortControlsProps) => { +}: FilterSortControlsProps) { const [isFiltersExpanded, setIsFiltersExpanded] = useState(false); const [isMobile, setIsMobile] = useState(false); const { genres } = useGenres(); const { groups } = useGroups(); useEffect(() => { - const checkMobile = () => { + function checkMobile() { setIsMobile(window.innerWidth < 768); - }; + } checkMobile(); window.addEventListener("resize", checkMobile); @@ -49,13 +49,13 @@ export const FilterSortControls = ({ return () => window.removeEventListener("resize", checkMobile); }, []); - const handleSortChange = (sort: SortOption) => { + function handleSortChange(sort: SortOption) { onStateChange({ sort, sortLocked: false }); - }; + } - const handleRefreshRankings = () => { + function handleRefreshRankings() { onStateChange({ sortLocked: false }); - }; + } const hasActiveFilters = state.stages.length > 0 || state.genres.length > 0 || state.minRating > 0; @@ -140,9 +140,7 @@ export const FilterSortControls = ({ onStateChange({ groupId: undefined })} - className={`text-purple-100 hover:bg-purple-600/30 ${ - !state.groupId ? "bg-purple-600/20" : "" - }`} + className={`text-purple-100 hover:bg-purple-600/30 ${!state.groupId ? "bg-purple-600/20" : ""}`} > All Votes @@ -150,9 +148,7 @@ export const FilterSortControls = ({ onStateChange({ groupId: group.id })} - className={`text-purple-100 hover:bg-purple-600/30 ${ - state.groupId === group.id ? "bg-purple-600/20" : "" - }`} + className={`text-purple-100 hover:bg-purple-600/30 ${state.groupId === group.id ? "bg-purple-600/20" : ""}`} > {group.name} {group.member_count && ( @@ -217,4 +213,4 @@ export const FilterSortControls = ({ )} ); -}; +} diff --git a/src/components/schedule/ScheduleHorizontalTimelineView.tsx b/src/components/schedule/ScheduleHorizontalTimelineView.tsx index 12b473b7..8e622c53 100644 --- a/src/components/schedule/ScheduleHorizontalTimelineView.tsx +++ b/src/components/schedule/ScheduleHorizontalTimelineView.tsx @@ -3,6 +3,8 @@ import { useScheduleData } from "@/hooks/useScheduleData"; import { calculateTimelineData } from "@/lib/timelineCalculator"; import { StageLabels } from "./StageLabels"; import { TimelineContainer } from "./TimelineContainer"; +import { useFestivalEdition } from "@/contexts/FestivalEditionContext"; +import { useEditionSetsQuery } from "@/hooks/queries/useEditionSetsQuery"; interface ScheduleHorizontalTimelineViewProps { userVotes: Record; @@ -13,14 +15,23 @@ export function ScheduleHorizontalTimelineView({ userVotes, onVote, }: ScheduleHorizontalTimelineViewProps) { - const { scheduleDays, loading, error } = useScheduleData(); + const { edition } = useFestivalEdition(); + const { data: editionSets = [], isLoading: setsLoading } = + useEditionSetsQuery(edition?.id); + const { scheduleDays, loading, error } = useScheduleData(editionSets); - const timelineData = useMemo( - () => calculateTimelineData(scheduleDays), - [scheduleDays], - ); + const timelineData = useMemo(() => { + if (!edition || !edition.start_date || !edition.end_date) { + return null; + } + return calculateTimelineData( + new Date(edition.start_date), + new Date(edition.end_date), + scheduleDays, + ); + }, [edition, scheduleDays]); - if (loading) { + if (loading || setsLoading) { return (

Loading horizontal timeline...

@@ -39,7 +50,16 @@ export function ScheduleHorizontalTimelineView({ if (!timelineData) { return (
-

No performances scheduled.

+

Festival dates not available yet.

+
+ ); + } + + // Check if schedule is published + if (!edition?.published) { + return ( +
+

Schedule not yet published.

); } diff --git a/src/hooks/useScheduleData.ts b/src/hooks/useScheduleData.ts index bff60c8f..2cac303f 100644 --- a/src/hooks/useScheduleData.ts +++ b/src/hooks/useScheduleData.ts @@ -1,7 +1,7 @@ import { useMemo } from "react"; import { formatDateTime } from "@/lib/timeUtils"; import { format, startOfDay } from "date-fns"; -import { useOfflineSetsData } from "./useOfflineSetsData"; +import type { FestivalSet } from "@/services/queries"; export interface ScheduleDay { date: string; @@ -35,8 +35,12 @@ export interface ScheduleSet extends ScheduleArtist { artists: ScheduleArtist[]; } -export const useScheduleData = (use24Hour: boolean = false) => { - const { sets, loading, error } = useOfflineSetsData(); +export function useScheduleData( + sets?: FestivalSet[], + use24Hour: boolean = false, +) { + const loading = false; + const error = null; const scheduleDays = useMemo(() => { // Enhanced defensive checks @@ -146,4 +150,4 @@ export const useScheduleData = (use24Hour: boolean = false) => { loading, error, }; -}; +} diff --git a/src/lib/timelineCalculator.ts b/src/lib/timelineCalculator.ts index c6f24e2d..7354ea73 100644 --- a/src/lib/timelineCalculator.ts +++ b/src/lib/timelineCalculator.ts @@ -19,27 +19,24 @@ export interface TimelineData { festivalEnd: Date; } -export const calculateTimelineData = ( +export function calculateTimelineData( + festivalStartDate: Date, + festivalEndDate: Date, scheduleDays: ScheduleDay[], -): TimelineData | null => { +): TimelineData | null { if (!scheduleDays || scheduleDays.length === 0) return null; - // Find the earliest and latest times across all days - let earliestTime = new Date("2099-01-01"); - let latestTime = new Date("1900-01-01"); + // Require festival dates to be provided + if (!festivalStartDate || !festivalEndDate) { + return null; + } - scheduleDays.forEach((day) => { - day.stages.forEach((stage) => { - stage.artists.forEach((artist) => { - if (artist.startTime && artist.startTime < earliestTime) { - earliestTime = artist.startTime; - } - if (artist.endTime && artist.endTime > latestTime) { - latestTime = artist.endTime; - } - }); - }); - }); + // Find the earliest set time from all scheduled sets + const earliestSetTime = calculateEarliestSetTime(scheduleDays); + + // Use the earliest set time if available, otherwise fall back to festival start date + const earliestTime = earliestSetTime || new Date(festivalStartDate); + const latestTime = new Date(festivalEndDate); // Create unified time grid from festival start to end const timeSlots = []; @@ -102,4 +99,20 @@ export const calculateTimelineData = ( festivalStart: earliestTime, festivalEnd: latestTime, }; -}; +} +function calculateEarliestSetTime(scheduleDays: ScheduleDay[]) { + let earliestSetTime: Date | null = null; + + scheduleDays.forEach((day) => { + day.stages.forEach((stage) => { + stage.artists.forEach((artist) => { + if (artist.startTime) { + if (!earliestSetTime || artist.startTime < earliestSetTime) { + earliestSetTime = artist.startTime; + } + } + }); + }); + }); + return earliestSetTime; +}