Skip to content

feat: Add event support to notes (color + recurring)#94

Merged
panteLx merged 4 commits intomainfrom
feat/events-in-notes
Dec 21, 2025
Merged

feat: Add event support to notes (color + recurring)#94
panteLx merged 4 commits intomainfrom
feat/events-in-notes

Conversation

@panteLx
Copy link
Owner

@panteLx panteLx commented Dec 21, 2025

Adds first-class "event" items alongside free-form notes so users can create colored, recurring events and have them displayed/styled in the calendar.

Adds type, color, recurring pattern and interval fields to the data model and API; updates hooks and responses to create/update events. Extends the note editor with type selection, color presets and custom recurring intervals, and updates calendar rendering to surface event titles and border styling. Updates translations, README copy and UI dialog handling, and adds a small UI dependency for radio controls.

Benefits: enables distinct event semantics, visual differentiation, and recurring scheduling for calendar entries, improving usability for planned events versus simple notes.

Summary by CodeRabbit

  • New Features

    • Added support for creating events alongside notes with customizable colors
    • Introduced recurring event patterns (weekly/monthly) with flexible intervals
    • Multi-entry date indicators showing total notes/events count per day
    • New dialog for viewing, editing, and deleting all notes/events for a specific date
  • Documentation

    • Updated Quick Actions description to clarify both notes and events can be added
  • UI Components

    • Added RadioGroup, Badge, and Alert components for enhanced event management

✏️ Tip: You can customize this high-level summary in your review settings.

Adds first-class "event" items alongside free-form notes so users can create colored, recurring events and have them displayed/styled in the calendar.

Adds type, color, recurring pattern and interval fields to the data model and API; updates hooks and responses to create/update events. Extends the note editor with type selection, color presets and custom recurring intervals, and updates calendar rendering to surface event titles and border styling. Updates translations, README copy and UI dialog handling, and adds a small UI dependency for radio controls.

Benefits: enables distinct event semantics, visual differentiation, and recurring scheduling for calendar entries, improving usability for planned events versus simple notes.
Copilot AI review requested due to automatic review settings December 21, 2025 18:35
@coderabbitai
Copy link

coderabbitai bot commented Dec 21, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

This PR introduces support for typed notes and recurring events to the calendar application. Database schema adds type, color, recurringPattern, and recurringInterval columns to calendar_notes. API routes extended to handle these fields. New event utility functions match recurring events by date. UI components redesigned to support event creation/editing with color selection and recurrence configuration. New dialog component for listing notes/events per date. Localization expanded across multiple languages.

Changes

Cohort / File(s) Change Summary
Database Schema & Migrations
lib/db/schema.ts, drizzle/0012_confused_nico_minoru.sql, drizzle/meta/0012_snapshot.json, drizzle/meta/_journal.json
Four new columns added to calendar_notes table: type (default "note"), color (nullable), recurringPattern (default "none"), and recurringInterval (integer). Schema types CalendarNote and NewCalendarNote updated via Drizzle inference.
API Routes
app/api/notes/route.ts, app/api/notes/[id]/route.ts
POST route now extracts and validates type, color, recurringPattern, recurringInterval; defaults applied on insert. PUT route accepts same fields, computes finalType with fallback to existing, conditionally updates fields based on type, and emits SSE event on success.
Event Utilities
lib/event-utils.ts
New module exporting matchesRecurringEvent (supports custom-weeks/custom-months patterns), findEventsForDate, findNotesForDate, and legacy wrappers for filtering notes by date with recurrence matching.
Note/Event Dialogs & State
components/note-sheet.tsx, components/dialog-manager.tsx, components/notes-list-dialog.tsx, hooks/useDialogStates.ts, hooks/useNoteActions.ts, hooks/useNotes.ts
NoteSheet extended with type selection, color picker, and recurring pattern UI; accepts and passes expanded parameters. DialogManager API expanded with onNoteSubmit signature change and new notesListDialog props. New NotesListDialog component renders events/notes split view with edit/delete actions. State hooks updated to track and propagate type, color, recurring metadata.
Calendar Grid & Main Page
components/calendar-grid.tsx, app/page.tsx
Calendar grid refactored to use findNotesForDate for per-date retrieval, displays event borders, icons, and counts. App page adds dialog handlers for notes list, edit, delete flows; wires extended note submission parameters through component tree.
UI Components
components/ui/radio-group.tsx, components/ui/badge.tsx, components/ui/alert.tsx, components/calendar-sheet.tsx, components/calendar-compare-sheet.tsx, components/calendar-compare-view.tsx
New RadioGroup/RadioGroupItem components wrap Radix primitives. New Badge component for visual labeling. New Alert/AlertTitle/AlertDescription components for styled notifications. CalendarSheet onSubmit prop type expanded to allow Promise. Compare components updated to use Alert instead of custom div styling.
Localization
messages/en.json, messages/de.json, messages/it.json
Note section expanded with type/event/recurring keys. Create/edit templates updated to "{item} erstellen/bearbeiten" pattern. New keys for event color, title, recurring patterns (weekly, monthly), intervals, and warnings. Hints updated with longer, more descriptive guidance.
Dependencies
package.json
Added @radix-ui/react-radio-group@^1.3.8.

Sequence Diagram

sequenceDiagram
    actor User
    participant UI as Note Sheet UI
    participant Handler as handleNoteSubmit
    participant API as API Route<br/>(POST/PUT)
    participant DB as Database
    participant Grid as Calendar Grid

    User->>UI: Select type (Note or Event)
    alt Type = Event
        User->>UI: Pick color
        User->>UI: Select recurring pattern
        alt Recurring = Custom
            User->>UI: Set interval & unit
        end
    end
    
    User->>UI: Submit
    UI->>Handler: handleNoteSubmit(text, type, color, pattern, interval)
    Handler->>API: POST/PUT with all fields
    API->>DB: Insert/Update note with type, color,<br/>recurringPattern, recurringInterval
    DB-->>API: Success
    API-->>Handler: Confirm
    Handler->>Grid: Refresh calendar view
    
    Note over Grid: findNotesForDate finds matching<br/>recurring events for each day
    Grid->>Grid: Render events with color,<br/>borders, recurrence badge
    Grid-->>User: Display updated calendar
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • event-utils.ts: Recurring event matching logic (custom-weeks, custom-months) requires careful validation of date arithmetic and modulo calculations
  • note-sheet.tsx: Substantial state and UI expansion; interplay between type, color, pattern, interval fields and conditional rendering paths
  • API routes: Field handling and defaults in POST/PUT; ensure backwards compatibility and proper validation
  • Dialog flow: Multi-component prop propagation (DialogManager → NoteSheet → API) with new notesListDialog state; verify all call sites updated correctly
  • Calendar grid: Refactored note lookup using findNotesForDate; verify recurring events display correctly on all matching dates
  • Localization keys: Verify interpolation placeholders ({item}, {count}) are used consistently across all supported languages

