diff --git a/react-shadcn-starter/src/App.tsx b/react-shadcn-starter/src/App.tsx index 831524c..edbf14f 100644 --- a/react-shadcn-starter/src/App.tsx +++ b/react-shadcn-starter/src/App.tsx @@ -16,6 +16,7 @@ function App() { {/* */} diff --git a/react-shadcn-starter/src/components/calendar.tsx b/react-shadcn-starter/src/components/calendar.tsx index e9e861a..168847b 100644 --- a/react-shadcn-starter/src/components/calendar.tsx +++ b/react-shadcn-starter/src/components/calendar.tsx @@ -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 } from 'lucide-react' +import { Calendar as CalendarIcon, ChevronLeft, ChevronRight, MoreHorizontal, 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' @@ -39,15 +39,111 @@ import { timeOptions, WebDAVConfig } from '@/lib/calendar-data' +import { CircleDataManager, Member } from '@/lib/circle-data' const dataManager = CalendarDataManager.getInstance(); +const circleManager = CircleDataManager.getInstance(); interface CalendarProps { webdavConfig: WebDAVConfig; calendarFile: string; + circleFile: string; } -export function OurCalendar({ webdavConfig, calendarFile }: CalendarProps) { +function AttendeeSelector({ selectedAttendees, onAttendeeChange }: { + selectedAttendees: string[]; + onAttendeeChange: (attendees: string[]) => void; +}) { + const [searchTerm, setSearchTerm] = React.useState(''); + const [members, setMembers] = React.useState([]); + const [suggestions, setSuggestions] = React.useState([]); + + React.useEffect(() => { + const fetchMembers = async () => { + try { + const fetchedMembers = await circleManager.fetchMembers(); + setMembers(fetchedMembers); + } catch (error) { + console.error('Error fetching members:', error); + } + }; + fetchMembers(); + }, []); + + React.useEffect(() => { + if (searchTerm.trim() === '') { + setSuggestions([]); + return; + } + + const filtered = members.filter(member => + member.name.toLowerCase().includes(searchTerm.toLowerCase()) && + !selectedAttendees.includes(member.email) + ); + setSuggestions(filtered); + }, [searchTerm, members, selectedAttendees]); + + const handleAddAttendee = (member: Member) => { + onAttendeeChange([...selectedAttendees, member.email]); + setSearchTerm(''); + setSuggestions([]); + }; + + const handleRemoveAttendee = (email: string) => { + onAttendeeChange(selectedAttendees.filter(e => e !== email)); + }; + + const getMemberName = (email: string) => { + const member = members.find(m => m.email === email); + return member ? member.name : email; + }; + + return ( +
+
+ {selectedAttendees.map(email => ( +
+ {getMemberName(email)} + +
+ ))} +
+
+ setSearchTerm(e.target.value)} + placeholder="Type to search members..." + className="dark:bg-gray-700 dark:text-white dark:border-gray-600" + /> + {suggestions.length > 0 && ( +
+ {suggestions.map(member => ( +
handleAddAttendee(member)} + > +
{member.name}
+
{member.email}
+
+ ))} +
+ )} +
+
+ ); +} + +export function OurCalendar({ webdavConfig, calendarFile, circleFile }: CalendarProps) { const [currentDate, setCurrentDate] = React.useState(new Date()) const [events, setEvents] = React.useState([]) const [selectedEvent, setSelectedEvent] = React.useState(null) @@ -60,7 +156,8 @@ export function OurCalendar({ webdavConfig, calendarFile }: CalendarProps) { React.useEffect(() => { dataManager.setConfig(webdavConfig, calendarFile); - }, [webdavConfig, calendarFile]); + circleManager.setConfig(webdavConfig, circleFile); + }, [webdavConfig, calendarFile, circleFile]); const startDate = startOfWeek(startOfMonth(currentDate)) const endDate = endOfWeek(endOfMonth(currentDate)) @@ -90,7 +187,6 @@ export function OurCalendar({ webdavConfig, calendarFile }: CalendarProps) { throw new Error(`Failed to fetch calendar data: ${response.status} ${response.statusText}`) } else { const data = await response.json() - // Ensure all events have the new fields const updatedEvents = data.events.map((event: Event) => ({ ...event, attendees: event.attendees || [], @@ -112,11 +208,10 @@ export function OurCalendar({ webdavConfig, calendarFile }: CalendarProps) { } const checkForUpdates = async () => { - if (selectedEvent) return; // Skip update check while editing + if (selectedEvent) return; try { const fetchedEvents = await dataManager.fetchEvents(); - // Ensure all events have the new fields const updatedEvents = fetchedEvents.map(event => ({ ...event, attendees: event.attendees || [], @@ -271,7 +366,7 @@ export function OurCalendar({ webdavConfig, calendarFile }: CalendarProps) { {...provided.droppableProps} className={cn( "border rounded-lg p-2 min-h-[100px] dark:border-gray-600", - !isSameMonth(day, currentDate) && "bg-gray-100 dark:bg-gray-700", + !isSameMonth(day, currentDate) && "bg-gray-100 dark:bg-gray-700", isSameMonth(day, currentDate) && "dark:bg-gray-800" )} > @@ -333,7 +428,7 @@ function EventForm({ onSubmit, initialData, isDarkMode }: { onSubmit: (event: Ev 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(initialData?.attendees?.join('\n') || '') + const [attendees, setAttendees] = React.useState(initialData?.attendees || []) const [isFullDay, setIsFullDay] = React.useState(initialData?.isFullDay || false) const handleSubmit = (e: React.FormEvent) => { @@ -349,7 +444,7 @@ function EventForm({ onSubmit, initialData, isDarkMode }: { onSubmit: (event: Ev category, color, content, - attendees: attendees.split('\n').filter(email => email.trim() !== ''), + attendees, isFullDay }) } @@ -453,14 +548,10 @@ function EventForm({ onSubmit, initialData, isDarkMode }: { onSubmit: (event: Ev
- -