diff --git a/src/App.tsx b/src/App.tsx
index 50b653a..7f1f5e7 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -20,6 +20,7 @@ import DynamicDemo from "./pages/DynamicDemo";
import CanvasDemo from "./pages/CanvasDemo";
import MultiWindowDemo from "./pages/MultiWindowDemo";
import AdvancedButtonDemo from "./pages/AdvancedButtonDemo";
+import DatePickerDemo from "./pages/DatePickerDemo";
import CommsTarget from "./pages/CommsTarget";
import VehicleSimulator from "./pages/VehicleSimulator";
import ThreeDChess from "./pages/ThreeDChess";
@@ -52,6 +53,7 @@ const App = () => (
} />
} />
} />
+ } />
} />
} />
} />
diff --git a/src/components/ui/date-picker.tsx b/src/components/ui/date-picker.tsx
new file mode 100644
index 0000000..de9abfe
--- /dev/null
+++ b/src/components/ui/date-picker.tsx
@@ -0,0 +1,261 @@
+import * as React from "react";
+import { format } from "date-fns";
+import { Calendar as CalendarIcon } from "lucide-react";
+
+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";
+
+export interface DatePickerProps {
+ date?: Date;
+ onDateChange?: (date: Date | undefined) => void;
+ placeholder?: string;
+ disabled?: boolean;
+ className?: string;
+ id?: string;
+ "data-testid"?: string;
+ "aria-label"?: string;
+}
+
+const DatePicker = React.forwardRef<
+ HTMLButtonElement,
+ DatePickerProps
+>(({
+ date,
+ onDateChange,
+ placeholder = "Pick a date",
+ disabled = false,
+ className,
+ id,
+ "data-testid": dataTestId,
+ "aria-label": ariaLabel,
+ ...props
+}, ref) => {
+ return (
+
+
+
+
+
+
+
+
+ );
+});
+DatePicker.displayName = "DatePicker";
+
+export interface DateRangePickerProps {
+ dateRange?: { from: Date | undefined; to: Date | undefined };
+ onDateRangeChange?: (range: { from: Date | undefined; to: Date | undefined }) => void;
+ placeholder?: string;
+ disabled?: boolean;
+ className?: string;
+ id?: string;
+ "data-testid"?: string;
+ "aria-label"?: string;
+}
+
+const DateRangePicker = React.forwardRef<
+ HTMLButtonElement,
+ DateRangePickerProps
+>(({
+ dateRange,
+ onDateRangeChange,
+ placeholder = "Pick a date range",
+ disabled = false,
+ className,
+ id,
+ "data-testid": dataTestId,
+ "aria-label": ariaLabel,
+ ...props
+}, ref) => {
+ return (
+
+
+
+
+
+ {
+ if (range) {
+ onDateRangeChange?.({
+ from: range.from,
+ to: range.to,
+ });
+ }
+ }}
+ numberOfMonths={2}
+ />
+
+
+ );
+});
+DateRangePicker.displayName = "DateRangePicker";
+
+export interface DatePickerWithPresetsProps {
+ date?: Date;
+ onDateChange?: (date: Date | undefined) => void;
+ placeholder?: string;
+ disabled?: boolean;
+ className?: string;
+ id?: string;
+ "data-testid"?: string;
+ "aria-label"?: string;
+ presets?: Array<{
+ label: string;
+ value: () => Date;
+ }>;
+}
+
+const DatePickerWithPresets = React.forwardRef<
+ HTMLButtonElement,
+ DatePickerWithPresetsProps
+>(({
+ date,
+ onDateChange,
+ placeholder = "Pick a date",
+ disabled = false,
+ className,
+ id,
+ "data-testid": dataTestId,
+ "aria-label": ariaLabel,
+ presets = [
+ {
+ label: "Today",
+ value: () => new Date(),
+ },
+ {
+ label: "Yesterday",
+ value: () => {
+ const yesterday = new Date();
+ yesterday.setDate(yesterday.getDate() - 1);
+ return yesterday;
+ },
+ },
+ {
+ label: "Last Week",
+ value: () => {
+ const lastWeek = new Date();
+ lastWeek.setDate(lastWeek.getDate() - 7);
+ return lastWeek;
+ },
+ },
+ {
+ label: "Last Month",
+ value: () => {
+ const lastMonth = new Date();
+ lastMonth.setMonth(lastMonth.getMonth() - 1);
+ return lastMonth;
+ },
+ },
+ ],
+ ...props
+}, ref) => {
+ return (
+
+
+
+
+
+
+
+
+
Quick Select
+
+ {presets.map((preset) => (
+
+ ))}
+
+
+
+
+
+
+
+ );
+});
+DatePickerWithPresets.displayName = "DatePickerWithPresets";
+
+export { DatePicker, DateRangePicker, DatePickerWithPresets };
diff --git a/src/pages/AdvancedButtonDemo.tsx b/src/pages/AdvancedButtonDemo.tsx
index 6ff7f9d..2c0d058 100644
--- a/src/pages/AdvancedButtonDemo.tsx
+++ b/src/pages/AdvancedButtonDemo.tsx
@@ -298,7 +298,7 @@ const AdvancedButtonDemo = () => {
return (
diff --git a/src/pages/CanvasDemo.tsx b/src/pages/CanvasDemo.tsx
index 71bc475..cedfb2c 100644
--- a/src/pages/CanvasDemo.tsx
+++ b/src/pages/CanvasDemo.tsx
@@ -263,7 +263,7 @@ const CanvasDemo = () => {
return (
diff --git a/src/pages/DatePickerDemo.tsx b/src/pages/DatePickerDemo.tsx
new file mode 100644
index 0000000..d9c8d7e
--- /dev/null
+++ b/src/pages/DatePickerDemo.tsx
@@ -0,0 +1,452 @@
+import { useState } from "react";
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+import { Button } from "@/components/ui/button";
+import { Badge } from "@/components/ui/badge";
+import { Separator } from "@/components/ui/separator";
+import { DatePicker, DateRangePicker, DatePickerWithPresets } from "@/components/ui/date-picker";
+import { format } from "date-fns";
+import { ModuleHeader } from "@/components/ModuleHeader";
+import { useToast } from "@/hooks/use-toast";
+
+const DatePickerDemo = () => {
+ const { toast } = useToast();
+
+ // Basic DatePicker state
+ const [selectedDate, setSelectedDate] = useState
();
+ const [disabledDate, setDisabledDate] = useState();
+
+ // Date Range state
+ const [dateRange, setDateRange] = useState<{ from: Date | undefined; to: Date | undefined }>({
+ from: undefined,
+ to: undefined,
+ });
+
+ // DatePicker with presets state
+ const [presetDate, setPresetDate] = useState();
+
+ // Form integration state
+ const [formData, setFormData] = useState({
+ startDate: undefined as Date | undefined,
+ endDate: undefined as Date | undefined,
+ eventDate: undefined as Date | undefined,
+ });
+
+ // Validation state
+ const [validationErrors, setValidationErrors] = useState([]);
+
+ const validateForm = () => {
+ const errors: string[] = [];
+
+ // Check if end date is before start date
+ if (formData.startDate && formData.endDate && formData.endDate < formData.startDate) {
+ errors.push("End date must be after start date");
+ }
+
+ // Check if event date is in the past
+ if (formData.eventDate && formData.eventDate < new Date()) {
+ errors.push("Event date cannot be in the past");
+ }
+
+ setValidationErrors(errors);
+ return errors.length === 0;
+ };
+
+ const handleFormSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+
+ if (!validateForm()) {
+ toast({
+ title: "Form Validation Failed",
+ description: "Please correct the validation errors below",
+ variant: "destructive",
+ });
+ return;
+ }
+
+ const selectedDates = {
+ start: formData.startDate ? format(formData.startDate, "PPP") : "Not selected",
+ end: formData.endDate ? format(formData.endDate, "PPP") : "Not selected",
+ event: formData.eventDate ? format(formData.eventDate, "PPP") : "Not selected",
+ };
+
+ toast({
+ title: "Form Submitted Successfully!",
+ description: `Start: ${selectedDates.start} | End: ${selectedDates.end} | Event: ${selectedDates.event}`,
+ });
+ };
+
+ const clearAllDates = () => {
+ setSelectedDate(undefined);
+ setDisabledDate(undefined);
+ setDateRange({ from: undefined, to: undefined });
+ setPresetDate(undefined);
+ setFormData({
+ startDate: undefined,
+ endDate: undefined,
+ eventDate: undefined,
+ });
+ setValidationErrors([]);
+
+ toast({
+ title: "All Dates Cleared",
+ description: "All date selections have been reset",
+ });
+ };
+
+ return (
+
+
+
+
+
+
+ {/* Basic DatePicker */}
+
+
+
+ Basic Date Picker
+
+
+ Simple date selection with calendar popup
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Selected Date:
+
+ {selectedDate ? format(selectedDate, "EEEE, MMMM do, yyyy") : "No date selected"}
+
+
+
+
+
+ {/* Date Range Picker */}
+
+
+
+ Date Range Picker
+
+
+ Select start and end dates for date ranges
+
+
+
+
+
+
+
+
+
+
+
+
Selected Range:
+
+ {dateRange.from ? (
+ dateRange.to ? (
+ `${format(dateRange.from, "MMM dd, yyyy")} - ${format(dateRange.to, "MMM dd, yyyy")}`
+ ) : (
+ `From: ${format(dateRange.from, "MMM dd, yyyy")}`
+ )
+ ) : (
+ "No date range selected"
+ )}
+
+
+
+
+
+ {/* DatePicker with Presets */}
+
+
+
+ Date Picker with Presets
+
+
+ Quick date selection with common presets
+
+
+
+
+
+
+
+
+
+
+
+
Selected Date:
+
+ {presetDate ? format(presetDate, "EEEE, MMMM do, yyyy") : "No date selected"}
+
+
+
+
+
+ {/* Form Integration */}
+
+
+
+ Form Integration
+
+
+ Date pickers integrated in a form context
+
+
+
+
+
+
+
+
+ {/* Control Panel */}
+
+
+
+ Control Panel
+
+
+ Global controls for testing date picker interactions
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Mission Summary */}
+
+
+ MISSION SUMMARY
+ Date selection systems status and calendar interface metrics
+
+
+
+
+
DATE PICKER STATUS:
+
+ - Basic Date Picker: {selectedDate ? "ACTIVE" : "STANDBY"}
+ - Date Range Picker: {dateRange.from ? "ACTIVE" : "STANDBY"}
+ - Preset Date Picker: {presetDate ? "ACTIVE" : "STANDBY"}
+ - Form Integration: {formData.startDate || formData.endDate || formData.eventDate ? "ACTIVE" : "STANDBY"}
+
+
+
+
SYSTEM METRICS:
+
+ - Interface Status: OPERATIONAL
+ - Date Selections: {[selectedDate, presetDate, formData.startDate, formData.endDate, formData.eventDate].filter(Boolean).length}
+ - Range Status: {dateRange.from && dateRange.to ? "COMPLETE" : dateRange.from ? "PARTIAL" : "EMPTY"}
+ - Form State: {Object.values(formData).some(date => date) ? "POPULATED" : "EMPTY"}
+
+
+
+
+
+
TEST PROTOCOLS:
+
+
+
+ - • Calendar popup activation and trigger testing
+ - • Basic date selection and display
+ - • Date range selection protocols
+ - • Preset date quick selection
+
+
+
+
+ - • Form integration and submission
+ - • Form validation and error handling
+ - • Disabled state handling
+ - • Clear and reset functionality
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default DatePickerDemo;
diff --git a/src/pages/DragDropDemo.tsx b/src/pages/DragDropDemo.tsx
index 5c02885..2073e22 100644
--- a/src/pages/DragDropDemo.tsx
+++ b/src/pages/DragDropDemo.tsx
@@ -115,7 +115,7 @@ const DragDropDemo = () => {
return (
diff --git a/src/pages/DynamicDemo.tsx b/src/pages/DynamicDemo.tsx
index 6782219..ad32e2c 100644
--- a/src/pages/DynamicDemo.tsx
+++ b/src/pages/DynamicDemo.tsx
@@ -157,7 +157,7 @@ const DynamicDemo = () => {
return (
diff --git a/src/pages/FramesDemo.tsx b/src/pages/FramesDemo.tsx
index 86ec891..8d07531 100644
--- a/src/pages/FramesDemo.tsx
+++ b/src/pages/FramesDemo.tsx
@@ -53,7 +53,7 @@ const FramesDemo = () => {
return (
diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx
index eb0d414..37c6eaa 100644
--- a/src/pages/Index.tsx
+++ b/src/pages/Index.tsx
@@ -19,7 +19,7 @@ const Index = () => {
"/button-demo", "/text-input-demo", "/login-demo", "/dropdown-demo",
"/checkbox-radio-demo", "/table-demo", "/modal-demo", "/alert-demo",
"/file-upload-demo", "/drag-drop-demo", "/frames-demo", "/dynamic-demo",
- "/canvas-demo", "/multi-window-demo", "/advanced-button-demo"
+ "/canvas-demo", "/multi-window-demo", "/advanced-button-demo", "/date-picker-demo"
];
const demoPages = [
@@ -89,6 +89,13 @@ const Index = () => {
difficulty: "Intermediate",
elements: ["file inputs", "file upload", "file validation"],
implemented: true
+ }, {
+ title: "Date Picker Demo",
+ path: "/date-picker-demo",
+ description: "Test date selection, range picking, and calendar interactions",
+ difficulty: "Intermediate",
+ elements: ["date pickers", "calendar popups", "date ranges", "form integration", "date validation"],
+ implemented: true
},
// Advanced Elements
{
diff --git a/src/pages/MultiWindowDemo.tsx b/src/pages/MultiWindowDemo.tsx
index af821f6..1082891 100644
--- a/src/pages/MultiWindowDemo.tsx
+++ b/src/pages/MultiWindowDemo.tsx
@@ -228,7 +228,7 @@ const MultiWindowDemo = () => {
return (