Possibly related PRs

  • PR #70: Both modify dialog management surface and props (notes-list dialog APIs and password dialog changes)
  • PR #54: Both extend note/event handling through useNoteActions, DialogManager, and calendar-grid flows
  • PR #83: Both touch note dialog/sheet surface and DialogManager component relationships

Suggested labels

enhancement


🐰 Hops with festive cheer!
Events now bloom in colors bright,
Recurring patterns, week to night,
Notes and gatherings take flight,
Calendar's magic shines more right! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.32% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main feature addition: introducing event support to notes with color and recurring capabilities, which aligns perfectly with the core objective of the PR.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/events-in-notes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@panteLx panteLx linked an issue Dec 21, 2025 that may be closed by this pull request
3 tasks
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (4)
lib/db/schema.ts (1)

121-124: Consider making recurringPattern NOT NULL for data consistency.

The recurringPattern field has a default value of "none" but is not marked as notNull(). This could lead to inconsistent states where some records have NULL and others have "none" for the same semantic meaning.

Unless there's a specific reason to allow NULL (e.g., distinguishing legacy records), consider updating the schema:

- recurringPattern: text("recurring_pattern").default("none"),
+ recurringPattern: text("recurring_pattern").notNull().default("none"),

This would require regenerating the migration to include NOT NULL in the ALTER statement.

As per coding guidelines, database schema changes require generating migrations with npm run db:generate and npm run db:migrate.

hooks/useNoteActions.ts (1)

5-22: Consider using an options object for cleaner function signatures.

The createNote and updateNote functions now have 7 positional parameters each, with several optional ones at the end. This pattern can become unwieldy as more fields are added.

