### Install Dependencies and Start Development Server Source: https://github.com/vmnog/calendarcn/blob/main/docs/runbooks/local-development.md Run these commands to install project dependencies and start the local development server. Access the application at http://localhost:3000. ```bash pnpm install pnpm dev # Open http://localhost:3000 ``` -------------------------------- ### Install Dependencies and Run Development Server Source: https://github.com/vmnog/calendarcn/blob/main/CONTRIBUTING.md Install project dependencies using pnpm and start the development server. Ensure Node.js and pnpm are installed. ```bash pnpm install # Install dependencies pnpm dev # Start dev server at localhost:3000 ``` -------------------------------- ### Start Development Server Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Starts the development server for testing the calendar application. Navigate to the project directory before running. ```bash cd /Users/victornogueira/fun/calendarcn/.claude/worktrees/feat-month-view && pnpm dev ``` -------------------------------- ### Install Dependencies Source: https://github.com/vmnog/calendarcn/blob/main/README.md Install project dependencies using pnpm. ```bash pnpm install ``` -------------------------------- ### Run Development Server Source: https://github.com/vmnog/calendarcn/blob/main/README.md Start the development server for CalendarCN. ```bash pnpm dev ``` -------------------------------- ### Add a New shadcn/ui Component Source: https://github.com/vmnog/calendarcn/blob/main/docs/runbooks/local-development.md Use this command to add a new component from the shadcn/ui library. Components are installed in `src/components/ui/` and should not be edited directly. ```bash pnpm dlx shadcn@latest add ``` -------------------------------- ### Update switchView for Month View Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Adjust the `switchView` function to correctly set the current date to the start of the month when the 'month' view is selected. ```typescript const switchView = React.useCallback( (newView: ViewType) => { if (newView === view) return; setView(newView); if (newView === "day") { setCurrentDate(startOfDay(new Date())); return; } if (newView === "month") { setCurrentDate((prev) => startOfMonth(prev)); return; } setCurrentDate((prev) => startOfWeek(prev, { weekStartsOn: 0 })); }, [view], ); ``` -------------------------------- ### Create useMonthEventDrag Hook Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Initializes the `useMonthEventDrag` hook, setting up state management for drag operations on calendar events within a month view. It handles drag start, state updates, and references for event handlers and data. ```typescript "use client"; import { useCallback, useEffect, useRef, useState } from "react"; import { addDays, differenceInCalendarDays, startOfDay } from "date-fns"; import type { CalendarEvent } from "@/components/calendar-types"; /** State exposed to the month view during drag */ export interface MonthDragState { eventId: string; event: CalendarEvent; originalDate: Date; targetDate: Date; isDragging: boolean; } interface UseMonthEventDragOptions { /** Ref to the grid container for coordinate calculations */ gridRef: React.RefObject; events: CalendarEvent[]; onEventChange?: (event: CalendarEvent) => void; onEventClick?: (event: CalendarEvent) => void; /** Number of columns (5 or 7 depending on showWeekends) */ columnCount: number; /** Number of rows */ rowCount: number; } interface UseMonthEventDragReturn { dragState: MonthDragState | null; handleDragMouseDown: (e: React.MouseEvent, event: CalendarEvent) => void; } const DRAG_THRESHOLD_PX = 4; interface DragInfo { eventId: string; event: CalendarEvent; startClientX: number; startClientY: number; isDragging: boolean; } /** * Hook for drag-to-move in month view. * Snaps to whole days. Timed events change date but keep time/duration. * All-day/multi-day events shift start date, preserving duration. */ export function useMonthEventDrag({ gridRef, events, onEventChange, onEventClick, columnCount, rowCount, }: UseMonthEventDragOptions): UseMonthEventDragReturn { const [dragState, setDragState] = useState(null); const dragRef = useRef(null); const onEventChangeRef = useRef(onEventChange); const onEventClickRef = useRef(onEventClick); const eventsRef = useRef(events); ``` -------------------------------- ### Build Project for Production Source: https://github.com/vmnog/calendarcn/blob/main/CONTRIBUTING.md Generate a production-ready build of the CalendarCN project. ```bash pnpm build # Production build ``` -------------------------------- ### Run Build Verification Commands Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Executes linting, type checking, and format checking to ensure the project builds correctly. Navigate to the project directory before running. ```bash cd /Users/victornogueira/fun/calendarcn/.claude/worktrees/feat-month-view && pnpm lint && pnpm typecheck && pnpm format:check ``` -------------------------------- ### Get Month Slot Count Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Calculates the dynamic number of event slots available in a month cell based on its height. Ensures the slot count stays within predefined minimum and maximum limits. ```typescript /** * Computes the dynamic slot count based on cell height. */ export function getMonthSlotCount(cellHeight: number): number { const raw = Math.floor(cellHeight / MONTH_EVENT_ROW_HEIGHT); if (raw < MIN_MONTH_SLOTS) return MIN_MONTH_SLOTS; if (raw > MAX_MONTH_SLOTS) return MAX_MONTH_SLOTS; return raw; } ``` -------------------------------- ### Pre-Deployment Checks Source: https://github.com/vmnog/calendarcn/blob/main/docs/runbooks/deployment.md Run these commands locally to ensure the project passes linting, type checking, formatting checks, and builds successfully before merging to main. ```bash pnpm lint # Must pass pnpm typecheck # Must pass pnpm format:check # Must pass pnpm build # Must succeed ``` -------------------------------- ### Project Development and Build Commands Source: https://github.com/vmnog/calendarcn/blob/main/CLAUDE.md Run these commands using pnpm to manage the project's development server, production build, linting, type checking, and formatting. ```bash pnpm dev # Dev server pnpm build # Production build pnpm lint # ESLint pnpm typecheck # TypeScript check pnpm format # Prettier format pnpm format:check # Prettier check (CI) ``` -------------------------------- ### useMonthEventDrag Hook Implementation Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md This hook manages the state and logic for dragging events within a month view. It includes effects for event change handlers, mouse event listeners for drag start, move, and end, and state updates for the dragged event's position. ```typescript useEffect(() => { onEventChangeRef.current = onEventChange; }, [onEventChange]); useEffect(() => { onEventClickRef.current = onEventClick; }, [onEventClick]); useEffect(() => { eventsRef.current = events; }, [events]); const handleMouseMoveRef = useRef<((e: MouseEvent) => void) | null>(null); const handleMouseUpRef = useRef<(() => void) | null>(null); const cleanup = useCallback(() => { if (handleMouseMoveRef.current) { window.removeEventListener("mousemove", handleMouseMoveRef.current); } if (handleMouseUpRef.current) { window.removeEventListener("mouseup", handleMouseUpRef.current); } }, []); useEffect(() => { handleMouseMoveRef.current = (e: MouseEvent) => { const drag = dragRef.current; if (!drag) return; const deltaX = Math.abs(e.clientX - drag.startClientX); const deltaY = Math.abs(e.clientY - drag.startClientY); if ( !drag.isDragging && deltaX < DRAG_THRESHOLD_PX && deltaY < DRAG_THRESHOLD_PX ) return; if (!drag.isDragging) { drag.isDragging = true; } const grid = gridRef.current; if (!grid) return; const rect = grid.getBoundingClientRect(); const cellWidth = rect.width / columnCount; const cellHeight = rect.height / rowCount; const col = Math.floor((e.clientX - rect.left) / cellWidth); const row = Math.floor((e.clientY - rect.top) / cellHeight); const clampedCol = Math.max(0, Math.min(col, columnCount - 1)); const clampedRow = Math.max(0, Math.min(row, rowCount - 1)); // Calculate target date from grid position // The grid's first cell is the first day of the first visible week row const dayIndex = clampedRow * columnCount + clampedCol; // We need to get the actual date from the grid — store the first date // For now, compute the day delta from the event's original position const originalDate = startOfDay(drag.event.start); // Find original position in the grid const firstCellElements = grid.querySelectorAll("[data-date]"); const dates: Date[] = []; firstCellElements.forEach((el) => { const dateStr = el.getAttribute("data-date"); if (dateStr) dates.push(new Date(dateStr)); }); if (dates.length > dayIndex) { const targetDate = dates[dayIndex]; setDragState({ eventId: drag.eventId, event: drag.event, originalDate, targetDate, isDragging: true, }); } }; handleMouseUpRef.current = () => { const drag = dragRef.current; if (!drag) return; cleanup(); if (drag.isDragging) { setDragState((prev) => { if (!prev) return null; const event = eventsRef.current.find((e) => e.id === drag.eventId); if (!event) return null; const daysDelta = differenceInCalendarDays( prev.targetDate, prev.originalDate, ); if (daysDelta !== 0) { onEventChangeRef.current?.({ ...event, start: addDays(event.start, daysDelta), end: addDays(event.end, daysDelta), }); } return null; }); } else { setDragState(null); } dragRef.current = null; }; }, [gridRef, cleanup, columnCount, rowCount]); const handleDragMouseDown = useCallback( (e: React.MouseEvent, event: CalendarEvent) => { if (e.button !== 0) return; onEventClickRef.current?.(event); dragRef.current = { eventId: event.id, event, startClientX: e.clientX, startClientY: e.clientY, isDragging: false, }; setDragState({ eventId: event.id, event, originalDate: startOfDay(event.start), targetDate: startOfDay(event.start), isDragging: false, }); if (handleMouseMoveRef.current) { window.addEventListener("mousemove", handleMouseMoveRef.current); } if (handleMouseUpRef.current) { window.addEventListener("mouseup", handleMouseUpRef.current); } }, [], ); useEffect(() => { return cleanup; }, [cleanup]); return { dragState, handleDragMouseDown }; } ``` -------------------------------- ### Add and Commit MonthViewDayCell Component Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Navigate to the project directory, stage the new component file, and commit the changes with a descriptive message. ```bash cd /Users/victornogueira/fun/calendarcn/.claude/worktrees/feat-month-view git add src/components/month-view-day-cell.tsx git commit -m "feat: add MonthViewDayCell component" ``` -------------------------------- ### New Component Prompt Template Source: https://github.com/vmnog/calendarcn/blob/main/tools/prompts/new-component.md Use this template to instruct Claude to create a new React component. Specify the component name, its location, and detailed requirements including directives, prop interfaces, import order, styling utilities, constant definitions, and early return patterns. Also, describe the component's functionality and its props. ```markdown Create a new React component `{ComponentName}` in `src/components/{component-name}.tsx`. Requirements: 1. Add `"use client"` directive at the top 2. Define `interface {ComponentName}Props` with typed props 3. Follow the project import order: external → @/lib/utils → @/hooks → @/components → @/lib 4. Use `cn()` for conditional Tailwind classes 5. Constants in ALL_CAPS with JSDoc comments 6. Early returns for edge cases 7. If types are shared, add them to `week-view-types.ts` The component should: {describe what it does} Props: - {prop1}: {type} — {description} - {prop2}: {type} — {description} After creating, run: `pnpm lint && pnpm typecheck && pnpm format:check` ``` -------------------------------- ### Run All Project Checks Source: https://github.com/vmnog/calendarcn/blob/main/CONTRIBUTING.md Execute linting, type checking, and building processes to ensure the project is in a valid state before submitting a pull request. ```bash pnpm lint && pnpm typecheck && pnpm build ``` -------------------------------- ### Verify Typecheck and Lint Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-02-month-view-vertical-scroll.md After implementing the hook, run the type checker and linter to ensure code quality and correctness. This step helps catch potential errors before committing. ```bash npx tsc --noEmit && pnpm lint ``` -------------------------------- ### Pre-Commit Checks Command Source: https://github.com/vmnog/calendarcn/blob/main/CLAUDE.md Execute this command before committing to ensure code quality and consistency. It runs linting, type checking, and format checking. ```bash pnpm lint && pnpm typecheck && pnpm format:check ``` -------------------------------- ### Clone Repository Source: https://github.com/vmnog/calendarcn/blob/main/README.md Clone the CalendarCN repository to your local machine. ```bash git clone https://github.com/vmnog/calendarcn.git cd calendarcn ``` -------------------------------- ### Run Code Quality and Type Checks Source: https://github.com/vmnog/calendarcn/blob/main/CONTRIBUTING.md Execute linters, type checkers, and formatters to maintain code quality and consistency. These commands should be run before committing. ```bash pnpm lint # Run ESLint pnpm typecheck # Run TypeScript type checking pnpm format # Format code with Prettier pnpm format:check # Check formatting without writing ``` -------------------------------- ### Run Typecheck and Lint Commands Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Executes type checking and linting commands within a specific project directory using pnpm. ```bash cd /Users/victornogueira/fun/calendarcn/.claude/worktrees/feat-month-view && pnpm typecheck && pnpm lint ``` -------------------------------- ### Event Lifecycle Flow Source: https://github.com/vmnog/calendarcn/blob/main/docs/architecture.md Illustrates the sequence of actions from user interaction to state update during event manipulation. ```text User interaction → Hook (drag/resize) → onEventChange callback → page.tsx setState → Re-render ``` -------------------------------- ### Run Code Quality Checks Source: https://github.com/vmnog/calendarcn/blob/main/docs/runbooks/local-development.md Execute these commands to perform linting, TypeScript type checking, and code formatting. Always run these before committing code. ```bash pnpm lint # ESLint checks pnpm typecheck # TypeScript checks pnpm format # Auto-format with Prettier pnpm format:check # Check without writing ``` ```bash pnpm lint && pnpm typecheck && pnpm format:check ``` -------------------------------- ### Git Add and Commit Changes Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Stages all changes in the current directory and commits them with a descriptive message. ```bash cd /Users/victornogueira/fun/calendarcn/.claude/worktrees/feat-month-view git add src/app/page.tsx git commit -m "feat: integrate MonthView into page with navigation and more-click" ``` -------------------------------- ### Initialize Scroll State and Hook in MonthView Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-02-month-view-vertical-scroll.md Set up refs, state variables for buffer rows and row height, and initialize the useVerticalScroll hook. This prepares the component for scroll-based navigation and rendering. ```typescript const scrollContainerRef = useRef(null); /** Number of rows visible in the viewport (for row height calculation) */ const visibleRowCount = weekRows.length; /** Dynamic buffer of extra rows above and below */ const [bufferAbove, setBufferAbove] = useState(0); const [bufferBelow, setBufferBelow] = useState(0); /** Row height in px — computed from container */ const [rowHeight, setRowHeight] = useState(0); // Measure row height from container useEffect(() => { const el = scrollContainerRef.current; if (!el || visibleRowCount === 0) return; const observer = new ResizeObserver(() => { setRowHeight(el.clientHeight / visibleRowCount); }); observer.observe(el); return () => observer.disconnect(); }, [visibleRowCount]); const handleScrollNavigate = useCallback( (rowsDelta: number) => { // Shift the base date by rowsDelta weeks // Positive = scrolled down (forward), negative = scrolled up (backward) const newDate = addWeeks(currentDate, rowsDelta); onDateChange?.(newDate); }, [currentDate, onDateChange], ); const { scrollOffset, slideOffset, isScrolling, isAnimating, triggerSlideAnimation, } = useVerticalScroll({ containerRef: scrollContainerRef, rowHeight, onNavigate: handleScrollNavigate, disabled: isDragging || isResizing, }); // Extend buffer dynamically based on scroll offset useEffect(() => { if (rowHeight <= 0) return; const rowsScrolled = Math.abs(scrollOffset) / rowHeight; const extraNeeded = Math.ceil(rowsScrolled) + 2; // +2 for safety if (scrollOffset < 0) { // Scrolling up (backward) — need rows above setBufferAbove((prev) => Math.max(prev, extraNeeded)); } else if (scrollOffset > 0) { // Scrolling down (forward) — need rows below setBufferBelow((prev) => Math.max(prev, extraNeeded)); } }, [scrollOffset, rowHeight]); // Reset buffer when currentDate changes (after navigation commit) useEffect(() => { setBufferAbove(0); setBufferBelow(0); }, [currentDate]); // Build buffered week rows: buffer-above + current month + buffer-below const bufferedWeekRows = useMemo(() => { const rows: MonthWeekRow[] = []; // Rows above: generate backwards from the first row of current month if (bufferAbove > 0 && weekRows.length > 0) { const firstDate = weekRows[0].days[0].date; for (let i = bufferAbove; i > 0; i--) { rows.push(generateWeekRow(addWeeks(firstDate, -i))); } } // Current month rows rows.push(...weekRows); // Rows below: generate forwards from the last row of current month if (bufferBelow > 0 && weekRows.length > 0) { const lastDate = weekRows[weekRows.length - 1].days[0].date; for (let i = 1; i <= bufferBelow; i++) { rows.push(generateWeekRow(addWeeks(lastDate, i))); } } return rows; }, [weekRows, bufferAbove, bufferBelow]); // Scroll style with transform const scrollTransformY = scrollOffset + slideOffset; const scrollStyle: React.CSSProperties = { transform: `translateY(${scrollTransformY}px)`, transition: isAnimating ? `transform ${200}ms ease-out` : undefined, }; ``` -------------------------------- ### Commit Changes Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Stages all changes and commits them with a descriptive message. Ensure you are in the correct project directory. ```bash cd /Users/victornogueira/fun/calendarcn/.claude/worktrees/feat-month-view git add -A git commit -m "feat: visual polish for month view - weekends, spanning bars, edge cases" ``` -------------------------------- ### CalendarCN Project Structure Source: https://github.com/vmnog/calendarcn/blob/main/CLAUDE.md Overview of the directory structure for the CalendarCN project, highlighting key components and utility files. ```text src/ ├── app/ # Next.js App Router (page.tsx owns top-level state) ├── components/ │ ├── ui/ # shadcn/ui primitives (don't edit directly) │ ├── week-view*.tsx # Core calendar — grid, columns, time axis, all-day row │ ├── calendar-event-* # Event rendering & interactions │ ├── event-* # Context menu, detail panel, detail popover │ ├── sidebar-* # Left/right sidebars │ ├── nav-* # Navigation components │ └── theme-* # Dark mode provider & toggle ├── hooks/ # Custom hooks (drag, resize, all-day-resize, scroll) └── lib/ ├── event-utils.ts # Event positioning & column assignment algorithms ├── mock-events.ts # Sample data └── utils.ts # cn() helper ``` -------------------------------- ### Add Month Grid Generation Utilities Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Implement `generateMonthGrid` and related types in `event-utils.ts` to create the month view's calendar grid structure. ```ts import { startOfMonth, endOfMonth, startOfWeek, endOfWeek, eachWeekOfInterval, eachDayOfInterval, getWeek, isSameMonth, format, isToday, } from "date-fns"; ``` ```ts /** Height of a single event row in the month grid (px) */ export const MONTH_EVENT_ROW_HEIGHT = 22; /** Minimum number of event slots per cell (1 event + "+N more") */ const MIN_MONTH_SLOTS = 2; /** Maximum number of event slots per cell at default viewport */ const MAX_MONTH_SLOTS = 6; /** * Represents a single row (week) in the month grid. * Each row contains 7 days (or 5 if weekends hidden). */ export interface MonthWeekRow { /** ISO week number */ weekNumber: number; /** Days in this row */ days: WeekDay[]; } /** * Generates the month grid: an array of week rows for the given month. * Each row has 7 days (Sun-Sat). Days from adjacent months are included * to fill complete weeks. Returns 4-6 rows; caller pads to minimum 5 if needed. */ export function generateMonthGrid( currentDate: Date, weekStartsOn: 0 | 1 | 2 | 3 | 4 | 5 | 6 = 0, ): MonthWeekRow[] { const monthStart = startOfMonth(currentDate); const monthEnd = endOfMonth(currentDate); const calendarStart = startOfWeek(monthStart, { weekStartsOn }); const calendarEnd = endOfWeek(monthEnd, { weekStartsOn }); const weekStarts = eachWeekOfInterval( { start: calendarStart, end: calendarEnd }, { weekStartsOn }, ); return weekStarts.map((weekStart) => { const weekEnd = endOfWeek(weekStart, { weekStartsOn }); const days = eachDayOfInterval({ start: weekStart, end: weekEnd }); return { weekNumber: getWeek(weekStart, { weekStartsOn }), days: days.map((date) => ({ date, dayName: format(date, "EEE"), dayNumber: date.getDate(), isToday: isToday(date), })), }; }); } ``` ```ts /** * A single event slot within a month day cell. * Either a visible event or a "+N more" overflow indicator. */ export type MonthCellSlot = | { type: "event-bar"; event: CalendarEvent; /** Column span (1 for single-day, >1 for multi-day) */ colSpan: number; /** Whether this is the start cell (renders the bar) or a continuation (empty placeholder) */ isStart: boolean; /** Whether the bar has a rounded left edge */ roundedLeft: boolean; /** Whether the bar has a rounded right edge */ roundedRight: boolean; } | { type: "event-item"; event: CalendarEvent; } | { type: "more"; count: number; } | { type: "spacer"; }; ``` -------------------------------- ### CalendarCN Key Files Source: https://github.com/vmnog/calendarcn/blob/main/CLAUDE.md Highlights of essential files within the CalendarCN project, detailing their specific roles in the component's functionality. ```text - `src/components/week-view-types.ts` — **canonical type source** for all shared types - `src/app/page.tsx` — owns `currentDate`, `events`, `selectedEventId` state - `src/lib/event-utils.ts` — positioning math (column assignment, pixel calculations) - `src/hooks/use-event-drag.ts` — drag interaction (snapping, cross-day, auto-scroll) - `src/hooks/use-event-resize.ts` — resize interaction (anchor model, edge flipping) ``` -------------------------------- ### Add Mock Events for Month View Testing Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Adds multi-day and all-day mock events to `src/lib/mock-events.ts` to test month view rendering. Includes a multi-day event spanning a week boundary and a single all-day event. ```typescript // Multi-day event spanning a week boundary (for month view testing) const monthViewMultiDay = new Date(baseDate); monthViewMultiDay.setDate( baseDate.getDate() + ((4 - baseDate.getDay() + 7) % 7), ); // Thursday const multiDayStart = new Date(monthViewMultiDay); multiDayStart.setHours(0, 0, 0, 0); const multiDayEnd = new Date(multiDayStart); multiDayEnd.setDate(multiDayEnd.getDate() + 4); // Thu-Mon (spans weekend) events.push({ id: "multi-day-spanning", title: "Team Offsite", start: multiDayStart, end: multiDayEnd, isAllDay: true, color: "purple" as EventColor, calendarId: "cal1", description: "Annual team offsite spanning Thu-Mon", timezone: "America/Sao_Paulo", status: "busy" as const, visibility: "default" as const, }); // All-day event on a single day const allDaySingle = new Date(baseDate); allDaySingle.setDate(baseDate.getDate() + 2); events.push({ id: "all-day-single", title: "Company Holiday", start: startOfDay(allDaySingle), end: startOfDay(allDaySingle), isAllDay: true, color: "green" as EventColor, calendarId: "cal1", timezone: "America/Sao_Paulo", status: "free" as const, visibility: "default" as const, }); ``` -------------------------------- ### Stage and Commit Changes Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Add the modified and renamed files to the Git staging area and commit them with a descriptive message. ```bash cd /Users/victornogueira/fun/calendarcn/.claude/worktrees/feat-month-view git add src/components/calendar-day-headers.tsx src/components/week-view.tsx git add -u # catches the deletion of the old file git commit -m "refactor: rename WeekViewDayColumns to CalendarDayHeaders" ``` -------------------------------- ### Event Positioning Pipeline Source: https://github.com/vmnog/calendarcn/blob/main/docs/architecture.md Details the steps involved in calculating the visual position and layout of events within the calendar grid. ```text events[] → getEventsForDay(day) → assignColumns() → calculatePositionedEvents() ``` -------------------------------- ### Commit Changes Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Stages and commits the newly created or modified component file using Git. This is a standard practice for version control. ```bash cd /Users/victornogueira/fun/calendarcn/.claude/worktrees/feat-month-view git add src/components/month-view-event-item.tsx git commit -m "feat: add MonthViewEventItem component" ``` -------------------------------- ### ADR Template Structure Source: https://github.com/vmnog/calendarcn/blob/main/docs/decisions/001-adr-template.md This markdown template provides the standard format for documenting architecture decisions. It includes sections for status, context, the decision made, and its positive, negative, and neutral consequences. ```markdown # ADR-NNN: Title ## Status [Proposed | Accepted | Deprecated | Superseded by ADR-NNN] ## Context What is the issue or question? What forces are at play? ## Decision What did we decide? Be specific about the chosen approach. ## Consequences ### Positive - What benefits does this bring? ### Negative - What tradeoffs or risks come with this? ### Neutral - Any neutral observations? ``` -------------------------------- ### Commit Changes Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Stage and commit the newly added MonthViewGrid component to the Git repository. This records the changes made to the codebase. ```bash cd /Users/victornogueira/fun/calendarcn/.claude/worktrees/feat-month-view git add src/components/month-view-grid.tsx git commit -m "feat: add MonthViewGrid component" ``` -------------------------------- ### Rename Component File Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Use `git mv` to rename the component file and update its path. ```bash cd /Users/victornogueira/fun/calendarcn/.claude/worktrees/feat-month-view git mv src/components/week-view-day-columns.tsx src/components/calendar-day-headers.tsx ``` -------------------------------- ### Initialize Month Event Drag Hook Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Initializes the useMonthEventDrag hook with necessary references and event handlers. Ensure gridRef is passed to MonthViewGrid and handleDragMouseDown is set as the onDragMouseDown prop. ```typescript const gridRef = React.useRef(null); const columnCount = showWeekends ? 7 : 5; const { dragState, handleDragMouseDown, } = useMonthEventDrag({ gridRef, events: filteredEvents, onEventChange, onEventClick, columnCount, rowCount: weekRows.length, }); ``` -------------------------------- ### Add MonthView Imports Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Import the MonthView component and necessary date-fns functions into your page component. Ensure date-fns imports are merged if they already exist. ```typescript import { MonthView } from "@/components/month-view"; import { addMonths, startOfMonth } from "date-fns"; ``` -------------------------------- ### CalendarCN System Diagram Source: https://github.com/vmnog/calendarcn/blob/main/docs/architecture.md Illustrates the component hierarchy and data flow for the CalendarCN React calendar component. ```text ┌─────────────────────────────────────────────────────┐ │ page.tsx (App State Owner) │ │ ┌─────────────┬──────────────┬──────────────────┐ │ │ │ currentDate │ events[] │ selectedEventId │ │ │ └──────┬──────┴──────┬───────┴────────┬─────────┘ │ │ │ │ │ │ │ ┌──────▼──────┐ ┌───▼────────┐ ┌───▼──────────┐ │ │ │ DatePicker │ │ WeekView │ │ EventDetail │ │ │ │ (sidebar) │ │ (main) │ │ Panel/Popover│ │ │ └─────────────┘ └───┬────────┘ └──────────────┘ │ │ │ │ │ ┌─────────────┼─────────────┐ │ │ │ │ │ │ │ ┌──────▼──────┐ ┌────▼─────┐ ┌────▼──────────┐ │ │ │ AllDayRow │ │ Grid │ │ TimeAxis │ │ │ │ │ │ │ │ │ │ │ │ (all-day │ │ (timed │ │ (hour labels) │ │ │ │ events) │ │ events) │ │ │ │ │ └─────────────┘ └────┬─────┘ └───────────────┘ │ │ │ │ │ ┌────────▼─────────┐ │ │ │ CalendarEventItem│ │ │ │ (renders each │ │ │ │ event segment) │ │ │ └──────────────────┘ │ └─────────────────────────────────────────────────────┘ ``` -------------------------------- ### Commit changes for all-day event resize cursors Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-03-31-resize-cursor-style.md Stage and commit the modified `calendar-event-item.tsx` and `use-all-day-resize.ts` files after updating the cursor styles for all-day event horizontal edges. ```bash git add src/components/calendar-event-item.tsx src/hooks/use-all-day-resize.ts git commit -m "feat: use col-resize cursor for all-day event horizontal edges" ``` -------------------------------- ### Rollback Deployment Source: https://github.com/vmnog/calendarcn/blob/main/docs/runbooks/deployment.md If a deployment needs to be reverted, use these Git commands to revert the merge commit and push the changes. Vercel will automatically deploy the revert. ```bash # Revert the merge commit git revert git push origin main # Vercel auto-deploys the revert ``` -------------------------------- ### Update Navigation Functions for Month View Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Modify navigation callbacks (goToToday, goToPrev, goToNext) to correctly handle the 'month' view by using `startOfMonth` and `addMonths`. ```typescript const goToToday = React.useCallback(() => { if (view === "day") { setCurrentDate(startOfDay(new Date())); } else if (view === "month") { setCurrentDate(startOfMonth(new Date())); } else { setCurrentDate(startOfWeek(new Date(), { weekStartsOn: 0 })); } }, [view]); ``` ```typescript const goToPrev = React.useCallback(() => { if (view === "day") { setCurrentDate((prev) => addDays(prev, -1)); } else if (view === "month") { setCurrentDate((prev) => addMonths(prev, -1)); } else { setCurrentDate((prev) => addWeeks(prev, -1)); } }, [view]); ``` ```typescript const goToNext = React.useCallback(() => { if (view === "day") { setCurrentDate((prev) => addDays(prev, 1)); } else if (view === "month") { setCurrentDate((prev) => addMonths(prev, 1)); } else { setCurrentDate((prev) => addWeeks(prev, 1)); } }, [view]); ``` -------------------------------- ### Final Commit for Month View Polish Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-02-month-view-vertical-scroll.md Commit all changes related to month view polish and edge case handling, including modifications to `src/components/month-view.tsx` and `src/components/month-view-grid.tsx`. ```bash git add -A git commit -m "feat: month view vertical scroll — polish and edge cases" ``` -------------------------------- ### Commit Fixes After Testing Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Stages and commits any fixes identified during manual testing. Ensure you are in the correct project directory. ```bash cd /Users/victornogueira/fun/calendarcn/.claude/worktrees/feat-month-view git add -A git commit -m "fix: address issues found during manual testing" ``` -------------------------------- ### Define Month View Props Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-02-month-view-vertical-scroll.md Add `onPrevMonth` and `onNextMonth` props to `MonthViewProps` to enable external navigation actions. ```typescript onPrevMonth?: () => void; onNextMonth?: () => void; ``` -------------------------------- ### Commit Week View Highlight Changes Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Stages and commits the files modified for the week view column highlight animation. Ensure all related files, including CSS and component definitions, are included. ```bash cd /Users/victornogueira/fun/calendarcn/.claude/worktrees/feat-month-view git add src/components/week-view.tsx src/components/week-view-types.ts src/components/week-view-grid.tsx src/app/page.tsx src/app/globals.css git commit -m "feat: add column highlight animation for more-click navigation" ``` -------------------------------- ### MonthView Component Implementation Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md The MonthView component orchestrates the display of a calendar month. It computes the month grid using `generateMonthGrid` and renders it via `MonthViewGrid`. It also handles context menu interactions and integrates with popover boundary providers. Use this component when a full month view is required. ```tsx "use client"; import * as React from "react"; import { cn } from "@/lib/utils"; import { format, startOfMonth } from "date-fns"; import type { CalendarEvent, ViewSettings } from "./calendar-types"; import { generateMonthGrid } from "@/lib/event-utils"; import { MonthViewGrid } from "./month-view-grid"; import { EventContextMenu } from "./event-context-menu"; import { CalendarPopoverBoundaryProvider } from "./calendar-popover-context"; export interface MonthViewProps { currentDate: Date; events?: CalendarEvent[]; viewSettings?: ViewSettings; onEventClick?: (event: CalendarEvent) => void; selectedEventId?: string; onBackgroundClick?: () => void; onEventChange?: (event: CalendarEvent) => void; onMoreClick?: (date: Date) => void; isSidebarOpen?: boolean; onDockToSidebar?: () => void; onClosePopover?: () => void; className?: string; } /** Day-of-week header labels */ const DAY_NAMES = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; const WEEKDAY_NAMES = ["Mon", "Tue", "Wed", "Thu", "Fri"]; /** * Month view orchestrator. * Computes the month grid and delegates rendering to MonthViewGrid. */ export function MonthView({ currentDate, events = [], viewSettings, onEventClick, selectedEventId, onBackgroundClick, onEventChange, onMoreClick, isSidebarOpen, onDockToSidebar, onClosePopover, className, }: MonthViewProps) { const showWeekends = viewSettings?.showWeekends ?? true; const showWeekNumbers = viewSettings?.showWeekNumbers ?? false; const monthDate = startOfMonth(currentDate); const weekRows = React.useMemo( () => generateMonthGrid(monthDate), [monthDate], ); // Filter events based on view settings const filteredEvents = React.useMemo(() => { if (viewSettings?.showDeclinedEvents === false) { // When we have a declined status, filter here. // For now, pass all events through. return events; } return events; }, [events, viewSettings?.showDeclinedEvents]); // Context menu state const [contextMenu, setContextMenu] = React.useState<{ x: number; y: number; event: CalendarEvent; } | null>(null); const handleContextMenu = React.useCallback( (e: React.MouseEvent, event: CalendarEvent) => { setContextMenu({ x: e.clientX, y: e.clientY, event }); }, [], ); const closeContextMenu = React.useCallback(() => { setContextMenu(null); }, []); const calendarBoundaryRef = React.useRef(null); const headerRef = React.useRef(null); const dayNames = showWeekends ? DAY_NAMES : WEEKDAY_NAMES; const columnCount = dayNames.length; return (
{ const target = e.target as HTMLElement; if (target.closest("[data-radix-popper-content-wrapper]")) return; if (target.closest("[role=button]")) return; if (target.closest("button")) return; onBackgroundClick?.(); }} > {/* Day-of-week header */}
{showWeekNumbers &&
} {dayNames.map((name) => (
{name}
))}
{/* Month grid */} {/* Context menu */} {contextMenu && ( )}
); } ``` -------------------------------- ### Calendar View and Settings Types Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Defines the possible view types for the calendar and settings for display preferences. ```typescript import type React from "react"; /** * Calendar view mode — "day" shows a single column, "week" shows 7 columns, "month" shows a month grid */ export type ViewType = "day" | "week" | "month"; /** * View settings for display preferences (toggleable from the view dropdown) */ export interface ViewSettings { showWeekends: boolean; showDeclinedEvents: boolean; showWeekNumbers: boolean; } ``` -------------------------------- ### Grouped Input Styling Source: https://github.com/vmnog/calendarcn/blob/main/CLAUDE.md Styles a wrapper for grouped inputs (e.g., icon + input) to handle borders and focus states, delegating visual styling to the wrapper. ```css - Wrapper div gets the border/hover/focus styling using `has-[:focus]`: rounded-sm border border-transparent cursor-text hover:border-[#373737] has-[:focus]:border-[#242424] has-[:focus]:bg-[#242424] - Wrapper gets `onClick={() => inputRef.current?.focus()}` so clicking anywhere focuses the input - Inner input gets `border-none p-0 bg-transparent outline-none` (no border, wrapper handles it) ``` -------------------------------- ### Commit Mock Event Changes Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Stages and commits the changes made to the mock events file. ```bash cd /Users/victornogueira/fun/calendarcn/.claude/worktrees/feat-month-view git add src/lib/mock-events.ts git commit -m "feat: add month-view-friendly mock events" ``` -------------------------------- ### Apply Highlight Class in WeekViewGrid Source: https://github.com/vmnog/calendarcn/blob/main/docs/superpowers/plans/2026-04-01-month-view.md Applies the 'column-highlight' CSS class to day columns in WeekViewGrid when the day matches the highlightedDate prop. This visually indicates the navigated-to day. ```typescript // In src/components/week-view-grid.tsx, when rendering day columns: // Check if the day matches highlightedDate and apply the column-highlight class. ```