From 8b131d7855f9c8cd4e5265ec684ac6c5b9bfbb3f Mon Sep 17 00:00:00 2001 From: Bruno Bernardino Date: Sun, 17 Mar 2024 14:32:47 +0000 Subject: [PATCH] Dynamically render calendar with mock data --- islands/calendar/MainCalendar.tsx | 251 +++++++++++------------------- lib/utils.ts | 37 +++++ routes/calendar.tsx | 72 ++++++++- 3 files changed, 200 insertions(+), 160 deletions(-) diff --git a/islands/calendar/MainCalendar.tsx b/islands/calendar/MainCalendar.tsx index ed73a21..b8bd2b4 100644 --- a/islands/calendar/MainCalendar.tsx +++ b/islands/calendar/MainCalendar.tsx @@ -2,7 +2,7 @@ import { useSignal } from '@preact/signals'; import { useEffect } from 'preact/hooks'; import { Calendar, CalendarEvent } from '/lib/types.ts'; -import { baseUrl, capitalizeWord } from '/lib/utils.ts'; +import { baseUrl, capitalizeWord, getWeeksForMonth } from '/lib/utils.ts'; // import { RequestBody as GetRequestBody, ResponseBody as GetResponseBody } from '/routes/api/contacts/get.tsx'; // import { RequestBody as AddRequestBody, ResponseBody as AddResponseBody } from '/routes/api/contacts/add.tsx'; // import { RequestBody as DeleteRequestBody, ResponseBody as DeleteResponseBody } from '/routes/api/contacts/delete.tsx'; @@ -28,6 +28,8 @@ export default function MainCalendar({ initialCalendars, initialCalendarEvents, const searchTimeout = useSignal>(0); const dateFormat = new Intl.DateTimeFormat('en-GB', { year: 'numeric', month: 'long' }); + const hourFormat = new Intl.DateTimeFormat('en-GB', { hour12: false, hour: '2-digit', minute: '2-digit' }); + const today = new Date().toISOString().substring(0, 10); function onClickAddEvent() { if (isAdding.value) { @@ -113,7 +115,6 @@ export default function MainCalendar({ initialCalendars, initialCalendarEvents, } function onClickChangeStartDate(changeTo: 'previous' | 'next' | 'today') { - const today = new Date().toISOString().substring(0, 10); const previousDay = new Date(new Date(startDate).setUTCDate(new Date(startDate).getUTCDate() - 1)).toISOString() .substring(0, 10); const nextDay = new Date(new Date(startDate).setUTCDate(new Date(startDate).getUTCDate() + 1)).toISOString() @@ -315,6 +316,11 @@ export default function MainCalendar({ initialCalendars, initialCalendarEvents, const visibleCalendars = calendars.value.filter((calendar) => calendar.is_visible); + const visibleCalendarEvents = calendarEvents.value; + + // TODO: Send in / consider user timezone + const weeks = getWeeksForMonth(new Date(startDate)); + return ( <>
@@ -497,7 +503,7 @@ export default function MainCalendar({ initialCalendars, initialCalendarEvents,
-
+
M on @@ -526,165 +532,94 @@ export default function MainCalendar({ initialCalendars, initialCalendarEvents, S un
-
- + {dayEvents.length > 0 + ? ( +
    + {[...dayEvents].slice(0, 2).map((dayEvent) => ( +
  1. + calendar.id === dayEvent.calendar_id) + ?.color || 'bg-gray-700' + }`} + > + +

    + {dayEvent.title} +

    +
    +
  2. + ))} + {dayEvents.length > 2 + ? ( +
  3. + +

    + ...{dayEvents.length - 2} more event{dayEvents.length - 2 === 1 ? '' : 's'} +

    +
    +
  4. + ) + : null} +
+ ) + : null} +
+ ); + }) + )} +
+
diff --git a/lib/utils.ts b/lib/utils.ts index 66126d1..0751a8c 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -628,3 +628,40 @@ export function parseVCardFromTextContents(text: string): Partial[] { export const capitalizeWord = (string: string) => { return `${string.charAt(0).toLocaleUpperCase()}${string.slice(1)}`; }; + +// NOTE: Considers weeks starting Monday, not Sunday +export function getWeeksForMonth(date: Date): { date: Date; isSameMonth: boolean }[][] { + const year = date.getFullYear(); + const month = date.getMonth(); + + const firstOfMonth = new Date(year, month, 1); + const lastOfMonth = new Date(year, month + 1, 0); + + const daysToShow = firstOfMonth.getDay() + (firstOfMonth.getDay() === 0 ? 6 : -1) + lastOfMonth.getDate(); + + const weekCount = Math.ceil(daysToShow / 7); + + const weeks: { date: Date; isSameMonth: boolean }[][] = []; + + const startingDate = new Date(firstOfMonth); + startingDate.setDate( + startingDate.getDate() - Math.abs(firstOfMonth.getDay() === 0 ? 6 : (firstOfMonth.getDay() - 1)), + ); + + for (let weekIndex = 0; weeks.length < weekCount; ++weekIndex) { + for (let dayIndex = 0; dayIndex < 7; ++dayIndex) { + if (!Array.isArray(weeks[weekIndex])) { + weeks[weekIndex] = []; + } + + const weekDayDate = new Date(startingDate); + weekDayDate.setDate(weekDayDate.getDate() + (dayIndex + weekIndex * 7)); + + const isSameMonth = weekDayDate.getMonth() === month; + + weeks[weekIndex].push({ date: weekDayDate, isSameMonth }); + } + } + + return weeks; +} diff --git a/routes/calendar.tsx b/routes/calendar.tsx index 3823984..d47fe45 100644 --- a/routes/calendar.tsx +++ b/routes/calendar.tsx @@ -8,14 +8,82 @@ async function getCalendars(userId: string): Promise setTimeout(() => resolve(true), 1)); - return []; + return [ + { + id: 'family-1', + name: 'Family', + color: 'bg-purple-500', + is_visible: true, + }, + { + id: 'personal-1', + name: 'Personal', + color: 'bg-sky-600', + is_visible: true, + }, + { + id: 'house-chores-1', + name: 'House Chores', + color: 'bg-red-700', + is_visible: true, + }, + ]; } async function getCalendarEvents(userId: string, calendarIds: string[]): Promise { // TODO: Remove this await new Promise((resolve) => setTimeout(() => resolve(true), 1)); - return []; + return [ + { + id: 'event-1', + user_id: userId, + calendar_id: 'family-1', + revision: 'fake-rev', + title: 'Dentist', + start_date: new Date('2024-03-17T14:00:00.000Z'), + end_date: new Date('2024-03-17T15:00:00.000Z'), + is_all_day: false, + status: 'scheduled', + extra: { + visibility: 'default', + }, + updated_at: new Date(), + created_at: new Date(), + }, + { + id: 'event-2', + user_id: userId, + calendar_id: 'personal-1', + revision: 'fake-rev', + title: 'Dermatologist', + start_date: new Date('2024-03-17T16:30:00.000Z'), + end_date: new Date('2024-03-17T17:30:00.000Z'), + is_all_day: false, + status: 'scheduled', + extra: { + visibility: 'default', + }, + updated_at: new Date(), + created_at: new Date(), + }, + { + id: 'event-3', + user_id: userId, + calendar_id: 'house-chores-1', + revision: 'fake-rev', + title: 'Vacuum', + start_date: new Date('2024-03-16T15:00:00.000Z'), + end_date: new Date('2024-03-16T16:00:00.000Z'), + is_all_day: false, + status: 'scheduled', + extra: { + visibility: 'default', + }, + updated_at: new Date(), + created_at: new Date(), + }, + ]; } interface Data {