🔎 Suggested refactor using options object
 interface UseNoteActionsProps {
-  createNote: (
-    text: string,
-    date: Date,
-    onPasswordRequired: () => void,
-    type?: "note" | "event",
-    color?: string,
-    recurringPattern?: string,
-    recurringInterval?: number
-  ) => Promise<boolean>;
-  updateNote: (
-    id: string,
-    text: string,
-    onPasswordRequired: () => void,
-    type?: "note" | "event",
-    color?: string,
-    recurringPattern?: string,
-    recurringInterval?: number
-  ) => Promise<boolean>;
+  createNote: (
+    text: string,
+    date: Date,
+    onPasswordRequired: () => void,
+    options?: {
+      type?: "note" | "event";
+      color?: string;
+      recurringPattern?: string;
+      recurringInterval?: number;
+    }
+  ) => Promise<boolean>;
+  updateNote: (
+    id: string,
+    text: string,
+    onPasswordRequired: () => void,
+    options?: {
+      type?: "note" | "event";
+      color?: string;
+      recurringPattern?: string;
+      recurringInterval?: number;
+    }
+  ) => Promise<boolean>;
components/calendar-grid.tsx (1)

134-152: Potential O(n²) complexity in recurring event matching.

The current implementation calls findEventForDate(notes, day) inside the filter callback for each note, which iterates through all notes again. This results in O(n²) complexity per day.

🔎 Suggested optimization
         // Find notes/events for this day using new event-utils
-        const matchingNotes = notes.filter((note) => {
-          if (!note.date) return false;
-          const noteDate = new Date(note.date);
-
-          // Always match exact date for both notes and events
-          if (isSameDay(noteDate, day)) return true;
-
-          // For events with recurring patterns, use findEventForDate
-          if (
-            note.type === "event" &&
-            note.recurringPattern &&
-            note.recurringPattern !== "none"
-          ) {
-            const foundEvent = findEventForDate(notes, day);
-            return foundEvent?.id === note.id;
-          }
-
-          return false;
-        });
+        // Find the event for this day (handles recurring) - O(n)
+        const dayEvent = findEventForDate(notes, day);
+        
+        // Find notes that match this exact day - O(n)
+        const dayNote = notes.find((note) => {
+          if (!note.date || note.type !== "note") return false;
+          return isSameDay(new Date(note.date), day);
+        });

This eliminates the need for matchingNotes entirely since you only use dayNote and dayEvent anyway.

components/note-sheet.tsx (1)

343-352: Consider dynamic max value based on unit.

The max={52} works well for weeks (≈1 year), but for months it allows intervals up to 52 months. Consider adjusting dynamically if you want to constrain monthly intervals differently.

🔎 Optional: Dynamic max based on unit
                   <Input
                     type="number"
                     min={1}
-                    max={52}
+                    max={recurringUnit === "weeks" ? 52 : 24}
                     value={recurringInterval}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aa1c7b8 and 34b2ea0.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (20)
  • README.md (1 hunks)
  • app/api/notes/[id]/route.ts (2 hunks)
  • app/api/notes/route.ts (2 hunks)
  • app/page.tsx (6 hunks)
  • components/calendar-grid.tsx (4 hunks)
  • components/calendar-sheet.tsx (1 hunks)
  • components/dialog-manager.tsx (1 hunks)
  • components/note-sheet.tsx (4 hunks)
  • components/ui/radio-group.tsx (1 hunks)
  • drizzle/0012_organic_romulus.sql (1 hunks)
  • drizzle/meta/0012_snapshot.json (1 hunks)
  • drizzle/meta/_journal.json (1 hunks)
  • hooks/useNoteActions.ts (2 hunks)
  • hooks/useNotes.ts (4 hunks)
  • lib/db/schema.ts (1 hunks)
  • lib/event-utils.ts (1 hunks)
  • messages/de.json (1 hunks)
  • messages/en.json (1 hunks)
  • messages/it.json (1 hunks)
  • package.json (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
app/api/**/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

app/api/**/*.ts: Use Next.js 16 App Router with async dynamic route params - always await params in route handlers
Password protection uses two-tier system: check isLocked flag for read operations (GET), and passwordHash for all write operations (POST/PUT/DELETE)

Files:

  • app/api/notes/[id]/route.ts
  • app/api/notes/route.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use English for all code, comments, and variable names
Write comments only for complex or non-obvious logic - prefer self-documenting variable names

Files:

  • app/api/notes/[id]/route.ts
  • components/ui/radio-group.tsx
  • hooks/useNotes.ts
  • components/calendar-grid.tsx
  • lib/event-utils.ts
  • app/api/notes/route.ts
  • app/page.tsx
  • hooks/useNoteActions.ts
  • components/calendar-sheet.tsx
  • components/note-sheet.tsx
  • components/dialog-manager.tsx
  • lib/db/schema.ts
hooks/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Create custom hooks in hooks/ directory for CRUD operations following existing patterns in available custom hooks

Files:

  • hooks/useNotes.ts
  • hooks/useNoteActions.ts
app/**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

app/**/*.tsx: Always use existing custom hooks from hooks/ instead of implementing custom fetch logic for CRUD operations on shifts, presets, notes, and calendars
Use getCachedPassword(), verifyAndCachePassword(), setCachedPassword(), and removeCachedPassword() from lib/password-cache.ts for password handling
Always use useDirtyState hook for tracking unsaved changes in forms and sheets
Use PRESET_COLORS array and store colors in hex format (#RRGGBB), applying 20% opacity for backgrounds using format ${color}20
Use formatDateToLocal() utility for formatting dates to YYYY-MM-DD format in UI
Use ConfirmationDialog component for confirmations - never use native JavaScript confirm()
Support real-time updates via Server-Sent Events (SSE) - listen to relevant SSE events and use silent refresh patterns (fetchData(false)) to update without loading states
Use Tailwind CSS 4, shadcn/ui components, and gradient styling patterns: gradient backgrounds (from-primary/10 via-primary/5), border opacity (border-border/50), consistent padding (px-6 py-6), and sticky footers
Use separate mobile calendar dialog with showMobileCalendarDialog prop for improved mobile UI/UX

Files:

  • app/page.tsx
messages/*.json

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

messages/*.json: Use next-intl for internationalization with messages in messages/{de,en,it}.json - add new keys to all three language files
Organize translation keys in messages/*.json as: common.* for CRUD operations with {item} parameter, validation.* for validation messages, form.* for reusable form labels, and feature-specific namespaces for feature keys

Files:

  • messages/de.json
  • messages/it.json
  • messages/en.json
messages/de.json

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

German translations must ALWAYS use informal 'du' form, never 'Sie'

Files:

  • messages/de.json
lib/db/schema.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

lib/db/schema.ts: Generate database migrations using npm run db:generate and npm run db:migrate - never use db:push
Store SQLite timestamps as integers using { mode: "timestamp" } which are auto-converted to Date objects
Use crypto.randomUUID() for all ID generation in database schema
Database schema changes require updating lib/db/schema.ts AND generating migrations - create API routes for new tables at app/api/tablename/route.ts and app/api/tablename/[id]/route.ts
Implement cascade delete relationships in database schema - deleting a calendar removes all related shifts, presets, and notes

Files:

  • lib/db/schema.ts
🧠 Learnings (13)
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/calendar*.tsx : Calendar interactions: left-click toggles shift with selected preset (delete if exists, create if not), right-click opens note dialog with prevented default context menu

Applied to files:

  • README.md
  • components/calendar-grid.tsx
  • app/page.tsx
  • messages/de.json
  • hooks/useNoteActions.ts
  • components/calendar-sheet.tsx
  • components/note-sheet.tsx
  • components/dialog-manager.tsx
  • messages/en.json
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*.tsx : Use Tailwind CSS 4, shadcn/ui components, and gradient styling patterns: gradient backgrounds (`from-primary/10 via-primary/5`), border opacity (`border-border/50`), consistent padding (`px-6 py-6`), and sticky footers

Applied to files:

  • package.json
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to lib/db/schema.ts : Database schema changes require updating `lib/db/schema.ts` AND generating migrations - create API routes for new tables at `app/api/tablename/route.ts` and `app/api/tablename/[id]/route.ts`

Applied to files:

  • app/api/notes/[id]/route.ts
  • lib/db/schema.ts
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*.tsx : Use separate mobile calendar dialog with `showMobileCalendarDialog` prop for improved mobile UI/UX

Applied to files:

  • components/calendar-grid.tsx
  • app/page.tsx
  • components/calendar-sheet.tsx
  • components/dialog-manager.tsx
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*.tsx : Always use existing custom hooks from `hooks/` instead of implementing custom fetch logic for CRUD operations on shifts, presets, notes, and calendars

Applied to files:

  • components/calendar-grid.tsx
  • app/page.tsx
  • hooks/useNoteActions.ts
  • components/note-sheet.tsx
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*.tsx : Use `formatDateToLocal()` utility for formatting dates to YYYY-MM-DD format in UI

Applied to files:

  • components/calendar-grid.tsx
  • lib/event-utils.ts
  • app/page.tsx
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*.tsx : Use `getCachedPassword()`, `verifyAndCachePassword()`, `setCachedPassword()`, and `removeCachedPassword()` from `lib/password-cache.ts` for password handling

Applied to files:

  • app/page.tsx
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to messages/*.json : Use next-intl for internationalization with messages in `messages/{de,en,it}.json` - add new keys to all three language files

Applied to files:

  • messages/de.json
  • messages/it.json
  • messages/en.json
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to messages/de.json : German translations must ALWAYS use informal 'du' form, never 'Sie'

Applied to files:

  • messages/de.json
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*sheet*.tsx : Use `BaseSheet` component for simple forms (create, edit, settings) with gradient headers, border opacity styling, and sticky footer

Applied to files:

  • components/note-sheet.tsx
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*.tsx : Always use `useDirtyState` hook for tracking unsaved changes in forms and sheets

Applied to files:

  • components/note-sheet.tsx
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to lib/db/schema.ts : Implement cascade delete relationships in database schema - deleting a calendar removes all related shifts, presets, and notes

Applied to files:

  • lib/db/schema.ts
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to messages/*.json : Organize translation keys in `messages/*.json` as: `common.*` for CRUD operations with `{item}` parameter, `validation.*` for validation messages, `form.*` for reusable form labels, and feature-specific namespaces for feature keys

Applied to files:

  • messages/en.json
🧬 Code graph analysis (5)
components/ui/radio-group.tsx (1)
lib/utils.ts (1)
  • cn (4-6)
hooks/useNotes.ts (1)
lib/password-cache.ts (1)
  • getCachedPassword (9-14)
components/calendar-grid.tsx (1)
lib/event-utils.ts (1)
  • findEventForDate (76-101)
app/page.tsx (3)
lib/event-utils.ts (2)
  • findNoteForDate (103-132)
  • findEventForDate (76-101)
components/empty-calendar-state.tsx (1)
  • EmptyCalendarState (11-59)
components/dialog-manager.tsx (1)
  • DialogManager (113-258)
components/note-sheet.tsx (2)
components/ui/base-sheet.tsx (1)
  • BaseSheet (41-142)
lib/constants.ts (1)
  • PRESET_COLORS (8-17)
🪛 Biome (2.1.2)
lib/event-utils.ts

[error] 51-51: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 53-53: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 56-59: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Agent
  • GitHub Check: build-dev
🔇 Additional comments (25)
README.md (1)

40-40: LGTM! Documentation accurately reflects the new event functionality.

The updated Quick Actions description now correctly mentions that right-click can add notes or events, aligning with the broader event support introduced in this PR.

drizzle/meta/_journal.json (1)

89-95: LGTM! Migration journal entry is properly structured.

The new journal entry for migration 0012_organic_romulus follows the established pattern and correctly accompanies the schema changes for event support.

components/ui/radio-group.tsx (1)

1-44: LGTM! Well-structured RadioGroup components.

The RadioGroup and RadioGroupItem components follow the established shadcn/ui pattern with proper:

  • forwardRef implementation for ref handling
  • TypeScript typing from Radix primitives
  • displayName for debugging
  • Tailwind styling with the cn utility

These components provide the foundation for the new note/event type selection UI.

messages/it.json (1)

121-139: LGTM! Italian translations properly support the new event and recurring features.

The translation keys are well-organized and cover all aspects of the new functionality:

  • Type selection (note vs. event)
  • Event-specific fields (color, title)
  • Recurring patterns and custom intervals

The use of {item} parameter in create and edit keys provides good flexibility for reuse.

As per coding guidelines, new translation keys should be added to all three language files (de, en, it).

messages/de.json (1)

121-139: LGTM! German translations correctly use informal 'du' form and cover all new features.

The translations properly implement:

  • Informal "du" form as required by guidelines (e.g., "drücke", "mache")
  • Consistent key structure with other language files
  • Complete coverage of event and recurring functionality

As per coding guidelines, German translations must always use informal 'du' form, never 'Sie'.

drizzle/0012_organic_romulus.sql (1)

1-4: LGTM! Migration correctly implements the schema changes.

The migration adds four new columns to support events and recurring items:

  • type with NOT NULL constraint and default value ensures backward compatibility
  • color and recurring_interval are appropriately nullable
  • recurring_pattern has a default but no NOT NULL constraint (consistent with schema.ts)

Note: The recurring_pattern nullability observation from lib/db/schema.ts applies here as well. If that field is updated to NOT NULL, remember to regenerate this migration.

As per coding guidelines, migrations should be generated using npm run db:generate and applied with npm run db:migrate.

package.json (1)

28-28: No action needed — dependency version is current.

The @radix-ui/react-radio-group dependency is at version 1.3.8, the latest stable release. The caret range specified is appropriate for dependency management.

components/calendar-sheet.tsx (1)

20-20: LGTM!

Extending the return type to support both synchronous and asynchronous submission aligns well with the expanded note/event submission flow across the PR.

drizzle/meta/0012_snapshot.json (1)

38-67: LGTM!

The snapshot correctly reflects the schema additions for event metadata (type, color, recurring_pattern, recurring_interval) with appropriate defaults and nullability constraints.

app/api/notes/route.ts (1)

85-94: LGTM!

The POST handler correctly extends the request body to accept event metadata and applies sensible defaults (type="note", recurringPattern="none") that align with the database schema.

Also applies to: 132-135

hooks/useNotes.ts (2)

43-97: LGTM!

The createNote function correctly extends its signature and request payload to support event metadata, and dynamically adjusts toast messages based on item type.


100-150: LGTM!

The updateNote function correctly extends its signature and request payload to support event metadata, with consistent error handling and dynamic toast messages.

components/dialog-manager.tsx (1)

103-109: LGTM!

The onNoteSubmit callback signature is correctly extended to accept event metadata parameters, aligning with the broader API changes across the PR.

app/page.tsx (4)

150-186: LGTM!

The handleNoteSubmit wrapper correctly extends the signature, forwards event metadata to noteActions.handleNoteSubmit, and reloads compare-mode data to reflect changes.


458-473: LGTM!

Replacing ad-hoc note lookups with findNoteForDate ensures consistent handling of both exact-date and recurring notes/events across all interaction paths (right-click, note icon, long press).


711-712: LGTM!

Using findEventForDate in compare mode correctly locates the original event for recurring instances, ensuring users edit the source event rather than creating duplicates.

Also applies to: 730-731


1080-1149: LGTM!

Wrapping EmptyCalendarState with DialogManager ensures the calendar creation dialog is accessible even when no calendars exist, improving the onboarding flow.

hooks/useNoteActions.ts (1)

37-94: LGTM!

The handleNoteSubmit callback correctly forwards all event-related parameters (type, color, recurringPattern, recurringInterval) to both createNote and updateNote, and the dependency array is complete.

components/calendar-grid.tsx (2)

210-225: LGTM!

The event border styling is correctly applied via inline styles when an event exists, and the conditional class handling properly defers to the inline style when dayEvent is present.


244-282: LGTM!

The event and note title rendering is well-implemented with:

  • Proper click handling that respects selectedPresetId state
  • Event propagation correctly stopped to prevent day click
  • Appropriate truncation and hover states for interactive elements
  • Title attributes for accessibility/tooltips
components/note-sheet.tsx (4)

27-33: LGTM!

The expanded onSubmit signature correctly supports the new event metadata fields while maintaining backward compatibility through optional parameters.


74-126: LGTM!

The initialization logic correctly:

  • Extracts the unit from compound patterns like "custom-weeks"
  • Captures initial state for change detection in edit mode
  • Resets all fields to defaults when the sheet closes

257-308: LGTM!

The type selection and color picker UI are well-implemented with:

  • Accessible radio buttons with proper labels
  • Color buttons using aria-label for screen readers
  • Clear visual feedback with the checkmark icon for the selected color

371-387: LGTM!

The note/event text input correctly adapts its label and placeholder based on the selected type, maintaining consistent UX while supporting both use cases.

messages/en.json (1)

120-140: German and Italian translation files properly include all note keys.

All new note.* keys are present in messages/de.json and messages/it.json with complete localizations. The keys are properly organized under the note namespace and follow the coding guidelines with correct {item} parameter usage for dynamic content.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds first-class event support to the notes feature, enabling users to create colored, recurring events that are visually distinguished from regular notes in the calendar. The implementation extends the existing notes data model with type discrimination, color properties, and flexible recurring patterns (custom weekly/monthly intervals).

Key changes:

  • Database schema extended with event-specific fields (type, color, recurringPattern, recurringInterval) and corresponding migration
  • New recurring event matching logic supporting yearly, quarterly, half-yearly, monthly, and custom interval patterns
  • Enhanced note editor UI with type selection (note/event), color picker, and recurring pattern configuration
  • Calendar grid rendering updated to display event borders and titles inline with dates

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
package.json, package-lock.json Adds @radix-ui/react-radio-group dependency for type selection UI
messages/{de,en,it}.json Adds translations for event-specific UI elements (type, color, recurring patterns)
lib/event-utils.ts New utility module implementing recurring event date matching logic
lib/db/schema.ts Extends calendarNotes table with type, color, and recurring pattern fields
drizzle/meta/_journal.json, drizzle/meta/0012_snapshot.json, drizzle/0012_organic_romulus.sql Database migration files for schema changes
components/ui/radio-group.tsx New shadcn/ui component for note/event type selection
components/note-sheet.tsx Major refactor to support event configuration (type, color, recurring patterns)
components/dialog-manager.tsx Updates onNoteSubmit signature to accept event parameters
components/calendar-sheet.tsx Type signature adjustment for async callback
components/calendar-grid.tsx Updates rendering logic to display event borders and titles, replaces sticky note icon with inline text
hooks/useNotes.ts Extends createNote and updateNote to accept event-specific parameters
hooks/useNoteActions.ts Updates submission flow to pass event parameters through
app/page.tsx Integrates findEventForDate and findNoteForDate utilities, adds DialogManager to empty state
app/api/notes/route.ts POST endpoint accepts and stores event parameters
app/api/notes/[id]/route.ts PUT endpoint accepts and updates event parameters
README.md Updates feature description to mention event support

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 214bec0 and 720bc4e.

📒 Files selected for processing (10)
  • app/page.tsx (9 hunks)
  • components/calendar-grid.tsx (5 hunks)
  • components/dialog-manager.tsx (3 hunks)
  • components/notes-list-dialog.tsx (1 hunks)
  • components/ui/badge.tsx (1 hunks)
  • hooks/useDialogStates.ts (4 hunks)
  • lib/event-utils.ts (1 hunks)
  • messages/de.json (1 hunks)
  • messages/en.json (1 hunks)
  • messages/it.json (2 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use English for all code, comments, and variable names
Write comments only for complex or non-obvious logic - prefer self-documenting variable names

Files:

  • components/ui/badge.tsx
  • app/page.tsx
  • components/notes-list-dialog.tsx
  • components/calendar-grid.tsx
  • components/dialog-manager.tsx
  • hooks/useDialogStates.ts
  • lib/event-utils.ts
app/**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

app/**/*.tsx: Always use existing custom hooks from hooks/ instead of implementing custom fetch logic for CRUD operations on shifts, presets, notes, and calendars
Use getCachedPassword(), verifyAndCachePassword(), setCachedPassword(), and removeCachedPassword() from lib/password-cache.ts for password handling
Always use useDirtyState hook for tracking unsaved changes in forms and sheets
Use PRESET_COLORS array and store colors in hex format (#RRGGBB), applying 20% opacity for backgrounds using format ${color}20
Use formatDateToLocal() utility for formatting dates to YYYY-MM-DD format in UI
Use ConfirmationDialog component for confirmations - never use native JavaScript confirm()
Support real-time updates via Server-Sent Events (SSE) - listen to relevant SSE events and use silent refresh patterns (fetchData(false)) to update without loading states
Use Tailwind CSS 4, shadcn/ui components, and gradient styling patterns: gradient backgrounds (from-primary/10 via-primary/5), border opacity (border-border/50), consistent padding (px-6 py-6), and sticky footers
Use separate mobile calendar dialog with showMobileCalendarDialog prop for improved mobile UI/UX

Files:

  • app/page.tsx
messages/*.json

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

messages/*.json: Use next-intl for internationalization with messages in messages/{de,en,it}.json - add new keys to all three language files
Organize translation keys in messages/*.json as: common.* for CRUD operations with {item} parameter, validation.* for validation messages, form.* for reusable form labels, and feature-specific namespaces for feature keys

Files:

  • messages/it.json
  • messages/de.json
  • messages/en.json
hooks/*.ts

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Create custom hooks in hooks/ directory for CRUD operations following existing patterns in available custom hooks

Files:

  • hooks/useDialogStates.ts
messages/de.json

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

German translations must ALWAYS use informal 'du' form, never 'Sie'

Files:

  • messages/de.json
🧠 Learnings (12)
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*.tsx : Use Tailwind CSS 4, shadcn/ui components, and gradient styling patterns: gradient backgrounds (`from-primary/10 via-primary/5`), border opacity (`border-border/50`), consistent padding (`px-6 py-6`), and sticky footers

Applied to files:

  • components/ui/badge.tsx
  • components/calendar-grid.tsx
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/calendar*.tsx : Calendar interactions: left-click toggles shift with selected preset (delete if exists, create if not), right-click opens note dialog with prevented default context menu

Applied to files:

  • app/page.tsx
  • components/notes-list-dialog.tsx
  • components/calendar-grid.tsx
  • components/dialog-manager.tsx
  • hooks/useDialogStates.ts
  • messages/de.json
  • messages/en.json
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*.tsx : Use separate mobile calendar dialog with `showMobileCalendarDialog` prop for improved mobile UI/UX

Applied to files:

  • app/page.tsx
  • components/notes-list-dialog.tsx
  • components/calendar-grid.tsx
  • components/dialog-manager.tsx
  • hooks/useDialogStates.ts
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*.tsx : Use `formatDateToLocal()` utility for formatting dates to YYYY-MM-DD format in UI

Applied to files:

  • app/page.tsx
  • components/calendar-grid.tsx
  • lib/event-utils.ts
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*.tsx : Always use existing custom hooks from `hooks/` instead of implementing custom fetch logic for CRUD operations on shifts, presets, notes, and calendars

Applied to files:

  • app/page.tsx
  • components/calendar-grid.tsx
  • components/dialog-manager.tsx
  • hooks/useDialogStates.ts
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*.tsx : Use `getCachedPassword()`, `verifyAndCachePassword()`, `setCachedPassword()`, and `removeCachedPassword()` from `lib/password-cache.ts` for password handling

Applied to files:

  • app/page.tsx
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*.tsx : Use `ConfirmationDialog` component for confirmations - never use native JavaScript `confirm()`

Applied to files:

  • components/notes-list-dialog.tsx
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to messages/*.json : Use next-intl for internationalization with messages in `messages/{de,en,it}.json` - add new keys to all three language files

Applied to files:

  • messages/it.json
  • messages/de.json
  • messages/en.json
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*sheet*.tsx : Use `BaseSheet` component for simple forms (create, edit, settings) with gradient headers, border opacity styling, and sticky footer

Applied to files:

  • components/dialog-manager.tsx
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*.tsx : Always use `useDirtyState` hook for tracking unsaved changes in forms and sheets

Applied to files:

  • hooks/useDialogStates.ts
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to messages/de.json : German translations must ALWAYS use informal 'du' form, never 'Sie'

Applied to files:

  • messages/de.json
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to messages/*.json : Organize translation keys in `messages/*.json` as: `common.*` for CRUD operations with `{item}` parameter, `validation.*` for validation messages, `form.*` for reusable form labels, and feature-specific namespaces for feature keys

Applied to files:

  • messages/en.json
🧬 Code graph analysis (5)
components/ui/badge.tsx (1)
lib/utils.ts (1)
  • cn (4-6)
components/calendar-grid.tsx (1)
lib/event-utils.ts (1)
  • findNotesForDate (90-119)
components/dialog-manager.tsx (2)
lib/db/schema.ts (1)
  • CalendarNote (164-164)
components/notes-list-dialog.tsx (1)
  • NotesListDialog (28-219)
hooks/useDialogStates.ts (1)
lib/db/schema.ts (1)
  • CalendarNote (164-164)
lib/event-utils.ts (1)
lib/db/schema.ts (1)
  • CalendarNote (164-164)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-dev
🔇 Additional comments (18)
components/ui/badge.tsx (1)

1-36: LGTM!

The Badge component follows standard shadcn/ui patterns with proper variant support, focus states, and transition styling. The implementation is clean and consistent with the project's UI component architecture.

hooks/useDialogStates.ts (1)

1-62: LGTM!

The additions properly extend the dialog state management to support the new notes list dialog. The state is correctly typed with CalendarNote[] and follows the existing patterns in the hook.

components/dialog-manager.tsx (2)

10-120: LGTM!

The DialogManager API is properly extended to support the notes list dialog. The onNoteSubmit signature now accepts event metadata (type, color, recurring pattern/interval), and the new notes list props are well-structured for handling per-date note operations.


257-267: LGTM!

The NotesListDialog is correctly rendered conditionally based on selectedDayDate and all props are properly wired to their respective handlers. The integration follows the established dialog management pattern.

components/notes-list-dialog.tsx (1)

1-219: LGTM!

The NotesListDialog component is well-designed with:

  • Clean separation of events and regular notes sections
  • Proper use of gradient styling patterns as per guidelines
  • Recurring labels displayed via Badge component
  • Locale-aware date formatting
  • Good UX with empty state, edit/delete actions, and Add button
  • Consistent with the project's dialog component architecture

Based on learnings, this follows Tailwind CSS 4, shadcn/ui components, and gradient styling patterns.

components/calendar-grid.tsx (3)

133-143: LGTM!

The note retrieval logic properly uses findNotesForDate to gather all notes/events for each day, including recurring items. The separation into events and regular notes with count tracking enables the enhanced day-level indicators. The O(n×d) complexity (notes × days) is reasonable for calendar rendering.


171-217: LGTM!

The multi-event border styling uses a creative backgroundImage gradient approach to support border-radius, since borderImage doesn't support it. This limitation is properly documented in the comments (lines 172-173). The implementation correctly applies:

  • Gradient borders for multiple events (excluding today)
  • Single-color borders for single events
  • Proper styling precedence (event borders override highlight styling)

240-306: LGTM!

The day header indicators are well-designed:

  • Multi-entry badge shows count when multiple notes/events exist
  • Single event/note displays with appropriate color styling (event color vs orange for notes)
  • Clickable interaction is properly gated on selectedPresetId to prevent conflicts with preset toggling
  • Clear visual hierarchy with truncation and opacity transitions
app/page.tsx (5)

150-268: LGTM!

The note submission and list handlers are well-implemented:

  • handleNoteSubmit properly extended to accept event metadata (type, color, recurring pattern/interval)
  • Compare mode data correctly reloaded after mutations
  • handleEditNoteFromList smoothly transitions from list to edit dialog
  • handleDeleteNoteFromList properly updates state and closes dialog when no notes remain
  • All handlers maintain data consistency across normal and compare modes

511-556: LGTM!

The day interaction handlers consistently use findNotesForDate to retrieve all notes/events for a date:

  • Right-click, note icon click, and long-press all follow the same pattern
  • Shows notes list dialog when notes exist (threshold >= 1), allowing users to add more
  • Opens direct edit dialog when no notes exist
  • Properly centralizes per-date note retrieval including recurring items

784-852: LGTM!

The compare mode interaction handlers properly adapt the note workflow for multi-calendar scenarios:

  • Use findNotesForDate on the specific calendar's notes
  • Set compareNoteCalendarId to track which calendar is being edited
  • Follow the same threshold pattern as normal mode
  • Data reloading is handled by the note submission wrappers

1204-1280: LGTM!

The empty state now properly renders the DialogManager, enabling calendar creation and other dialog operations even when no calendars exist. This improves UX by ensuring consistent dialog availability across all app states.


1477-1482: LGTM!

All three DialogManager instances (empty state, compare mode, and main) consistently pass the new notes list dialog props. The integration is complete and ensures uniform note/event management behavior across all application modes.

lib/event-utils.ts (2)

3-59: LGTM!

The matchesRecurringEvent function correctly implements recurring pattern logic with fixes for previously identified issues:

  • Switch case declarations are now properly wrapped in blocks (lines 23-40, 42-54)
  • Month-based patterns use year-aware calculation (eventYear * 12 + eventMonth) to correctly handle year boundaries (lines 49-51)
  • Week-based patterns normalize dates to midnight before calculating day differences
  • Both patterns verify targetDate >= eventDate before checking intervals

The implementation properly addresses all past review concerns.


61-134: LGTM!

The find functions are well-designed:

  • findEventsForDate filters for event-type notes with exact or recurring matches
  • findNotesForDate filters all notes with exact matches, plus recurring matches for events only (line 108)
  • Exact date matching properly compares year, month, and date
  • Legacy wrappers provide backward compatibility by returning the first match
  • Clear separation of concerns between event-specific and general note retrieval
messages/de.json (2)

121-126: LGTM! Proper use of informal 'du' form and template parameters.

The updated translations correctly use the informal imperative forms ("drücke", "mache") as required by the coding guidelines. The template-based format for create/edit strings with {item} parameter follows the proper pattern for reusable translations.


127-143: No action needed. The Italian translation file (messages/it.json) is present in the repository and already contains all the new keys required by this change. The coding guideline requirement to add new keys to all three language files (messages/{de,en,it}.json) has been satisfied.

Likely an incorrect or invalid review comment.

messages/en.json (1)

121-143: LGTM! English translations are clear and consistent with German file.

The English translations follow the same structure and key organization as the German file, properly using template parameters for dynamic values. The text is natural and grammatically correct.

Note: The missing Italian translations have already been flagged in the messages/de.json review.

@panteLx panteLx mentioned this pull request Dec 21, 2025
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 23 out of 24 changed files in this pull request and generated 7 comments.

Comments suppressed due to low confidence (1)

app/api/notes/[id]/route.ts:144

  • Similar to the POST route, the PUT route lacks validation for the type, recurringPattern, and recurringInterval fields. The type should be validated to be either "note" or "event", the recurringPattern should be validated to be one of the expected values, and recurringInterval should be validated as a positive integer when recurringPattern is custom.
    // Determine the final type value
    const finalType = type !== undefined ? type : existingNote.type;

    const [updated] = await db
      .update(calendarNotes)
      .set({
        note,
        type: finalType,
        // Clear event-specific fields when converting to note
        color:
          finalType === "note"
            ? null
            : color !== undefined
            ? color
            : existingNote.color,
        recurringPattern:
          finalType === "note"
            ? "none"
            : recurringPattern !== undefined
            ? recurringPattern
            : existingNote.recurringPattern,
        recurringInterval:
          finalType === "note"
            ? null
            : recurringInterval !== undefined
            ? recurringInterval
            : existingNote.recurringInterval,
        updatedAt: new Date(),
      })
      .where(eq(calendarNotes.id, id))
      .returning();

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
components/note-sheet.tsx (1)

357-366: Validate and clamp interval input.

The interval input accepts values from 1-52 via the max attribute, but the onChange handler doesn't enforce this limit. Users can type values exceeding 52 (e.g., 999), which bypasses the intended constraint.

🔎 Proposed fix to clamp input value
                  <Input
                    type="number"
                    min={1}
                    max={52}
                    value={recurringInterval}
-                   onChange={(e) =>
-                     setRecurringInterval(parseInt(e.target.value) || 1)
-                   }
+                   onChange={(e) => {
+                     const raw = parseInt(e.target.value, 10);
+                     if (Number.isNaN(raw)) {
+                       setRecurringInterval(1);
+                       return;
+                     }
+                     setRecurringInterval(Math.min(Math.max(raw, 1), 52));
+                   }}
                    className="w-20"
                  />
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 720bc4e and 0a00aec.

📒 Files selected for processing (8)
  • components/calendar-compare-sheet.tsx (2 hunks)
  • components/calendar-compare-view.tsx (2 hunks)
  • components/note-sheet.tsx (4 hunks)
  • components/ui/alert.tsx (1 hunks)
  • lib/event-utils.ts (1 hunks)
  • messages/de.json (1 hunks)
  • messages/en.json (1 hunks)
  • messages/it.json (3 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use English for all code, comments, and variable names
Write comments only for complex or non-obvious logic - prefer self-documenting variable names

Files:

  • components/calendar-compare-sheet.tsx
  • components/ui/alert.tsx
  • components/calendar-compare-view.tsx
  • components/note-sheet.tsx
  • lib/event-utils.ts
messages/*.json

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

messages/*.json: Use next-intl for internationalization with messages in messages/{de,en,it}.json - add new keys to all three language files
Organize translation keys in messages/*.json as: common.* for CRUD operations with {item} parameter, validation.* for validation messages, form.* for reusable form labels, and feature-specific namespaces for feature keys

Files:

  • messages/en.json
  • messages/de.json
  • messages/it.json
messages/de.json

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

German translations must ALWAYS use informal 'du' form, never 'Sie'

Files:

  • messages/de.json
🧠 Learnings (12)
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*.tsx : Use separate mobile calendar dialog with `showMobileCalendarDialog` prop for improved mobile UI/UX

Applied to files:

  • components/calendar-compare-sheet.tsx
  • components/calendar-compare-view.tsx
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*sheet*.tsx : Use `BaseSheet` component for simple forms (create, edit, settings) with gradient headers, border opacity styling, and sticky footer

Applied to files:

  • components/calendar-compare-sheet.tsx
  • components/note-sheet.tsx
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/calendar*.tsx : Calendar interactions: left-click toggles shift with selected preset (delete if exists, create if not), right-click opens note dialog with prevented default context menu

Applied to files:

  • components/calendar-compare-sheet.tsx
  • messages/en.json
  • components/calendar-compare-view.tsx
  • components/note-sheet.tsx
  • messages/de.json
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*.tsx : Use Tailwind CSS 4, shadcn/ui components, and gradient styling patterns: gradient backgrounds (`from-primary/10 via-primary/5`), border opacity (`border-border/50`), consistent padding (`px-6 py-6`), and sticky footers

Applied to files:

  • components/ui/alert.tsx
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*.tsx : Use `ConfirmationDialog` component for confirmations - never use native JavaScript `confirm()`

Applied to files:

  • components/ui/alert.tsx
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to messages/*.json : Use next-intl for internationalization with messages in `messages/{de,en,it}.json` - add new keys to all three language files

Applied to files:

  • messages/en.json
  • messages/de.json
  • messages/it.json
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to messages/*.json : Organize translation keys in `messages/*.json` as: `common.*` for CRUD operations with `{item}` parameter, `validation.*` for validation messages, `form.*` for reusable form labels, and feature-specific namespaces for feature keys

Applied to files:

  • messages/en.json
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*.tsx : Always use existing custom hooks from `hooks/` instead of implementing custom fetch logic for CRUD operations on shifts, presets, notes, and calendars

Applied to files:

  • components/calendar-compare-view.tsx
  • components/note-sheet.tsx
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*.tsx : Use `getCachedPassword()`, `verifyAndCachePassword()`, `setCachedPassword()`, and `removeCachedPassword()` from `lib/password-cache.ts` for password handling

Applied to files:

  • components/calendar-compare-view.tsx
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*.tsx : Always use `useDirtyState` hook for tracking unsaved changes in forms and sheets

Applied to files:

  • components/note-sheet.tsx
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to messages/de.json : German translations must ALWAYS use informal 'du' form, never 'Sie'

Applied to files:

  • messages/de.json
📚 Learning: 2025-12-20T20:00:42.991Z
Learnt from: CR
Repo: panteLx/BetterShift PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-20T20:00:42.991Z
Learning: Applies to app/**/*.tsx : Use `formatDateToLocal()` utility for formatting dates to YYYY-MM-DD format in UI

Applied to files:

  • lib/event-utils.ts
🧬 Code graph analysis (3)
components/calendar-compare-sheet.tsx (1)
components/ui/alert.tsx (2)
  • Alert (58-58)
  • AlertDescription (58-58)
components/calendar-compare-view.tsx (1)
components/ui/alert.tsx (2)
  • Alert (58-58)
  • AlertDescription (58-58)
lib/event-utils.ts (1)
lib/db/schema.ts (1)
  • CalendarNote (164-164)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-dev
🔇 Additional comments (12)
components/ui/alert.tsx (1)

1-58: LGTM! Standard shadcn/ui Alert component.

This is a well-implemented shadcn/ui Alert component following established patterns. The variant system, accessibility attributes (role="alert"), and forwardRef pattern are all correctly applied.

lib/event-utils.ts (2)

3-70: LGTM! Recurring event matching logic is correct.

The function properly handles:

  • Weekly recurrence with custom intervals (accounting for day-of-week and normalized dates)
  • Monthly recurrence with custom intervals (year-aware month calculation)
  • Edge cases where event day doesn't exist in target month (e.g., Jan 31 → Feb 28/29)

Past review concerns about year boundaries and switch declaration scoping have been addressed.


73-145: LGTM! Date filtering functions are well-structured.

The helper functions correctly filter notes/events by date, handling both exact matches and recurring patterns. The legacy wrappers maintain backward compatibility by returning the first match.

messages/it.json (1)

121-144: LGTM! Italian translations are correct.

The Italian translations for the new note and event features are accurate. The past issue with Spanish words ("Notas", "Eventos") has been corrected to proper Italian ("Note", "Eventi").

messages/de.json (1)

121-144: LGTM! German translations use correct informal 'du' form.

The German translations for note and event features properly use the informal 'du' form throughout (e.g., "drücke", "mache"), consistent with the project's translation guidelines.

components/calendar-compare-sheet.tsx (1)

46-53: LGTM! Consistent Alert component usage.

The mobile warning has been updated to use the standardized Alert component with appropriate warning styling and iconography.

components/calendar-compare-view.tsx (1)

157-164: LGTM! Consistent Alert component usage.

The mobile warning implementation matches the pattern used in calendar-compare-sheet, maintaining consistency across the codebase.

messages/en.json (1)

121-144: LGTM! English translations are well-structured.

The translation keys follow the project's organization guidelines with feature-specific namespaces and reusable parameters like {item} and {count}.

components/note-sheet.tsx (4)

28-72: LGTM! Comprehensive state management for notes and events.

The extended onSubmit signature and state management properly support the new event features (type, color, recurring patterns) while maintaining a complete initial state reference for accurate change detection.


75-144: LGTM! Initialization and change detection are thorough.

The initialization effect correctly normalizes recurring patterns (e.g., "custom-weeks" → "custom" + "weeks"), and change detection properly compares all fields in the composite state.


146-170: LGTM! Save handler correctly builds recurring pattern.

The save handler properly reconstructs the pattern format (e.g., "custom" + "weeks" → "custom-weeks") and conditionally passes only relevant parameters based on the note type.


239-401: LGTM! Note/event editor UI is well-structured.

The UI correctly adapts based on the selected type (note vs. event), showing appropriate fields for event metadata (color picker, recurring pattern) and using contextual labels and placeholders.

@panteLx panteLx merged commit fb4a407 into main Dec 21, 2025
3 checks passed
@panteLx panteLx deleted the feat/events-in-notes branch December 21, 2025 21:06
@coderabbitai coderabbitai bot mentioned this pull request Dec 27, 2025
6 tasks
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.

feature request: Put the holidays in red

1 participant