Skip to content

Conversation

@domhhv
Copy link
Owner

@domhhv domhhv commented Jan 28, 2026

Moved calendar filter state management from local component state to a global store via calendarFilters and related actions. Removed MonthCalendarHeader and integrated filter visibility logic into MonthCalendarNavigation and MonthCalendarFilters. Updated filtering logic to use arrays instead of Sets, and ensured filters are updated when habits or traits change. This improves consistency and enables filter state sharing across components.

Summary by CodeRabbit

  • New Features

    • New separate navigation and filter panels for the calendar with animated, responsive filter presentation on mobile/desktop.
    • Persisted calendar filters with read/write controls and updated filter toggles.
  • Refactor

    • Calendar range now recalculates from the focused date and locale for more predictable month/week views.
    • Filters moved to centralized state and UI; habit/trait selections show grouped items and icons.
  • Bug Fixes

    • Filters auto-adjust when habit or trait lists change.

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

Moved calendar filter state management from local component state to a global store via `calendarFilters` and related actions. Removed MonthCalendarHeader and integrated filter visibility logic into MonthCalendarNavigation and MonthCalendarFilters. Updated filtering logic to use arrays instead of Sets, and ensured filters are updated when habits or traits change. This improves consistency and enables filter state sharing across components.
@domhhv domhhv self-assigned this Jan 28, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 28, 2026

📝 Walkthrough

Walkthrough

This PR extracts MonthCalendarHeader into MonthCalendarNavigation and MonthCalendarFilters, moves calendar filter state into the UI store (arrays, new hooks), removes user-driven initialization from month/week calendars, and changes MonthCalendarGrid to consume filters via hooks instead of props.

Changes

Cohort / File(s) Summary
Month calendar composition
src/components/calendar/MonthCalendar.tsx
Replaced MonthCalendarHeader with a layout that renders MonthCalendarNavigation and MonthCalendarFilters; simplified range effect to derive range from state.focusedDate.
Header split → Navigation & Filters
src/components/calendar/MonthCalendarNavigation.tsx, src/components/calendar/MonthCalendarFilters.tsx, src/components/calendar/MonthCalendarHeader.tsx
Deleted MonthCalendarHeader. Introduced MonthCalendarNavigation (now only accepts state) and MonthCalendarFilters (now self-contained, no props). Filter toggle and rendering moved into these components; MonthCalendarHeader export removed.
Grid consumes internal filters
src/components/calendar/MonthCalendarGrid.tsx
Removed filters prop from Grid props; Grid now calls useCalendarFilters() and switches Set lookups to array .includes().
Week calendar simplification
src/components/calendar/WeekCalendar.tsx
Removed user-based initialization and local fetchedWeekYear; range now computed from focusedDate and locale/firstDayOfWeek.
UI store: calendar filters & hooks
src/stores/ui.store.ts
Added CalendarFilters type, calendarFilters state, changeCalendarFilters mutation, and exported hooks useCalendarFilters and useCalendarFiltersChange.
Habits/Traits store sync to filters
src/stores/habits.store.ts, src/stores/traits.store.ts
After fetching, stores update state.calendarFilters.habitIds / traitIds (initialize or intersect existing ids to keep filters in sync).

Sequence Diagram(s)

mermaid
sequenceDiagram
participant User
participant MonthCalendarNavigation
participant UiStore
participant MonthCalendarFilters
participant MonthCalendarGrid

User->>MonthCalendarNavigation: click prev/next or toggle filter
MonthCalendarNavigation->>UiStore: changeCalendarRange / toggle isShownOnMobile
Note over UiStore: calendarRange / calendarFilters updated
User->>MonthCalendarFilters: open / select habits or traits
MonthCalendarFilters->>UiStore: changeCalendarFilters(updatedFilters)
UiStore->>MonthCalendarGrid: filters updated (subscribed)
MonthCalendarGrid->>UiStore: request occurrences using new filters

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰
I hopped through code with tiny feet,
Split header paths and made them neat,
Filters nest within the store,
Arrays dance where Sets were before,
A cheerful hop — the calendar's complete! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main refactoring work: moving calendar filter state management from local component state to a Zustand store, which is the primary architectural change across multiple files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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.

@relativeci
Copy link

relativeci bot commented Jan 28, 2026

#176 Bundle Size — 1.82MiB (-0.02%).

973e080(current) vs ed681fa main#174(baseline)

Warning

Bundle contains 2 duplicate packages – View duplicate packages

Bundle metrics  Change 3 changes Improvement 1 improvement
                 Current
#176
     Baseline
#174
Improvement  Initial JS 1.08MiB(-0.03%) 1.08MiB
No change  Initial CSS 0B 0B
Change  Cache Invalidation 87.1% 87.09%
No change  Chunks 8 8
No change  Assets 9 9
Change  Modules 6672(-0.01%) 6673
No change  Duplicate Modules 0 0
No change  Duplicate Code 0% 0%
No change  Packages 223 223
No change  Duplicate Packages 2 2
Bundle size by type  Change 1 change Improvement 1 improvement
                 Current
