Skip to content

feat(calendar): unified calendar view with meetings, milestones, and sprints#164

Merged
dimakis merged 4 commits into
mainfrom
feat/calendar-view
Apr 10, 2026
Merged

feat(calendar): unified calendar view with meetings, milestones, and sprints#164
dimakis merged 4 commits into
mainfrom
feat/calendar-view

Conversation

@dimakis
Copy link
Copy Markdown
Owner

@dimakis dimakis commented Apr 10, 2026

Summary

  • Adds /calendar route with a unified calendar view merging Google Calendar events, release milestones (from Smartsheet via calendar_api.py), and sprint boundaries
  • Mobile-first design: single-day scrollable view (default) with week toggle; desktop gets a 7-column grid layout
  • Backend GET /api/calendar endpoint shells out to mgmt/command_center/calendar_api.py with graceful fallback on failure
  • Click-to-expand event cards showing attendees, milestone feature counts, and video call links

New files

File Purpose
server/app.ts (modified) /api/calendar endpoint + execFile async import
frontend/src/hooks/useCalendarData.ts Data fetching hook with date/days params
frontend/src/pages/CalendarView.tsx Page component: EventCard, SprintBar, day grouping, navigation
frontend/src/styles/global.css (modified) Mobile calendar styles (~300 lines)
frontend/src/styles/desktop.css (modified) Desktop 7-column grid overrides
frontend/src/App.tsx (modified) /calendar route behind ProtectedRoute

Test plan

  • 5 server tests: auth guard, default params, date/days params, invalid date, days clamping
  • 5 hook tests: fetch on mount, days param, refetch on date change, error handling, type separation
  • 7 page tests: header/nav rendering, meeting cards, milestone cards, sprint bars, loading state, date navigation, back button
  • Full suite: 827 tests passing
  • Manual: deploy and verify on phone (day view) and desktop (week grid)

🤖 Generated with Claude Code

Copy link
Copy Markdown
Owner Author

@dimakis dimakis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Centaur Review

Found 8 issue(s) (1 critical) (3 warning).

server/app.ts

