Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const CalculatorWidget = React.lazy(() => import('./components/CalculatorWidget.
const TaskComposerPanel = React.lazy(() => import('./components/TaskComposerPanel.jsx'));
const FriendsPanel = React.lazy(() => import('./components/FriendsPanel.jsx'));
const Calendar = React.lazy(() => import('./components/Calendar.jsx'));
const CalendarSummaryCard = React.lazy(() => import('./components/CalendarSummaryCard.jsx'));

const DynamicColorPlugin = registerPlugin('DynamicColor');

Expand Down Expand Up @@ -1641,6 +1642,11 @@ function App() {
<ProductivityDashboard tasks={tasks} />
</Suspense>
</ErrorBoundary>
<ErrorBoundary>
<Suspense fallback={<LoadingFallback />}>
<CalendarSummaryCard onOpenCalendar={() => setShowCalendar(true)} />
</Suspense>
</ErrorBoundary>
</div>
<ErrorBoundary>
<Suspense fallback={<LoadingFallback />}>
Expand Down Expand Up @@ -1742,11 +1748,18 @@ function App() {
/>
</Suspense>
</ErrorBoundary>
<ErrorBoundary>
<Suspense fallback={<LoadingFallback />}>
<ProductivityDashboard tasks={tasks} />
</Suspense>
</ErrorBoundary>
<div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem', width: '100%' }}>
<ErrorBoundary>
<Suspense fallback={<LoadingFallback />}>
<ProductivityDashboard tasks={tasks} />
</Suspense>
</ErrorBoundary>
<ErrorBoundary>
<Suspense fallback={<LoadingFallback />}>
<CalendarSummaryCard onOpenCalendar={() => setShowCalendar(true)} />
</Suspense>
</ErrorBoundary>
</div>
</div>
<ErrorBoundary>
<div>
Expand Down
18 changes: 11 additions & 7 deletions frontend/src/components/Calendar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const COLOR_PRESETS = [
{ name: 'Pink', value: '#ec4899' }
];

const getDDayText = (targetDate) => {
export const getDDayText = (targetDate) => {
if (!targetDate) return '';
const today = new Date();
today.setHours(0, 0, 0, 0);
Expand All @@ -32,7 +32,7 @@ const getDDayText = (targetDate) => {
return `D+${Math.abs(diff)}`;
};

const formatDateStr = (d) => {
export const formatDateStr = (d) => {
const yr = d.getFullYear();
const mo = String(d.getMonth() + 1).padStart(2, '0');
const dy = String(d.getDate()).padStart(2, '0');
Expand Down Expand Up @@ -97,7 +97,7 @@ const getRruleLabel = (rule) => {
// Splits a stored timestamp into an editable date ('YYYY-MM-DD') + time ('HH:MM').
// Handles naive values ('2026-09-01', '2026-09-01T17:15') and full Google ISO
// values ('2026-09-01T17:15:00+05:30') — keeps the wall-clock time as-is.
const splitDateTime = (value) => {
export const splitDateTime = (value) => {
if (!value) return { date: '', time: '' };
const s = String(value);
const m = s.match(/^(\d{4}-\d{2}-\d{2})(?:T(\d{2}):(\d{2})(?::\d{2})?.*)?$/);
Expand All @@ -106,7 +106,7 @@ const splitDateTime = (value) => {
};

// "5:15 PM" style wall-clock display for the detail panel.
const getEventTime = (ev) => {
export const getEventTime = (ev) => {
if (!ev || ev.is_all_day || !ev.event_date) return '';
const { time } = splitDateTime(ev.event_date);
if (!time) return '';
Expand Down Expand Up @@ -313,7 +313,7 @@ const CalendarCell = memo(({ dateStr, day, currentMonth, isToday, isSelected, da
// Does this event occupy the given calendar day (YYYY-MM-DD)?
// Handles single/multi-day spans AND recurring events (DAILY/WEEKLY/MONTHLY/YEARLY
// with optional INTERVAL). For recurring events the optional end_date caps the series.
const isEventOnDate = (ev, dateStr) => {
export const isEventOnDate = (ev, dateStr) => {
const startStr = ev.event_date ? String(ev.event_date).slice(0, 10) : '';
if (!startStr) return false;
// A single occurrence was "deleted" via the exclude endpoint — skip that date.
Expand Down Expand Up @@ -761,8 +761,12 @@ const Calendar = () => {
const res = await apiFetch('/api/calendar/events');
if (res && res.ok) {
const data = await res.json();
if (Array.isArray(data)) setEvents(data);
else setFetchError(true);
if (Array.isArray(data)) {
setEvents(data);
window.dispatchEvent(new CustomEvent('snowball-calendar-updated', { detail: data }));
} else {
setFetchError(true);
}
} else {
setFetchError(true);
}
Expand Down
Loading
Loading