Spaces:
Sleeping
Sleeping
| import { useState } from "react"; | |
| import { addDays, format, startOfMonth } from "date-fns"; | |
| import { Calendar as CalendarUI } from "@/components/ui/calendar"; | |
| import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; | |
| import { Label } from "@/components/ui/label"; | |
| import { Input } from "@/components/ui/input"; | |
| import { Button } from "@/components/ui/button"; | |
| import { Dropdown } from "@/components/ui/dropdown"; | |
| import { | |
| Dialog, | |
| DialogContent, | |
| DialogDescription, | |
| DialogFooter, | |
| DialogHeader, | |
| DialogTitle, | |
| DialogTrigger, | |
| } from "@/components/ui/dialog"; | |
| import Layout from "@/components/layout/layout"; | |
| import { CalendarClock, CalendarPlus, CalendarRange, Check, Filter, Users } from "lucide-react"; | |
| type EventType = "meeting" | "deadline" | "reminder" | "task"; | |
| type EventPriority = "low" | "medium" | "high"; | |
| interface CalendarEvent { | |
| id: string; | |
| title: string; | |
| date: Date; | |
| type: EventType; | |
| priority: EventPriority; | |
| assignees?: string[]; | |
| description?: string; | |
| } | |
| const CalendarPage = () => { | |
| const [date, setDate] = useState<Date>(new Date()); | |
| const [events, setEvents] = useState<CalendarEvent[]>([ | |
| { | |
| id: "1", | |
| title: "Team Standup", | |
| date: new Date(), | |
| type: "meeting", | |
| priority: "medium", | |
| assignees: ["Team Alpha"], | |
| description: "Daily team standup meeting" | |
| }, | |
| { | |
| id: "2", | |
| title: "Project Deadline", | |
| date: addDays(new Date(), 7), | |
| type: "deadline", | |
| priority: "high", | |
| description: "Final delivery of the UI components" | |
| }, | |
| { | |
| id: "3", | |
| title: "Review Backlog", | |
| date: addDays(new Date(), 2), | |
| type: "task", | |
| priority: "low", | |
| description: "Go through backlog and prioritize tasks" | |
| } | |
| ]); | |
| const [showAddEvent, setShowAddEvent] = useState(false); | |
| const [newEvent, setNewEvent] = useState<Partial<CalendarEvent>>({ | |
| title: "", | |
| date: new Date(), | |
| type: "meeting", | |
| priority: "medium", | |
| description: "" | |
| }); | |
| const [selectedView, setSelectedView] = useState<"month" | "week" | "day">("month"); | |
| const [filter, setFilter] = useState<EventType | "all">("all"); | |
| const handleAddEvent = () => { | |
| if (newEvent.title && newEvent.date) { | |
| const event: CalendarEvent = { | |
| id: Math.random().toString(36).substring(2, 9), | |
| title: newEvent.title, | |
| date: newEvent.date, | |
| type: newEvent.type as EventType, | |
| priority: newEvent.priority as EventPriority, | |
| description: newEvent.description | |
| }; | |
| setEvents([...events, event]); | |
| setNewEvent({ | |
| title: "", | |
| date: new Date(), | |
| type: "meeting", | |
| priority: "medium", | |
| description: "" | |
| }); | |
| setShowAddEvent(false); | |
| } | |
| }; | |
| const filteredEvents = events.filter(event => { | |
| if (filter === "all") return true; | |
| return event.type === filter; | |
| }); | |
| const getEventsForDate = (date: Date) => { | |
| return filteredEvents.filter(event => | |
| event.date.getDate() === date.getDate() && | |
| event.date.getMonth() === date.getMonth() && | |
| event.date.getFullYear() === date.getFullYear() | |
| ); | |
| }; | |
| const eventsForSelectedDate = getEventsForDate(date); | |
| const getPriorityColor = (priority: EventPriority) => { | |
| switch (priority) { | |
| case "high": return "text-red-500"; | |
| case "medium": return "text-amber-500"; | |
| case "low": return "text-green-500"; | |
| default: return ""; | |
| } | |
| }; | |
| const getEventTypeIcon = (type: EventType) => { | |
| switch (type) { | |
| case "meeting": return <Users className="h-4 w-4" />; | |
| case "deadline": return <CalendarRange className="h-4 w-4" />; | |
| case "reminder": return <CalendarClock className="h-4 w-4" />; | |
| case "task": return <Check className="h-4 w-4" />; | |
| default: return null; | |
| } | |
| }; | |
| return ( | |
| <Layout> | |
| <div className="container mx-auto py-6"> | |
| <div className="flex flex-col lg:flex-row gap-8"> | |
| <div className="w-full lg:w-3/5"> | |
| <Card> | |
| <CardHeader className="pb-3"> | |
| <div className="flex items-center justify-between"> | |
| <CardTitle>Calendar</CardTitle> | |
| <div className="flex items-center gap-2"> | |
| <Dropdown | |
| options={[ | |
| { label: "Month", value: "month" }, | |
| { label: "Week", value: "week" }, | |
| { label: "Day", value: "day" } | |
| ]} | |
| value={selectedView} | |
| onValueChange={(value) => setSelectedView(value as "month" | "week" | "day")} | |
| placeholder="View" | |
| className="w-[120px]" | |
| /> | |
| <Dialog> | |
| <DialogTrigger asChild> | |
| <Button size="sm" variant="outline"> | |
| <Filter className="h-4 w-4 mr-2" /> | |
| Filter | |
| </Button> | |
| </DialogTrigger> | |
| <DialogContent> | |
| <DialogHeader> | |
| <DialogTitle>Filter Events</DialogTitle> | |
| <DialogDescription> | |
| Show events by type | |
| </DialogDescription> | |
| </DialogHeader> | |
| <div className="grid gap-4 py-4"> | |
| <Dropdown | |
| options={[ | |
| { label: "All Events", value: "all" }, | |
| { label: "Meetings", value: "meeting" }, | |
| { label: "Deadlines", value: "deadline" }, | |
| { label: "Reminders", value: "reminder" }, | |
| { label: "Tasks", value: "task" } | |
| ]} | |
| value={filter} | |
| onValueChange={(value) => setFilter(value as EventType | "all")} | |
| placeholder="Filter by type" | |
| /> | |
| </div> | |
| <DialogFooter> | |
| <Button onClick={() => setFilter("all")}>Reset Filters</Button> | |
| </DialogFooter> | |
| </DialogContent> | |
| </Dialog> | |
| <Dialog open={showAddEvent} onOpenChange={setShowAddEvent}> | |
| <DialogTrigger asChild> | |
| <Button size="sm"> | |
| <CalendarPlus className="h-4 w-4 mr-2" /> | |
| Add Event | |
| </Button> | |
| </DialogTrigger> | |
| <DialogContent> | |
| <DialogHeader> | |
| <DialogTitle>Add New Event</DialogTitle> | |
| <DialogDescription> | |
| Create a new event for your calendar | |
| </DialogDescription> | |
| </DialogHeader> | |
| <div className="grid gap-4 py-4"> | |
| <div className="grid gap-2"> | |
| <Label htmlFor="title">Title</Label> | |
| <Input | |
| id="title" | |
| value={newEvent.title} | |
| onChange={(e) => setNewEvent({...newEvent, title: e.target.value})} | |
| placeholder="Event title" | |
| /> | |
| </div> | |
| <div className="grid gap-2"> | |
| <Label>Date</Label> | |
| <CalendarUI | |
| selected={newEvent.date} | |
| onSelect={(date) => date && setNewEvent({...newEvent, date})} | |
| className="p-3 pointer-events-auto" | |
| /> | |
| </div> | |
| <div className="grid gap-2"> | |
| <Label htmlFor="type">Event Type</Label> | |
| <Dropdown | |
| options={[ | |
| { label: "Meeting", value: "meeting" }, | |
| { label: "Deadline", value: "deadline" }, | |
| { label: "Reminder", value: "reminder" }, | |
| { label: "Task", value: "task" } | |
| ]} | |
| value={newEvent.type} | |
| onValueChange={(value) => setNewEvent({...newEvent, type: value as EventType})} | |
| placeholder="Select type" | |
| /> | |
| </div> | |
| <div className="grid gap-2"> | |
| <Label htmlFor="priority">Priority</Label> | |
| <Dropdown | |
| options={[ | |
| { label: "Low", value: "low" }, | |
| { label: "Medium", value: "medium" }, | |
| { label: "High", value: "high" } | |
| ]} | |
| value={newEvent.priority} | |
| onValueChange={(value) => setNewEvent({...newEvent, priority: value as "low" | "medium" | "high"})} | |
| placeholder="Select priority" | |
| /> | |
| </div> | |
| <div className="grid gap-2"> | |
| <Label htmlFor="description">Description</Label> | |
| <Input | |
| id="description" | |
| value={newEvent.description} | |
| onChange={(e) => setNewEvent({...newEvent, description: e.target.value})} | |
| placeholder="Event description" | |
| /> | |
| </div> | |
| </div> | |
| <DialogFooter> | |
| <Button variant="outline" onClick={() => setShowAddEvent(false)}>Cancel</Button> | |
| <Button onClick={handleAddEvent}>Add Event</Button> | |
| </DialogFooter> | |
| </DialogContent> | |
| </Dialog> | |
| </div> | |
| </div> | |
| <CardDescription> | |
| {format(date, "MMMM yyyy")} | |
| </CardDescription> | |
| </CardHeader> | |
| <CardContent> | |
| <CalendarUI | |
| selected={date} | |
| onSelect={(newDate) => newDate && setDate(newDate)} | |
| className="p-3 pointer-events-auto" | |
| month={startOfMonth(date)} | |
| /> | |
| </CardContent> | |
| </Card> | |
| </div> | |
| <div className="w-full lg:w-2/5"> | |
| <Card> | |
| <CardHeader> | |
| <CardTitle> | |
| Events for {format(date, "MMMM d, yyyy")} | |
| </CardTitle> | |
| <CardDescription> | |
| {eventsForSelectedDate.length} {eventsForSelectedDate.length === 1 ? "event" : "events"} scheduled | |
| </CardDescription> | |
| </CardHeader> | |
| <CardContent> | |
| {eventsForSelectedDate.length > 0 ? ( | |
| <div className="space-y-4"> | |
| {eventsForSelectedDate.map((event) => ( | |
| <div | |
| key={event.id} | |
| className="flex items-start p-3 rounded-lg border border-border" | |
| > | |
| <div className="flex-1"> | |
| <div className="flex items-center gap-2"> | |
| <span className={getPriorityColor(event.priority)}> | |
| {getEventTypeIcon(event.type)} | |
| </span> | |
| <h3 className="font-medium">{event.title}</h3> | |
| </div> | |
| <div className="text-sm text-muted-foreground mt-1"> | |
| {event.description} | |
| </div> | |
| {event.assignees && ( | |
| <div className="text-xs text-muted-foreground mt-2 flex items-center gap-1"> | |
| <Users className="h-3 w-3" /> | |
| {event.assignees.join(", ")} | |
| </div> | |
| )} | |
| </div> | |
| <div className={`text-xs font-medium ${getPriorityColor(event.priority)}`}> | |
| {event.priority} | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| ) : ( | |
| <div className="flex flex-col items-center justify-center py-8 text-center"> | |
| <CalendarRange className="h-10 w-10 text-muted-foreground mb-2" /> | |
| <p className="text-muted-foreground">No events scheduled for this day</p> | |
| <Button | |
| variant="link" | |
| className="mt-2" | |
| onClick={() => setShowAddEvent(true)} | |
| > | |
| Add an event | |
| </Button> | |
| </div> | |
| )} | |
| </CardContent> | |
| </Card> | |
| </div> | |
| </div> | |
| </div> | |
| </Layout> | |
| ); | |
| }; | |
| export default CalendarPage; | |