...
This commit is contained in:
parent
af105583f1
commit
8a98ada8f3
@ -1,6 +1,6 @@
|
||||
import * as React from 'react'
|
||||
import { addDays, format, startOfWeek, endOfWeek, startOfMonth, endOfMonth, eachDayOfInterval, isSameMonth, isSameDay, parseISO } from 'date-fns'
|
||||
import { Calendar as CalendarIcon, ChevronLeft, ChevronRight, MoreHorizontal, Plus, Moon, Sun, Clock, X } from 'lucide-react'
|
||||
import { ChevronLeft, ChevronRight, Plus, Moon, Sun, Clock, X } from 'lucide-react'
|
||||
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
@ -8,8 +8,6 @@ import { v4 as uuidv4 } from 'uuid'
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
|
||||
import { Calendar } from "@/components/ui/calendar"
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@ -21,16 +19,8 @@ import {
|
||||
} from "@/components/ui/dialog"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Textarea } from "@/components/ui/textarea"
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select"
|
||||
import { useToast } from "@/components/ui/use-toast"
|
||||
import { Switch } from "@/components/ui/switch"
|
||||
import { useToast } from "@/components/ui/use-toast"
|
||||
import {
|
||||
Event,
|
||||
categories,
|
||||
@ -40,6 +30,7 @@ import {
|
||||
WebDAVConfig
|
||||
} from '@/lib/calendar-data'
|
||||
import { CircleDataManager, Member } from '@/lib/circle-data'
|
||||
import { EventForm } from './eventform'
|
||||
|
||||
const dataManager = CalendarDataManager.getInstance();
|
||||
const circleManager = CircleDataManager.getInstance();
|
||||
@ -50,7 +41,7 @@ interface CalendarProps {
|
||||
circleFile: string;
|
||||
}
|
||||
|
||||
function AttendeeSelector({ selectedAttendees, onAttendeeChange }: {
|
||||
export function AttendeeSelector({ selectedAttendees, onAttendeeChange }: {
|
||||
selectedAttendees: string[];
|
||||
onAttendeeChange: (attendees: string[]) => void;
|
||||
}) {
|
||||
@ -421,154 +412,6 @@ export function OurCalendar({ webdavConfig, calendarFile, circleFile }: Calendar
|
||||
)
|
||||
}
|
||||
|
||||
function EventForm({ onSubmit, initialData, isDarkMode }: { onSubmit: (event: Event) => void, initialData?: Event, isDarkMode: boolean }) {
|
||||
const [title, setTitle] = React.useState(initialData?.title || '')
|
||||
const [date, setDate] = React.useState<Date | undefined>(initialData ? parseISO(initialData.date) : undefined)
|
||||
const [time, setTime] = React.useState(initialData?.time || '10:00')
|
||||
const [duration, setDuration] = React.useState(initialData?.duration?.toString() || '60')
|
||||
const [category, setCategory] = React.useState(initialData?.category || '')
|
||||
const [content, setContent] = React.useState(initialData?.content || '')
|
||||
const [attendees, setAttendees] = React.useState<string[]>(initialData?.attendees || [])
|
||||
const [isFullDay, setIsFullDay] = React.useState(initialData?.isFullDay || false)
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
if (title && date && category) {
|
||||
const color = categories.find(c => c.name === category)?.color || 'bg-gray-500 dark:bg-gray-700'
|
||||
onSubmit({
|
||||
id: initialData?.id || uuidv4(),
|
||||
title,
|
||||
date: date.toISOString().split('T')[0],
|
||||
time: isFullDay ? '00:00' : time,
|
||||
duration: parseInt(duration),
|
||||
category,
|
||||
color,
|
||||
content,
|
||||
attendees,
|
||||
isFullDay
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<Label htmlFor="title" className="dark:text-white">Title</Label>
|
||||
<Input
|
||||
id="title"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
required
|
||||
className="dark:bg-gray-700 dark:text-white dark:border-gray-600"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="date" className="dark:text-white">Date</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant={"outline"}
|
||||
className={cn(
|
||||
"w-full justify-start text-left font-normal",
|
||||
!date && "text-muted-foreground",
|
||||
"dark:bg-gray-700 dark:text-white dark:border-gray-600"
|
||||
)}
|
||||
>
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{date ? format(date, "PPP") : <span>Pick a date</span>}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={date}
|
||||
onSelect={setDate}
|
||||
initialFocus
|
||||
className={isDarkMode ? "dark" : ""}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch
|
||||
id="isFullDay"
|
||||
checked={isFullDay}
|
||||
onCheckedChange={setIsFullDay}
|
||||
className="dark:bg-gray-600"
|
||||
/>
|
||||
<Label htmlFor="isFullDay" className="dark:text-white">Full Day Event</Label>
|
||||
</div>
|
||||
{!isFullDay && (
|
||||
<div>
|
||||
<Label htmlFor="time" className="dark:text-white">Time</Label>
|
||||
<Select value={time} onValueChange={setTime}>
|
||||
<SelectTrigger className="dark:bg-gray-700 dark:text-white dark:border-gray-600">
|
||||
<SelectValue placeholder="Select a time" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="dark:bg-gray-800 dark:text-white max-h-[200px]">
|
||||
{timeOptions.map((timeOption) => (
|
||||
<SelectItem key={timeOption} value={timeOption}>
|
||||
{timeOption}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
)}
|
||||
{!isFullDay && (
|
||||
<div>
|
||||
<Label htmlFor="duration" className="dark:text-white">Duration (minutes)</Label>
|
||||
<Select value={duration} onValueChange={setDuration}>
|
||||
<SelectTrigger className="dark:bg-gray-700 dark:text-white dark:border-gray-600">
|
||||
<SelectValue placeholder="Select duration" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="dark:bg-gray-800 dark:text-white">
|
||||
{[15, 30, 45, 60, 90, 120, 180, 240].map((mins) => (
|
||||
<SelectItem key={mins} value={mins.toString()}>
|
||||
{dataManager.formatDuration(mins)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<Label htmlFor="category" className="dark:text-white">Category</Label>
|
||||
<Select value={category} onValueChange={setCategory}>
|
||||
<SelectTrigger className="dark:bg-gray-700 dark:text-white dark:border-gray-600">
|
||||
<SelectValue placeholder="Select a category" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="dark:bg-gray-800 dark:text-white">
|
||||
{categories.map((cat) => (
|
||||
<SelectItem key={cat.name} value={cat.name}>
|
||||
{cat.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="attendees" className="dark:text-white">Attendees</Label>
|
||||
<AttendeeSelector
|
||||
selectedAttendees={attendees}
|
||||
onAttendeeChange={setAttendees}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="content" className="dark:text-white">Content (Markdown)</Label>
|
||||
<Textarea
|
||||
id="content"
|
||||
value={content}
|
||||
onChange={(e) => setContent(e.target.value)}
|
||||
rows={5}
|
||||
className="dark:bg-gray-700 dark:text-white dark:border-gray-600"
|
||||
/>
|
||||
</div>
|
||||
<Button type="submit" className="dark:bg-blue-600 dark:text-white">{initialData ? 'Update' : 'Add'} Event</Button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
function EventDetailsDialog({ event, onClose, onEdit, onDelete, isDarkMode }: {
|
||||
event: Event
|
||||
onClose: () => void
|
||||
|
182
react-shadcn-starter/src/components/eventform.tsx
Normal file
182
react-shadcn-starter/src/components/eventform.tsx
Normal file
@ -0,0 +1,182 @@
|
||||
import * as React from 'react'
|
||||
import { format, parseISO } from 'date-fns'
|
||||
import { CalendarIcon } from 'lucide-react'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Calendar } from "@/components/ui/calendar"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Textarea } from "@/components/ui/textarea"
|
||||
import { Switch } from "@/components/ui/switch"
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select"
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover"
|
||||
import { Event, categories, timeOptions, CalendarDataManager } from '@/lib/calendar-data'
|
||||
import { AttendeeSelector } from './calendar'
|
||||
|
||||
const dataManager = CalendarDataManager.getInstance();
|
||||
|
||||
interface EventFormProps {
|
||||
onSubmit: (event: Event) => void;
|
||||
initialData?: Event;
|
||||
isDarkMode: boolean;
|
||||
}
|
||||
|
||||
export function EventForm({ onSubmit, initialData, isDarkMode }: EventFormProps) {
|
||||
const [title, setTitle] = React.useState(initialData?.title || '')
|
||||
const [date, setDate] = React.useState<Date>(initialData ? parseISO(initialData.date) : new Date())
|
||||
const [time, setTime] = React.useState(initialData?.time || '10:00')
|
||||
const [duration, setDuration] = React.useState(initialData?.duration?.toString() || '60')
|
||||
const [category, setCategory] = React.useState(initialData?.category || '')
|
||||
const [content, setContent] = React.useState(initialData?.content || '')
|
||||
const [attendees, setAttendees] = React.useState<string[]>(initialData?.attendees || [])
|
||||
const [isFullDay, setIsFullDay] = React.useState(initialData?.isFullDay || false)
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
if (title && date && category) {
|
||||
const color = categories.find(c => c.name === category)?.color || 'bg-gray-500 dark:bg-gray-700'
|
||||
onSubmit({
|
||||
id: initialData?.id || uuidv4(),
|
||||
title,
|
||||
date: date.toISOString().split('T')[0],
|
||||
time: isFullDay ? '00:00' : time,
|
||||
duration: parseInt(duration),
|
||||
category,
|
||||
color,
|
||||
content,
|
||||
attendees,
|
||||
isFullDay
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div>
|
||||
<Label htmlFor="title" className="dark:text-white">Title</Label>
|
||||
<Input
|
||||
id="title"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
required
|
||||
className="dark:bg-gray-700 dark:text-white dark:border-gray-600"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="date" className="dark:text-white">Date</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant={"outline"}
|
||||
className={cn(
|
||||
"w-full justify-start text-left font-normal",
|
||||
!date && "text-muted-foreground",
|
||||
"dark:bg-gray-700 dark:text-white dark:border-gray-600"
|
||||
)}
|
||||
>
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{date ? format(date, "PPP") : <span>Pick a date</span>}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={date}
|
||||
onSelect={(newDate) => newDate && setDate(newDate)}
|
||||
initialFocus
|
||||
className={isDarkMode ? "dark" : ""}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Switch
|
||||
id="isFullDay"
|
||||
checked={isFullDay}
|
||||
onCheckedChange={setIsFullDay}
|
||||
className="dark:bg-gray-600"
|
||||
/>
|
||||
<Label htmlFor="isFullDay" className="dark:text-white">Full Day Event</Label>
|
||||
</div>
|
||||
{!isFullDay && (
|
||||
<div>
|
||||
<Label htmlFor="time" className="dark:text-white">Time</Label>
|
||||
<Select value={time} onValueChange={setTime}>
|
||||
<SelectTrigger className="dark:bg-gray-700 dark:text-white dark:border-gray-600">
|
||||
<SelectValue placeholder="Select a time" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="dark:bg-gray-800 dark:text-white max-h-[200px]">
|
||||
{timeOptions.map((timeOption) => (
|
||||
<SelectItem key={timeOption} value={timeOption}>
|
||||
{timeOption}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
)}
|
||||
{!isFullDay && (
|
||||
<div>
|
||||
<Label htmlFor="duration" className="dark:text-white">Duration (minutes)</Label>
|
||||
<Select value={duration} onValueChange={setDuration}>
|
||||
<SelectTrigger className="dark:bg-gray-700 dark:text-white dark:border-gray-600">
|
||||
<SelectValue placeholder="Select duration" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="dark:bg-gray-800 dark:text-white">
|
||||
{[15, 30, 45, 60, 90, 120, 180, 240].map((mins) => (
|
||||
<SelectItem key={mins} value={mins.toString()}>
|
||||
{dataManager.formatDuration(mins)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<Label htmlFor="category" className="dark:text-white">Category</Label>
|
||||
<Select value={category} onValueChange={setCategory}>
|
||||
<SelectTrigger className="dark:bg-gray-700 dark:text-white dark:border-gray-600">
|
||||
<SelectValue placeholder="Select a category" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="dark:bg-gray-800 dark:text-white">
|
||||
{categories.map((cat) => (
|
||||
<SelectItem key={cat.name} value={cat.name}>
|
||||
{cat.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="attendees" className="dark:text-white">Attendees</Label>
|
||||
<AttendeeSelector
|
||||
selectedAttendees={attendees}
|
||||
onAttendeeChange={setAttendees}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label htmlFor="content" className="dark:text-white">Content (Markdown)</Label>
|
||||
<Textarea
|
||||
id="content"
|
||||
value={content}
|
||||
onChange={(e) => setContent(e.target.value)}
|
||||
rows={5}
|
||||
className="dark:bg-gray-700 dark:text-white dark:border-gray-600"
|
||||
/>
|
||||
</div>
|
||||
<Button type="submit" className="dark:bg-blue-600 dark:text-white">{initialData ? 'Update' : 'Add'} Event</Button>
|
||||
</form>
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue
Block a user