Calendar feature is well-structured with good test coverage, but the Python script dependency doesn't exist in the repo, and the fetch hook doesn't handle HTTP error responses properly.

  • 🔴 unsafe_assumptions (L675): CALENDAR_SCRIPT references ${BASE_REPO}/command_center/calendar_api.py but this file does not exist in the repository. The route will always fall into the catch branch, returning an error payload. Either the script should be added to the repo, or this dependency should be documented.
  • 🟡 unsafe_assumptions (L694): JSON.parse(stdout) on the Python script output has no validation. Malformed or unexpected output will throw and be caught, but valid JSON with an unexpected shape will be passed straight to the client. Consider validating with a Zod schema (consistent with the project's api-schemas.ts pattern). [fixable]

frontend/src/hooks/useCalendarData.ts

Calendar feature is well-structured with good test coverage, but the Python script dependency doesn't exist in the repo, and the fetch hook doesn't handle HTTP error responses properly.

  • 🟡 bugs (L62): The fetch chain calls r.json() without checking r.ok. A 400/401 response will be parsed as JSON and stored in state — the resulting object (e.g. { error: "..." }) has no events or sprints arrays, so the component would render with undefined data. Should check r.ok and reject on error status codes. [fixable]

frontend/src/pages/__tests__/CalendarView.test.tsx

Calendar feature is well-structured with good test coverage, but the Python script dependency doesn't exist in the repo, and the fetch hook doesn't handle HTTP error responses properly.

  • 🔵 missing_tests: No test for the Day/Week view toggle (viewDays state change). The toggle buttons are rendered but never exercised in tests. Also no test for the 'Today' button click (handleToday). [fixable]
  • 🔵 missing_tests: No test for the expanded state of EventCard — clicking a meeting or milestone card should reveal detail section (.cal-event-detail). This is a key interaction path. [fixable]

frontend/src/pages/CalendarView.tsx

Calendar feature is well-structured with good test coverage, but the Python script dependency doesn't exist in the repo, and the fetch hook doesn't handle HTTP error responses properly.

  • 🟡 bugs (L155): const today = new Date().toISOString().slice(0, 10) is computed once on render outside of state/effect, but handleToday captures it as a closure. If the component stays mounted across midnight, today becomes stale. Minor in practice, but the same value is also used as initial state for baseDate, which is fine. [fixable]
  • 🔵 style: CalendarView is 256 lines with multiple sub-components (EventCard, SprintBar) and utility functions inlined. Consider extracting EventCard and SprintBar into separate component files to match the project convention of short, focused files. [fixable]

frontend/src/styles/global.css

Calendar feature is well-structured with good test coverage, but the Python script dependency doesn't exist in the repo, and the fetch hook doesn't handle HTTP error responses properly.

  • 🔵 style: 308 lines of calendar CSS appended to an already 2700-line global.css. The project has separate style files (e.g. desktop.css). Consider a dedicated calendar.css to keep files manageable. [fixable]

Comment thread server/app.ts
// --- Calendar API ---

const execFileAsync = promisify(execFile);
const CALENDAR_SCRIPT = join(BASE_REPO, 'command_center', 'calendar_api.py');
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 unsafe_assumptions: CALENDAR_SCRIPT references ${BASE_REPO}/command_center/calendar_api.py but this file does not exist in the repository. The route will always fall into the catch branch, returning an error payload. Either the script should be added to the repo, or this dependency should be documented.

Comment thread server/app.ts Outdated
[CALENDAR_SCRIPT, '--date', dateParam, '--days', String(daysParam)],
{ timeout: CALENDAR_TIMEOUT_MS },
);
const data = JSON.parse(stdout);
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 unsafe_assumptions: JSON.parse(stdout) on the Python script output has no validation. Malformed or unexpected output will throw and be caught, but valid JSON with an unexpected shape will be passed straight to the client. Consider validating with a Zod schema (consistent with the project's api-schemas.ts pattern). [fixable]


fetch(`/api/calendar?date=${date}&days=${days}`)
.then((r) => r.json())
.then((result: CalendarData) => {
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 bugs: The fetch chain calls r.json() without checking r.ok. A 400/401 response will be parsed as JSON and stored in state — the resulting object (e.g. { error: "..." }) has no events or sprints arrays, so the component would render with undefined data. Should check r.ok and reject on error status codes. [fixable]

const d = addDays(baseDate, i);
map.set(d, []);
}
for (const evt of events) {
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 bugs: const today = new Date().toISOString().slice(0, 10) is computed once on render outside of state/effect, but handleToday captures it as a closure. If the component stays mounted across midnight, today becomes stale. Minor in practice, but the same value is also used as initial state for baseDate, which is fine. [fixable]

Copy link
Copy Markdown
Owner Author

@dimakis dimakis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Centaur Review

Found 7 issue(s) (1 critical) (3 warning).

frontend/src/styles/desktop.css

Solid feature addition with good test coverage and clean structure; main concern is the desktop grid being hardcoded to 7 columns regardless of view mode, and UTC-based date grouping that will misplace events near midnight.

  • 🔴 bugs (L468): Desktop grid is hardcoded to repeat(7, 1fr) but the view supports day mode (viewDays=1). When viewing 1 day on desktop, a single column will render in a 7-column grid, leaving 6 empty columns. The grid-template-columns should be dynamic or use auto-fill. [fixable]

server/app.ts

Solid feature addition with good test coverage and clean structure; main concern is the desktop grid being hardcoded to 7 columns regardless of view mode, and UTC-based date grouping that will misplace events near midnight.

  • 🟡 unsafe_assumptions (L675): CALENDAR_SCRIPT is resolved once at module load using BASE_REPO (the repo Mitzo points at). If REPO_PATH doesn't contain command_center/calendar_api.py, the route silently returns empty data. The existsSync guard handles this, but there's no way for the frontend to distinguish 'no events today' from 'calendar script is missing' since the error is in an optional field the frontend never displays. [fixable]
  • 🟡 unsafe_assumptions (L680): execFileAsync('python3', [CALENDAR_SCRIPT, ...]) assumes python3 is on PATH. On some systems (Windows, minimal containers) this may not exist. Consider making the Python path configurable or detecting it at startup. [fixable]

frontend/src/pages/CalendarView.tsx

Solid feature addition with good test coverage and clean structure; main concern is the desktop grid being hardcoded to 7 columns regardless of view mode, and UTC-based date grouping that will misplace events near midnight.

  • 🟡 bugs (L7): toLocalDate() converts ISO datetime strings to dates using new Date(isoStr).toISOString().slice(0, 10) which returns UTC date. An event at e.g. 2026-04-10T23:00:00-04:00 is actually 2026-04-11 in UTC and would be grouped under April 11 instead of April 10 (the user's local date). Should use local date extraction instead. [fixable]

frontend/src/components/EventCard.tsx

Solid feature addition with good test coverage and clean structure; main concern is the desktop grid being hardcoded to 7 columns regardless of view mode, and UTC-based date grouping that will misplace events near midnight.

  • 🔵 missing_tests: No unit tests for EventCard or SprintBar components. The CalendarView integration test covers them indirectly, but the EventCard has non-trivial rendering logic (attendee truncation at 5, time formatting, hangout link rendering) that would benefit from dedicated tests.
  • 🔵 style (L4): Local formatTime() shadows the existing formatTime export from lib/formatTime.ts. While they do different things (clock time vs relative time), the name collision may confuse maintainers. Consider naming this formatClockTime to distinguish it. [fixable]

frontend/src/hooks/useCalendarData.ts

Solid feature addition with good test coverage and clean structure; main concern is the desktop grid being hardcoded to 7 columns regardless of view mode, and UTC-based date grouping that will misplace events near midnight.

  • 🔵 style (L3): CalendarEvent has type: 'meeting' | 'milestone' | 'sprint' but sprint is never used — sprints are a separate SprintInfo type in a separate array. The 'sprint' variant in CalendarEvent.type is dead code. Also, the Zod schema in api-schemas.ts only allows z.enum(['meeting', 'milestone']), so a sprint-typed event would fail server validation. [fixable]


.cal-body {
display: grid;
grid-template-columns: repeat(7, 1fr);
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 bugs: Desktop grid is hardcoded to repeat(7, 1fr) but the view supports day mode (viewDays=1). When viewing 1 day on desktop, a single column will render in a 7-column grid, leaving 6 empty columns. The grid-template-columns should be dynamic or use auto-fill. [fixable]

Comment thread server/app.ts
// --- Calendar API ---

const execFileAsync = promisify(execFile);
const CALENDAR_SCRIPT = join(BASE_REPO, 'command_center', 'calendar_api.py');
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 unsafe_assumptions: CALENDAR_SCRIPT is resolved once at module load using BASE_REPO (the repo Mitzo points at). If REPO_PATH doesn't contain command_center/calendar_api.py, the route silently returns empty data. The existsSync guard handles this, but there's no way for the frontend to distinguish 'no events today' from 'calendar script is missing' since the error is in an optional field the frontend never displays. [fixable]

Comment thread server/app.ts

app.get('/api/calendar', async (req, res) => {
const dateParam = (req.query.date as string) || new Date().toISOString().slice(0, 10);
const daysParam = Math.max(1, Math.min(31, parseInt(req.query.days as string) || 7));
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 unsafe_assumptions: execFileAsync('python3', [CALENDAR_SCRIPT, ...]) assumes python3 is on PATH. On some systems (Windows, minimal containers) this may not exist. Consider making the Python path configurable or detecting it at startup. [fixable]

import { EventCard } from '../components/EventCard';
import { SprintBar } from '../components/SprintBar';

function toLocalDate(isoStr: string): string {
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 bugs: toLocalDate() converts ISO datetime strings to dates using new Date(isoStr).toISOString().slice(0, 10) which returns UTC date. An event at e.g. 2026-04-10T23:00:00-04:00 is actually 2026-04-11 in UTC and would be grouped under April 11 instead of April 10 (the user's local date). Should use local date extraction instead. [fixable]

import { useState } from 'react';
import type { CalendarEvent } from '../hooks/useCalendarData';

function formatTime(isoStr: string): string {
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 style: Local formatTime() shadows the existing formatTime export from lib/formatTime.ts. While they do different things (clock time vs relative time), the name collision may confuse maintainers. Consider naming this formatClockTime to distinguish it. [fixable]

@@ -0,0 +1,100 @@
import { useState, useEffect, useMemo } from 'react';

export interface CalendarEvent {
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 style: CalendarEvent has type: 'meeting' | 'milestone' | 'sprint' but sprint is never used — sprints are a separate SprintInfo type in a separate array. The 'sprint' variant in CalendarEvent.type is dead code. Also, the Zod schema in api-schemas.ts only allows z.enum(['meeting', 'milestone']), so a sprint-typed event would fail server validation. [fixable]

dimakis and others added 4 commits April 10, 2026 20:41
Unified calendar page that merges Google Calendar events, release
milestones, and sprint boundaries via the mgmt calendar_api.py backend.
Mobile-first day/week view with responsive desktop grid layout.

- GET /api/calendar endpoint shells out to calendar_api.py
- useCalendarData hook with date navigation
- CalendarView page with EventCard expand/collapse
- Sprint bars, milestone badges, attendee chips
- Day/Week toggle, prev/next/today navigation
- 17 tests (5 server + 5 hook + 7 page)
- Quick action added to .mitzo.json
- Route at /calendar behind ProtectedRoute

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add script existence check before exec (was #1 critical)
- Add Zod CalendarResponse schema for stdout validation (#2)
- Check r.ok before parsing fetch response in hook (#3)
- Compute today fresh in handleToday to avoid stale date (#6)
- Extract EventCard + SprintBar to separate component files (#7)
- Extract calendar CSS to dedicated calendar.css (#8)
- Add tests: Day/Week toggle, Today button, EventCard expand,
  milestone expand, non-ok HTTP response handling (#4, #5)

832 tests passing (5 new).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove unused imports (beforeEach, writeFileSync, result)
- Fix toLocalDate to use local timezone instead of UTC
- Remove dead 'sprint' variant from CalendarEvent.type union

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Also add .claude/worktrees/ to .prettierignore to prevent
worktree artifacts from failing format checks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@dimakis dimakis force-pushed the feat/calendar-view branch from c4924e7 to ef5367f Compare April 10, 2026 19:41
@dimakis dimakis merged commit 2aef14d into main Apr 10, 2026
1 check passed
@dimakis dimakis deleted the feat/calendar-view branch April 10, 2026 19:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant