diff --git a/src/components/admin/ParkForm.tsx b/src/components/admin/ParkForm.tsx index d224916f..9e4ee75e 100644 --- a/src/components/admin/ParkForm.tsx +++ b/src/components/admin/ParkForm.tsx @@ -230,11 +230,13 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }:
- - Closing Date (if applicable) + setValue('closing_date', date ? date.toISOString().split('T')[0] : '')} + placeholder="Select closing date" + disablePast={false} + fromYear={1800} />
diff --git a/src/components/admin/RideForm.tsx b/src/components/admin/RideForm.tsx index 892adce7..01cb057d 100644 --- a/src/components/admin/RideForm.tsx +++ b/src/components/admin/RideForm.tsx @@ -291,11 +291,13 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }:
- - Closing Date (if applicable) + setValue('closing_date', date ? date.toISOString().split('T')[0] : '')} + placeholder="Select closing date" + disablePast={false} + fromYear={1800} />
diff --git a/src/components/parks/ParkFilters.tsx b/src/components/parks/ParkFilters.tsx index 5472c305..92df64d7 100644 --- a/src/components/parks/ParkFilters.tsx +++ b/src/components/parks/ParkFilters.tsx @@ -9,6 +9,7 @@ import { Separator } from '@/components/ui/separator'; import { RotateCcw } from 'lucide-react'; import { Park } from '@/types/database'; import { FilterState } from '@/pages/Parks'; +import { MonthYearPicker } from '@/components/ui/month-year-picker'; interface ParkFiltersProps { filters: FilterState; @@ -141,27 +142,25 @@ export function ParkFilters({ filters, onFiltersChange, parks }: ParkFiltersProp
- onFiltersChange({ + onFiltersChange({ ...filters, - openingYearStart: e.target.value ? parseInt(e.target.value) : null + openingYearStart: date ? date.getFullYear() : null })} + placeholder="From year" + fromYear={minYear} + toYear={currentYear} /> - onFiltersChange({ + onFiltersChange({ ...filters, - openingYearEnd: e.target.value ? parseInt(e.target.value) : null + openingYearEnd: date ? date.getFullYear() : null })} + placeholder="To year" + fromYear={minYear} + toYear={currentYear} />
diff --git a/src/components/reviews/ReviewForm.tsx b/src/components/reviews/ReviewForm.tsx index fa1c09ba..47183f14 100644 --- a/src/components/reviews/ReviewForm.tsx +++ b/src/components/reviews/ReviewForm.tsx @@ -152,9 +152,13 @@ export function ReviewForm({ setValue('visit_date', date ? date.toISOString().split('T')[0] : '')} - placeholder="Select visit date" + placeholder="When did you visit?" disableFuture={true} + fromYear={1950} /> +

+ Select the date of your visit to help others understand when this experience occurred. +

{/* Wait Time (for rides) */} diff --git a/src/components/search/SearchFilters.tsx b/src/components/search/SearchFilters.tsx index 91328f05..ef4f924e 100644 --- a/src/components/search/SearchFilters.tsx +++ b/src/components/search/SearchFilters.tsx @@ -10,6 +10,7 @@ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/component import { ChevronDown, Filter, X } from 'lucide-react'; import { Combobox } from '@/components/ui/combobox'; import { useCountries, useStatesProvinces, useManufacturers, useCompanyHeadquarters } from '@/hooks/useAutocompleteData'; +import { DateRangePicker, DateRange } from '@/components/ui/date-range-picker'; export interface SearchFilters { // Park filters @@ -19,6 +20,7 @@ export interface SearchFilters { status?: string; openingYearMin?: number; openingYearMax?: number; + openingDateRange?: DateRange; ratingMin?: number; ratingMax?: number; rideCountMin?: number; @@ -33,11 +35,13 @@ export interface SearchFilters { speedMin?: number; speedMax?: number; intensityLevel?: string; + rideDateRange?: DateRange; // Company filters companyType?: string; foundedYearMin?: number; foundedYearMax?: number; + companyDateRange?: DateRange; headquarters?: string; } @@ -179,25 +183,37 @@ export function SearchFiltersComponent({ filters, onFiltersChange, activeTab }:
- -
- updateFilter('openingYearMin', e.target.value ? parseInt(e.target.value) : undefined)} - /> - updateFilter('openingYearMax', e.target.value ? parseInt(e.target.value) : undefined)} - /> -
+ + updateFilter('openingDateRange', range)} + placeholder="Select opening date range" + disableFuture={true} + fromYear={1800} + presets={[ + { + label: "Last 10 years", + range: { + from: new Date(new Date().getFullYear() - 10, 0, 1), + to: new Date(), + }, + }, + { + label: "Last 25 years", + range: { + from: new Date(new Date().getFullYear() - 25, 0, 1), + to: new Date(), + }, + }, + { + label: "Before 2000", + range: { + from: new Date(1800, 0, 1), + to: new Date(1999, 11, 31), + }, + }, + ]} + />
@@ -301,6 +317,40 @@ export function SearchFiltersComponent({ filters, onFiltersChange, activeTab }:
+
+ + updateFilter('rideDateRange', range)} + placeholder="Select ride opening date range" + disableFuture={true} + fromYear={1800} + presets={[ + { + label: "Recent (last 5 years)", + range: { + from: new Date(new Date().getFullYear() - 5, 0, 1), + to: new Date(), + }, + }, + { + label: "Modern era (2000+)", + range: { + from: new Date(2000, 0, 1), + to: new Date(), + }, + }, + { + label: "Classic era (before 2000)", + range: { + from: new Date(1800, 0, 1), + to: new Date(1999, 11, 31), + }, + }, + ]} + /> +
+
@@ -382,25 +432,37 @@ export function SearchFiltersComponent({ filters, onFiltersChange, activeTab }:
- -
- updateFilter('foundedYearMin', e.target.value ? parseInt(e.target.value) : undefined)} - /> - updateFilter('foundedYearMax', e.target.value ? parseInt(e.target.value) : undefined)} - /> -
+ + updateFilter('companyDateRange', range)} + placeholder="Select founding date range" + disableFuture={true} + fromYear={1800} + presets={[ + { + label: "Founded after 2000", + range: { + from: new Date(2000, 0, 1), + to: new Date(), + }, + }, + { + label: "Founded 1950-2000", + range: { + from: new Date(1950, 0, 1), + to: new Date(1999, 11, 31), + }, + }, + { + label: "Founded before 1950", + range: { + from: new Date(1800, 0, 1), + to: new Date(1949, 11, 31), + }, + }, + ]} + />
)} diff --git a/src/components/ui/date-range-picker.tsx b/src/components/ui/date-range-picker.tsx new file mode 100644 index 00000000..6a101346 --- /dev/null +++ b/src/components/ui/date-range-picker.tsx @@ -0,0 +1,182 @@ +import * as React from "react"; +import { format } from "date-fns"; +import { CalendarIcon, X } from "lucide-react"; +import type { DateRange as CalendarDateRange } from "react-day-picker"; + +import { cn } from "@/lib/utils"; +import { Button } from "@/components/ui/button"; +import { Calendar } from "@/components/ui/calendar"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { Badge } from "@/components/ui/badge"; + +export interface DateRange { + from?: Date; + to?: Date; +} + +export interface DateRangePickerProps { + value?: DateRange; + onChange?: (range: DateRange | undefined) => void; + placeholder?: string; + disabled?: boolean; + className?: string; + disableFuture?: boolean; + disablePast?: boolean; + fromYear?: number; + toYear?: number; + presets?: Array<{ + label: string; + range: DateRange; + }>; +} + +const defaultPresets = [ + { + label: "Today", + range: { + from: new Date(), + to: new Date(), + }, + }, + { + label: "Last 7 days", + range: { + from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), + to: new Date(), + }, + }, + { + label: "Last 30 days", + range: { + from: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000), + to: new Date(), + }, + }, + { + label: "This year", + range: { + from: new Date(new Date().getFullYear(), 0, 1), + to: new Date(), + }, + }, +]; + +export function DateRangePicker({ + value, + onChange, + placeholder = "Pick a date range", + disabled = false, + className, + disableFuture = false, + disablePast = false, + fromYear, + toYear, + presets = defaultPresets, +}: DateRangePickerProps) { + const [open, setOpen] = React.useState(false); + + const handleSelect = (range: DateRange | undefined) => { + onChange?.(range); + }; + + const handlePresetSelect = (preset: DateRange) => { + handleSelect(preset); + setOpen(false); + }; + + const handleClear = () => { + handleSelect(undefined); + }; + + const getDisabledDates = (date: Date) => { + const now = new Date(); + if (disableFuture && date > now) return true; + if (disablePast && date < now) return true; + return false; + }; + + const formatRange = (range: DateRange | undefined) => { + if (!range?.from) return placeholder; + if (!range.to) return format(range.from, "MMM dd, yyyy"); + if (range.from.getTime() === range.to.getTime()) { + return format(range.from, "MMM dd, yyyy"); + } + return `${format(range.from, "MMM dd, yyyy")} - ${format(range.to, "MMM dd, yyyy")}`; + }; + + return ( +
+ + + + + +
+
+ void} + numberOfMonths={2} + disabled={getDisabledDates} + className="p-3 pointer-events-auto" + fromYear={fromYear} + toYear={toYear} + /> +
+ {presets.length > 0 && ( +
+
Presets
+
+ {presets.map((preset, index) => ( + + ))} +
+
+ )} +
+
+
+ {value?.from && ( + + {formatRange(value)} + + )} +
+ ); +} \ No newline at end of file diff --git a/src/components/ui/month-year-picker.tsx b/src/components/ui/month-year-picker.tsx new file mode 100644 index 00000000..ed9cbf7e --- /dev/null +++ b/src/components/ui/month-year-picker.tsx @@ -0,0 +1,143 @@ +import * as React from "react"; +import { format } from "date-fns"; +import { CalendarIcon, ChevronLeft, ChevronRight } from "lucide-react"; + +import { cn } from "@/lib/utils"; +import { Button } from "@/components/ui/button"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; + +export interface MonthYearPickerProps { + date?: Date; + onSelect?: (date: Date | undefined) => void; + placeholder?: string; + disabled?: boolean; + className?: string; + fromYear?: number; + toYear?: number; +} + +export function MonthYearPicker({ + date, + onSelect, + placeholder = "Pick month and year", + disabled = false, + className, + fromYear = 1800, + toYear = new Date().getFullYear(), +}: MonthYearPickerProps) { + const [open, setOpen] = React.useState(false); + const [tempMonth, setTempMonth] = React.useState(date?.getMonth() ?? new Date().getMonth()); + const [tempYear, setTempYear] = React.useState(date?.getFullYear() ?? new Date().getFullYear()); + + const months = [ + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" + ]; + + const years = Array.from( + { length: toYear - fromYear + 1 }, + (_, i) => toYear - i + ); + + const handleSelect = () => { + const newDate = new Date(tempYear, tempMonth, 1); + onSelect?.(newDate); + setOpen(false); + }; + + const handleClear = () => { + onSelect?.(undefined); + setOpen(false); + }; + + React.useEffect(() => { + if (date) { + setTempMonth(date.getMonth()); + setTempYear(date.getFullYear()); + } + }, [date]); + + return ( + + + + + +
+
+
+ + +
+ +
+ + +
+
+ +
+ + +
+
+
+
+ ); +} \ No newline at end of file