@@ -22,6 +22,7 @@ export default function CommunityCalendar() {
2222 const [ showEventModal , setShowEventModal ] = useState ( false ) ;
2323 const [ overflowDate , setOverflowDate ] = useState ( null ) ;
2424 const popupRef = useRef ( null ) ;
25+ const [ calendarView , setCalendarView ] = useState ( 'month' ) ;
2526
2627 const currentDate = new Date ( ) ;
2728 const currentMonth = currentDate . getMonth ( ) ;
@@ -192,6 +193,106 @@ export default function CommunityCalendar() {
192193 'Full Event' : 'statusFull' ,
193194 } ;
194195
196+ function WeeklyTimeGrid ( { events, selectedDate, onEventClick, darkMode } ) {
197+ const hours = Array . from ( { length : 24 } , ( _ , i ) => i ) ;
198+
199+ const startOfWeek = useMemo ( ( ) => {
200+ const d = new Date ( selectedDate ) ;
201+ d . setDate ( d . getDate ( ) - d . getDay ( ) ) ;
202+ return d ;
203+ } , [ selectedDate ] ) ;
204+
205+ const weekDays = useMemo ( ( ) => {
206+ return Array . from ( { length : 7 } , ( _ , i ) => {
207+ const day = new Date ( startOfWeek ) ;
208+ day . setDate ( startOfWeek . getDate ( ) + i ) ;
209+ return day ;
210+ } ) ;
211+ } , [ startOfWeek ] ) ;
212+
213+ return (
214+ < div
215+ className = { `${ styles . weekGridContainer } ${ darkMode ? styles . weekGridContainerDark : '' } ` }
216+ >
217+ < div className = { `${ styles . weekGridHeader } ${ darkMode ? styles . weekGridHeaderDark : '' } ` } >
218+ < div className = { styles . timeGutter } />
219+ { weekDays . map ( date => (
220+ < div key = { date . toString ( ) } className = { styles . dayColumnHeader } >
221+ < div className = { `${ styles . dayLabel } ${ darkMode ? styles . dayLabelDark : '' } ` } >
222+ { date . toLocaleDateString ( 'en-US' , { weekday : 'short' } ) }
223+ </ div >
224+ < div className = { `${ styles . dateLabel } ${ darkMode ? styles . dateLabelDark : '' } ` } >
225+ { date . getDate ( ) }
226+ </ div >
227+ </ div >
228+ ) ) }
229+ </ div >
230+
231+ < div className = { styles . weekGridBody } >
232+ { hours . map ( hour => (
233+ < div key = { hour } className = { `${ styles . hourRow } ${ darkMode ? styles . hourRowDark : '' } ` } >
234+ < div className = { `${ styles . timeLabel } ${ darkMode ? styles . timeLabelDark : '' } ` } >
235+ { hour === 0
236+ ? '12 AM'
237+ : hour > 12
238+ ? `${ hour - 12 } PM`
239+ : hour === 12
240+ ? '12 PM'
241+ : `${ hour } AM` }
242+ </ div >
243+
244+ { weekDays . map ( date => {
245+ const cellEvents = events . filter ( e => {
246+ const eventDate = new Date ( e . date ) ;
247+ const [ hStr ] = e . time . split ( ':' ) ;
248+ let h = parseInt ( hStr , 10 ) ;
249+ const isPM = e . time . toLowerCase ( ) . includes ( 'pm' ) ;
250+ const isAM = e . time . toLowerCase ( ) . includes ( 'am' ) ;
251+ if ( isPM && h !== 12 ) h += 12 ;
252+ if ( isAM && h === 12 ) h = 0 ;
253+
254+ return eventDate . toDateString ( ) === date . toDateString ( ) && h === hour ;
255+ } ) ;
256+
257+ return (
258+ < div
259+ key = { date . toString ( ) }
260+ className = { `${ styles . gridCell } ${ darkMode ? styles . gridCellDark : '' } ` }
261+ >
262+ { cellEvents . map ( ev => (
263+ < button
264+ key = { ev . id }
265+ type = "button"
266+ className = { `${ styles . gridEvent } ${ darkMode ? styles . gridEventDark : '' } ` }
267+ onClick = { ( ) => onEventClick ( ev ) }
268+ aria-label = { `Open event ${ ev . title } at ${ ev . time } ` }
269+ >
270+ < div
271+ className = { `${ styles . gridEventTime } ${
272+ darkMode ? styles . gridEventTimeDark : ''
273+ } `}
274+ >
275+ { ev . time }
276+ </ div >
277+ < div
278+ className = { `${ styles . gridEventTitle } ${
279+ darkMode ? styles . gridEventTitleDark : ''
280+ } `}
281+ >
282+ { ev . title }
283+ </ div >
284+ </ button >
285+ ) ) }
286+ </ div >
287+ ) ;
288+ } ) }
289+ </ div >
290+ ) ) }
291+ </ div >
292+ </ div >
293+ ) ;
294+ }
295+
195296 // Render event tiles
196297 const tileContent = useCallback (
197298 ( { date, view } ) => {
@@ -211,20 +312,24 @@ export default function CommunityCalendar() {
211312 key = { e . id }
212313 type = "button"
213314 className = { `${ styles . eventItem } ${ styles [ statusKey ] || '' } ` }
214- onClick = { ( ) => handleEventClick ( e ) }
315+ onClick = { e_obj => {
316+ e_obj . stopPropagation ( ) ;
317+ handleEventClick ( e ) ;
318+ } }
215319 title = { e . title }
216320 >
217321 { e . title }
218322 </ button >
219323 ) ;
220324 } ) }
221-
222325 { hiddenCount > 0 && (
223326 < button
224327 type = "button"
225328 className = { styles . moreEvents }
226- onClick = { ( ) => setOverflowDate ( date ) }
227- title = "View all events"
329+ onClick = { e_obj => {
330+ e_obj . stopPropagation ( ) ;
331+ setOverflowDate ( date ) ;
332+ } }
228333 >
229334 +{ hiddenCount } more
230335 </ button >
@@ -345,6 +450,16 @@ export default function CommunityCalendar() {
345450 < header className = { calendarClasses . header } >
346451 < h1 > Community Calendar</ h1 >
347452 < div className = { calendarClasses . filters } >
453+ < select
454+ value = { calendarView }
455+ onChange = { e => setCalendarView ( e . target . value ) }
456+ className = { styles . viewSelector }
457+ >
458+ < option value = "month" > Day View (Month)</ option >
459+ < option value = "week" > Week View (Time Grid)</ option >
460+ < option value = "year" > Month View (Year)</ option >
461+ < option value = "decade" > Year View (Decade)</ option >
462+ </ select >
348463 < select value = { filter . type } onChange = { handleFilterChange ( 'type' ) } >
349464 < option value = "all" > All Types</ option >
350465 { uniqueFilterValues . types . map ( t => (
@@ -378,13 +493,25 @@ export default function CommunityCalendar() {
378493 />
379494 </ div >
380495 < div className = { calendarClasses . calendarSection } >
381- < ReactCalendar
382- className = { calendarClasses . reactCalendar }
383- tileContent = { tileContent }
384- tileClassName = { tileClassName }
385- onClickDay = { handleDateSelect }
386- value = { selectedDate }
387- />
496+ { calendarView === 'week' ? (
497+ < WeeklyTimeGrid
498+ events = { filteredEvents }
499+ selectedDate = { selectedDate }
500+ onEventClick = { handleEventClick }
501+ darkMode = { darkMode }
502+ />
503+ ) : (
504+ < ReactCalendar
505+ className = { calendarClasses . reactCalendar }
506+ tileContent = { tileContent }
507+ tileClassName = { tileClassName }
508+ onClickDay = { handleDateSelect }
509+ value = { selectedDate }
510+ view = { calendarView }
511+ onViewChange = { ( { view } ) => setCalendarView ( view ) }
512+ />
513+ ) }
514+
388515 < section
389516 className = { `${ styles . selectedDatePanel } ${
390517 darkMode ? styles . selectedDatePanelDarkMode : ''
0 commit comments