#176
     Baseline
#174
Improvement  JS 1.59MiB (-0.02%) 1.59MiB
No change  CSS 241.08KiB 241.08KiB

Bundle analysis reportBranch refactor/global-filters-storeProject dashboard


Generated by RelativeCIDocumentationReport issue

Copy link
Contributor

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/calendar/MonthCalendarGrid.tsx (1)

214-225: Remove the defensive fallback || '' or use a dedicated sentinel that won't collide with data values.

The filter filters.traitIds.includes(o.habit.trait.id || '') creates a logic bug: when traitIds = [''] (nothing selected), occurrences with undefined trait.id would incorrectly match because the fallback '' equals the sentinel value, showing them when they should be hidden. Since Occurrence.habit.trait.id is typed as required, either ensure this data is never falsy, or replace the sentinel with a value that can't occur in real trait IDs (e.g., '__NONE__').

🤖 Fix all issues with AI agents
In `@src/components/calendar/MonthCalendarFilters.tsx`:
- Around line 240-254: The img inside SelectItem is using an invalid ARIA role
("role=\"habit-icon\"") which will be ignored; remove the role attribute and if
the attribute was only for tests replace it with a data-testid (e.g., on the img
element), otherwise rely on the existing meaningful alt text (habit.name) for
accessibility; locate the img in the SelectItem rendering where
getPublicUrl(StorageBuckets.HABIT_ICONS, habit.iconPath) is used and update
accordingly.

In `@src/stores/habits.store.ts`:
- Around line 48-55: The current set(...) call blindly replaces
state.calendarFilters.habitIds with all fetched habit ids, which clears any user
selection; modify the update in the set block (around state.habits and
state.calendarFilters) to compute the intersection of existing
state.calendarFilters.habitIds and the newly fetched habits' ids (habits.map(h
=> h.id)), and only use the full list when there is no prior selection (e.g.,
undefined/empty). Concretely, read the prior selection from
state.calendarFilters.habitIds, intersect it with the fetched ids to produce
newHabitIds (falling back to fetched ids if intersection is
empty/uninitialized), and assign that to calendarFilters.habitIds while still
setting state.habits = keyBy(habits, 'id').

In `@src/stores/traits.store.ts`:
- Around line 39-46: The current set((state) => { ... }) block overwrites
state.calendarFilters.traitIds with all fetched trait IDs which erases any user
selections; change the assignment to preserve existing selections by computing
the intersection between the existing state.calendarFilters.traitIds and the
newly fetched traits' ids (traits.map(t => t.id)), and only default to the full
list of fetched ids when state.calendarFilters.traitIds is undefined or empty;
update the logic around state.traits = keyBy(traits, 'id') and
state.calendarFilters to use this intersection so user-selected filters survive
a refresh.
🧹 Nitpick comments (3)
src/components/calendar/MonthCalendar.tsx (1)

17-21: Use @components alias (and barrel export) for the new subcomponent imports.

This keeps imports consistent with the repo’s alias + barrel conventions.

♻️ Suggested import adjustment
-import MonthCalendarFilters from './MonthCalendarFilters';
-import MonthCalendarNavigation from './MonthCalendarNavigation';
+import MonthCalendarFilters from '@components/calendar/MonthCalendarFilters';
+import MonthCalendarNavigation from '@components/calendar/MonthCalendarNavigation';
As per coding guidelines, “Use path aliases throughout the codebase: `@components`, `@pages`, `@stores`, `@services`, `@hooks`, `@models`, `@utils`, `@db-types`, `@tests`” and “Export new components, hooks, models, services, stores, and utils from their respective barrel files and import using corresponding path aliases”.
src/components/calendar/MonthCalendarGrid.tsx (1)

24-27: Remove commented-out code.

The commented prop // filters: OccurrenceFilters; is dead code and should be removed for cleanliness.

Suggested fix
 type CalendarGridProps = {
-  // filters: OccurrenceFilters;
   state: CalendarState;
 };
src/components/calendar/MonthCalendarFilters.tsx (1)

42-56: Consider extracting sentinel value to a constant.

The empty string '' is used as a sentinel value across multiple checks (lines 43, 51, 71, 102). Extracting this to a named constant would improve readability and reduce magic value usage.

Suggested refactor
+const EMPTY_FILTER_SENTINEL = '';
+
 const MonthCalendarFilters = () => {
   // ...
   const areAllHabitsSelected = React.useMemo(() => {
-    if (filters.habitIds.length === 1 && filters.habitIds.includes('')) {
+    if (filters.habitIds.length === 1 && filters.habitIds.includes(EMPTY_FILTER_SENTINEL)) {
       return false;
     }
     // ...
   }, [filters.habitIds, habits]);

This constant could be defined in the store and exported for consistent usage across components.

@domhhv domhhv merged commit df4fbfc into main Jan 28, 2026
11 checks passed
@domhhv domhhv deleted the refactor/global-filters-store branch January 28, 2026 16:59
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.

2 participants