Skip to content

Conversation

@ComputelessComputer
Copy link
Collaborator

  • Improved contact header design
  • Enhanced floating button functionality
  • Polished metadata chip UI
  • Added memo field to humans
  • Performed minor UI fixes and improvements
  • Refined contact UI

@coderabbitai
Copy link

coderabbitai bot commented Oct 23, 2025

📝 Walkthrough

Walkthrough

Removes a monolithic calendar module and splits it into focused calendar subcomponents; updates contacts and organizations to inline editable fields and grid layouts; enriches session metadata, participants UI, and overflow menus; adds a human memo field; minor UI/styling tweaks and a notification ignore-list update.

Changes

Cohort / File(s) Summary
Calendar: split monolith into focused components
apps/desktop/src/components/main/body/calendars.tsx, apps/desktop/src/components/main/body/calendars/index.tsx, apps/desktop/src/components/main/body/calendars/calendar-checkbox-row.tsx, apps/desktop/src/components/main/body/calendars/calendar-day.tsx, apps/desktop/src/components/main/body/calendars/day-events.tsx, apps/desktop/src/components/main/body/calendars/day-sessions.tsx, apps/desktop/src/components/main/body/calendars/day-more.tsx
Deletes the single calendars.tsx and introduces modular calendar components: TabItemCalendar/TabContentCalendar (index), CalendarCheckboxRow, TabContentCalendarDay, TabContentCalendarDayEvents, TabContentCalendarDaySessions, and TabContentCalendarDayMore. Moves grid calculation, per-day measurement, event/session retrieval, popovers, and sidebar/selection logic into these files.
Contacts: details & organization UI refactor
apps/desktop/src/components/main/body/contacts/details.tsx, apps/desktop/src/components/main/body/contacts/organization-details.tsx, apps/desktop/src/components/main/body/contacts/index.tsx
Replaces edit-form flows with inline Editable*Field components in contact details; reorganizes organization view into responsive person cards, adds per-person actions, introduces onPersonClick prop wiring, and removes legacy edit forms.
Contacts: presentational tweaks
apps/desktop/src/components/main/body/contacts/organizations.tsx, apps/desktop/src/components/main/body/contacts/shared.tsx
Adjusts truncation and layout classes for organization buttons and column header title (presentational only).
Session metadata: new components & conditional rendering
apps/desktop/src/components/main/body/sessions/outer-header/metadata/index.tsx, apps/desktop/src/components/main/body/sessions/outer-header/metadata/shared.ts, apps/desktop/src/components/main/body/sessions/outer-header/metadata/description.tsx, apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx, apps/desktop/src/components/main/body/sessions/outer-header/metadata/link.tsx, apps/desktop/src/components/main/body/sessions/outer-header/metadata/date.tsx
Adds MeetingLocation and MeetingDescription, extends MeetingMetadata with location and description, introduces null guards for metadata usage, reorders and conditionally renders metadata sections, and removes a Divider helper.
Session participants: searchable, interactive UI
apps/desktop/src/components/main/body/sessions/outer-header/metadata/participants.tsx, apps/desktop/src/components/main/body/sessions/outer-header/metadata/others.tsx
Replaces the simple participant list with interactive chips, hover cards, inline search with keyboard navigation, add/remove mapping flows, and enriches participant model (adds job_title, linkedin_username, organization). MeetingParticipant made internal.
Overflow & share menus: controlled behavior & new actions
apps/desktop/src/components/main/body/sessions/outer-header/overflow.tsx, apps/desktop/src/components/main/body/sessions/outer-header/share.tsx, apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx
Makes overflow menu controlled via local open state and threads setOpen through folder submenu to enable auto-close; adds Copy/Export/Delete menu items and subtle styling (rounded corners) in share popover; updates SearchableFolderSubmenuContent signature to accept optional setOpen.
Floating controls & util
apps/desktop/src/components/main/body/sessions/floating/shared.tsx
Adjusts FloatingButton styling and default pulsing-icon animation; adds exported utility formatTime(seconds): string.
Data model update
apps/desktop/src/store/tinybase/persisted.ts
Adds optional memo preprocess + schema field to humans table and updates the inferred Human type.
Notification & settings tweaks
plugins/notification/src/handler.rs, apps/desktop/src/components/settings/calendar.tsx
Adds com.apple.garageband10 to mic-start ignore list in notification handler; removes a placeholder comment in settings calendar file.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant TabItem as TabItemCalendar
    participant Content as TabContentCalendar
    participant Day as TabContentCalendarDay
    participant Events as TabContentCalendarDayEvents
    participant Sessions as TabContentCalendarDaySessions
    participant More as TabContentCalendarDayMore

    User->>TabItem: select calendars tab
    TabItem->>Content: render calendar content
    Content->>Content: compute 6x7 grid & selected calendars
    loop per day cell
        Content->>Day: render day cell (ids, flags)
        Day->>Day: measure height → visible/hidden items
        alt visible event
            Day->>Events: render event trigger (popover)
            User->>Events: open popover → open/create session editor
        else visible session
            Day->>Sessions: render session trigger
            User->>Sessions: open session editor
        end
        alt hidden items
            Day->>More: render "+N more"
            User->>More: open popover to list hidden items
        end
    end
    User->>Content: toggle sidebar / navigate months
    Content->>Content: update state (selectedCalendars, month)
Loading
sequenceDiagram
    participant User
    participant Participants as MeetingParticipants
    participant Store as TinyBase
    participant Search as SearchInput
    participant Chip as ParticipantChip

    User->>Participants: view participants
    Participants->>Store: fetch & enrich participants
    User->>Search: type query
    Search->>Store: query humans
    Store-->>Search: results
    User->>Search: select result
    Search->>Participants: create mapping to add participant
    Participants->>Participants: re-render chips
    User->>Chip: hover → HoverCard shows details
    User->>Chip: remove → delete mapping
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • yujonglee

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The PR title "UI enhancements" is related to the changeset as the changes do include various UI improvements across multiple components. However, the title is overly vague and generic, using a non-descriptive term that fails to convey meaningful information about the scope or specifics of the changes. The changeset includes significant refactoring (calendars feature split into multiple files), redesigned contact components, metadata improvements, schema changes, and minor styling fixes—diverse changes that warrant a more specific and descriptive title. A developer scanning git history would not understand what specific UI improvements were made or why this PR is significant. Consider revising the title to be more specific about the primary changes, such as "Refactor calendars into modular components and enhance contact/metadata UI" or similar, which would clearly indicate the main scope and intent of the PR to future readers.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed The PR description is related to the changeset and provides a bulleted list of changes that align with the actual modifications made. The items mentioned—improved contact header design, enhanced floating button functionality, polished metadata chip UI, added memo field to humans, and refined contact UI—all correspond to real changes in the PR. While the description lacks detailed explanations and could provide more context, it is not generic or completely vague, and it does convey that multiple UI areas were worked on.
✨ 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 jj-branch-27

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.

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: 12

Caution

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

⚠️ Outside diff range comments (4)
apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx (1)

87-87: Add accessible label to folder icon.

The icon lacks an accessible label, making it unclear to screen reader users what it represents.

Apply this diff to improve accessibility:

-              <FolderIcon />
+              <FolderIcon className="mr-2 h-4 w-4" aria-hidden="true" />

Note: Icons used decoratively alongside text should have aria-hidden="true" since the adjacent text already conveys the meaning.

apps/desktop/src/components/main/body/sessions/outer-header/metadata/index.tsx (1)

36-46: Replace invalid Tailwind class "max-w-28" with valid alternative.

The max-w-28 utility does not exist in Tailwind's default max-width utilities. Use max-w-md (28rem), max-w-[7rem], or another valid option from the available utilities.

Line 37:

className="max-w-28 text-neutral-700"
apps/desktop/src/components/main/body/contacts/details.tsx (2)

340-357: Use a real button for the Popover trigger (keyboard/a11y).

A div as an interactive trigger isn’t keyboard-focusable by default. Switch to a button to fix a11y and follow semantics.

-        <div className="inline-flex items-center cursor-pointer hover:bg-neutral-50 py-1 rounded-lg transition-colors">
+        <button
+          type="button"
+          className="inline-flex items-center hover:bg-neutral-50 py-1 rounded-lg transition-colors"
+          aria-label="Select organization"
+        >
 ...
-            : <span className="text-neutral-400 text-base">Select organization</span>}
-        </div>
+            : <span className="text-neutral-400 text-base">Select organization</span>}
+        </button>

438-454: “Create” row is a no-op; hide or implement to avoid dead UI.

The button renders but onClick={() => {}} does nothing. Either wire it or hide/disable it until ready.

Example minimal guard:

-              {organizations.length === 0 && (
+              {organizations.length === 0 && searchTerm.trim() && (
                 <button
                   type="button"
-                  className="flex items-center px-3 py-2 text-sm text-left hover:bg-neutral-100 transition-colors w-full"
-                  onClick={() => {}}
+                  className="flex items-center px-3 py-2 text-sm text-left hover:bg-neutral-100 transition-colors w-full disabled:opacity-50 disabled:cursor-not-allowed"
+                  onClick={/* TODO: implement create org */}
+                  disabled
                 >
🧹 Nitpick comments (18)
plugins/notification/src/handler.rs (1)

111-111: LGTM – Appropriate addition to the ignore list.

Adding GarageBand to the mic-start ignore list makes sense, as it's a music production application that legitimately uses the microphone for recording and music creation, similar to other creative tools already in the list (e.g., Descript, Loom, OBS Studio).

Optional: Consider externalizing the ignore list.

The hardcoded list now contains 12+ app identifiers and continues to grow. Consider moving this to a configuration file or constant to improve maintainability and allow easier updates without code changes.

Note: Change unrelated to PR objectives.

The PR description focuses on "UI enhancements" (calendar, contacts, metadata UI), but this change modifies notification handling logic. While the change itself is sound, it appears unrelated to the stated PR objectives.

apps/desktop/src/components/main/body/sessions/floating/shared.tsx (1)

37-41: Consider edge case handling for robustness.

The core formatting logic is correct. However, consider adding defensive checks for edge cases to prevent unexpected output:

Apply this diff to handle edge cases:

 export function formatTime(seconds: number): string {
+  if (!Number.isFinite(seconds) || seconds < 0) {
+    return "00:00";
+  }
   const mins = Math.floor(seconds / 60);
   const secs = Math.floor(seconds % 60);
   return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`;
 }

Additionally, consider adding JSDoc documentation since this is an exported utility function:

/**
 * Formats seconds into MM:SS string format.
 * @param seconds - The number of seconds to format (must be non-negative and finite)
 * @returns A string in MM:SS format (e.g., "03:45")
 */
export function formatTime(seconds: number): string {
  // ...
}
apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx (2)

66-66: Improve type safety for folders parameter.

The any type reduces TypeScript's ability to catch errors. Based on usage (line 84 accesses folder.name), define a proper interface for the folder object.

Apply this diff to add proper typing:

+interface Folder {
+  name: string;
+  // Add other known properties
+}
+
 function SearchableFolderContent({ folders, onSelectFolder, setOpen }: {
-  folders: Record<string, any>;
+  folders: Record<string, Folder>;
   onSelectFolder: (folderId: string) => void;
   setOpen?: (open: boolean) => void;
 }) {

39-39: Consider extracting duplicate empty state message.

The "No folders available" message appears identically on lines 39 and 60. While the duplication is minimal, extracting it to a constant would improve maintainability if the message or styling needs to change.

Example refactor:

const EmptyFoldersMessage = () => (
  <div className="py-6 text-center text-sm text-muted-foreground">
    No folders available
  </div>
);

// Then use: <EmptyFoldersMessage />

Also applies to: 60-60

apps/desktop/src/components/main/body/sessions/outer-header/metadata/shared.ts (1)

34-35: Remove unnecessary type assertions.

The type assertions as string | null | undefined on lines 34-35 are unnecessary since location and description are already declared with the same type in the MeetingMetadata interface. The type assertions may also hide potential type mismatches.

Apply this diff to remove the unnecessary type assertions:

   return {
     tood: eventNote ?? "",
     meeting_link: meetingLink,
     title,
     started_at: startedAt,
     ended_at: endedAt,
-    location: location as string | null | undefined,
-    description: description as string | null | undefined,
+    location,
+    description,
   };
apps/desktop/src/components/main/body/calendars/calendar-day.tsx (1)

107-124: Improve Tailwind className grouping with cn.

Per coding guidelines, when using cn for Tailwind classNames, group classes by logical sections within the array items. The current implementation could be more readable with better grouping.

As per coding guidelines.

Consider this improved grouping:

       <div
         className={cn(
-          ["text-sm size-6 rounded-full flex items-center justify-center mb-1", isToday && "bg-red-500"],
+          [
+            // Layout & sizing
+            "size-6 rounded-full flex items-center justify-center mb-1",
+            // Typography
+            "text-sm",
+            // Conditional background
+            isToday && "bg-red-500",
+          ],
         )}
       >
         <span
           className={cn(
             [
+              // Conditional text colors based on date state
               isToday && "text-white font-medium",
               !isToday && !isCurrentMonth && "text-neutral-400",
               !isToday && isCurrentMonth && isWeekend && "text-neutral-500",
               !isToday && isCurrentMonth && !isWeekend && "text-neutral-700",
             ],
           )}
         >
apps/desktop/src/components/main/body/sessions/outer-header/metadata/others.tsx (1)

129-134: Implement participant sorting by attendance.

The TODO comment indicates that participant sorting based on attendance is not yet implemented. The current implementation returns unsorted participants (sort function returns 0 for all comparisons).

Do you want me to generate an implementation that sorts participants by attendance status, or open a new issue to track this task?

apps/desktop/src/components/main/body/calendars/tab-content-calendar.tsx (2)

33-47: Keep selected calendars in sync with store changes.

Initializing Set once from calendarIds leaves out calendars added later (or keeps removed ones). Sync via effect.

-const [selectedCalendars, setSelectedCalendars] = useState<Set<string>>(() => new Set(calendarIds));
+const [selectedCalendars, setSelectedCalendars] = useState<Set<string>>(() => new Set(calendarIds));
+useEffect(() => {
+  setSelectedCalendars(prev => {
+    const next = new Set<string>();
+    // keep only existing ids, add any new ones by default (or choose your policy)
+    calendarIds.forEach(id => next.add(prev.has(id) ? id : id));
+    return next;
+  });
+}, [calendarIds]);

136-149: Minor: extract static week labels to a module constant.

Avoid re-allocating the array each render and makes it easier to localize later.

apps/desktop/src/components/main/body/calendars/day-events.tsx (1)

73-85: Consider creating/linking a session to the event when none exists.

openNew creates a note UI, but no mapping to this event is recorded here. If you intend to relate them, insert a mapping row (e.g., mapping_event_session) before opening.

apps/desktop/src/components/main/body/sessions/outer-header/metadata/participants.tsx (2)

210-215: Search predicate admits rows with neither name nor email; tighten matching.

Current condition can include entries lacking both fields. Use a clear OR match.

-        if (
-          name && !name.toLowerCase().includes(normalizedQuery)
-          && (!email || !email.toLowerCase().includes(normalizedQuery))
-        ) {
-          return;
-        }
+        const matchesName = name?.toLowerCase().includes(normalizedQuery) ?? false;
+        const matchesEmail = email?.toLowerCase().includes(normalizedQuery) ?? false;
+        if (!matchesName && !matchesEmail) return;

320-353: Avoid document.getElementById and duplicate input IDs; use a ref.

Multiple instances will share id="participant-search-input". Prefer a ref for focusing, and make the id unique if needed.

-        <div
+        const inputRef = useRef<HTMLInputElement>(null);
+        <div
           className={cn(
             "flex flex-wrap items-center w-full px-2 py-1.5 gap-1.5 rounded bg-neutral-50 border border-neutral-200 focus-within:border-neutral-300 min-h-[36px]",
             isFocused && "border-neutral-300",
           )}
-          onClick={() => document.getElementById("participant-search-input")?.focus()}
+          onClick={() => inputRef.current?.focus()}
         >
 ...
-          <input
-            id="participant-search-input"
+          <input
+            id={`participant-search-input-${sessionId}`}
+            ref={inputRef}
             type="text"
apps/desktop/src/components/main/body/contacts/details.tsx (4)

264-289: Clarify LinkedIn field semantics (username vs full URL).

Field key is linkedin_username but placeholder suggests a full URL. This mismatch can break later openUrl(). Decide on one:

  • Store a URL (preferred), or
  • Store a username and normalize to a URL on use.

Would you like me to patch both the input help text and the open handler to normalize values?


383-385: Null‑guard org names in filter to avoid crashes.

If an org lacks name, .toLowerCase() throws.

-  const organizations = searchTerm.trim()
-    ? allOrganizations.filter((org: any) => org.name.toLowerCase().includes(searchTerm.toLowerCase()))
+  const organizations = searchTerm.trim()
+    ? allOrganizations.filter((org: any) =>
+        (org.name ?? "").toLowerCase().includes(searchTerm.toLowerCase())
+      )
     : allOrganizations;

190-197: Normalize single‑line inputs (trim).

Trim leading/trailing spaces for name, job title, email, and LinkedIn. Don’t trim memo.

-    (e: React.ChangeEvent<HTMLInputElement>) => e.target.value,
+    (e: React.ChangeEvent<HTMLInputElement>) => e.target.value.trim(),

Apply the above to the handlers for name, job_title, email, and linkedin_username.

Also applies to: 212-219, 239-246, 267-274


134-136: Optional: consistent date format.

For reliability across locales, consider a fixed format (e.g., toLocaleDateString(undefined, { year:'numeric', month:'short', day:'numeric' })) or a shared formatter.

apps/desktop/src/components/main/body/contacts/organization-details.tsx (2)

75-76: Clarify casting precedence for initials.

Parenthesize to avoid misreading and make TS intent explicit.

-                                    {getInitials(human.name as string || human.email as string)}
+                                    {getInitials((human.name as string) || (human.email as string))}

59-59: Optional: responsive grid.

Scale columns with viewport for better density.

-                      <div className="grid grid-cols-3 gap-4">
+                      <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7a6a9aa and 0f9bf07.

📒 Files selected for processing (27)
  • apps/desktop/src/components/main/body/calendars.tsx (0 hunks)
  • apps/desktop/src/components/main/body/calendars/calendar-checkbox-row.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/calendar-day.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/day-events.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/day-more.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/day-sessions.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/index.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/tab-content-calendar.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/tab-item-calendar.tsx (1 hunks)
  • apps/desktop/src/components/main/body/contacts/details.tsx (9 hunks)
  • apps/desktop/src/components/main/body/contacts/index.tsx (1 hunks)
  • apps/desktop/src/components/main/body/contacts/organization-details.tsx (4 hunks)
  • apps/desktop/src/components/main/body/contacts/organizations.tsx (1 hunks)
  • apps/desktop/src/components/main/body/contacts/shared.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/floating/shared.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/date.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/description.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/index.tsx (3 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/link.tsx (2 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/others.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/participants.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/shared.ts (3 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/share.tsx (2 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx (1 hunks)
  • apps/desktop/src/store/tinybase/persisted.ts (2 hunks)
  • plugins/notification/src/handler.rs (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/desktop/src/components/main/body/calendars.tsx
🧰 Additional context used
📓 Path-based instructions (1)
apps/desktop/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (apps/desktop/.cursor/rules/style.mdc)

apps/desktop/**/*.{tsx,jsx}: When there are many Tailwind classNames with conditional logic, use the utility cn imported as import { cn } from "@hypr/utils"
When using cn for Tailwind classNames, always pass an array
Group Tailwind classNames by logical sections when using cn (split array items by grouping)

Files:

  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/description.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx
  • apps/desktop/src/components/main/body/contacts/index.tsx
  • apps/desktop/src/components/main/body/calendars/tab-item-calendar.tsx
  • apps/desktop/src/components/main/body/calendars/tab-content-calendar.tsx
  • apps/desktop/src/components/main/body/contacts/shared.tsx
  • apps/desktop/src/components/main/body/calendars/day-events.tsx
  • apps/desktop/src/components/main/body/contacts/details.tsx
  • apps/desktop/src/components/main/body/calendars/day-more.tsx
  • apps/desktop/src/components/main/body/calendars/calendar-day.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx
  • apps/desktop/src/components/main/body/calendars/index.tsx
  • apps/desktop/src/components/main/body/contacts/organization-details.tsx
  • apps/desktop/src/components/main/body/calendars/day-sessions.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/link.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/index.tsx
  • apps/desktop/src/components/main/body/sessions/floating/shared.tsx
  • apps/desktop/src/components/main/body/contacts/organizations.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/participants.tsx
  • apps/desktop/src/components/main/body/calendars/calendar-checkbox-row.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/share.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/date.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/others.tsx
🧬 Code graph analysis (14)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/description.tsx (1)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/shared.ts (1)
  • useMeetingMetadata (13-37)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (1)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/shared.ts (1)
  • useMeetingMetadata (13-37)
apps/desktop/src/components/main/body/calendars/tab-item-calendar.tsx (2)
apps/desktop/src/components/main/body/calendars/index.tsx (1)
  • TabItemCalendar (7-7)
apps/desktop/src/components/main/body/shared.tsx (2)
  • TabItem (26-26)
  • TabItemBase (28-103)
apps/desktop/src/components/main/body/calendars/tab-content-calendar.tsx (4)
apps/desktop/src/store/zustand/tabs/index.ts (1)
  • useTabs (21-30)
apps/desktop/src/components/main/body/index.tsx (1)
  • StandardTabWrapper (272-284)
apps/desktop/src/components/main/body/calendars/calendar-checkbox-row.tsx (1)
  • CalendarCheckboxRow (5-20)
apps/desktop/src/components/main/body/calendars/calendar-day.tsx (1)
  • TabContentCalendarDay (10-144)
apps/desktop/src/components/main/body/calendars/day-events.tsx (2)
apps/desktop/src/store/zustand/tabs/index.ts (1)
  • useTabs (21-30)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/desktop/src/components/main/body/contacts/details.tsx (1)
apps/desktop/src/components/main/body/contacts/shared.tsx (1)
  • getInitials (7-17)
apps/desktop/src/components/main/body/calendars/day-more.tsx (3)
apps/desktop/src/components/main/body/calendars/day-events.tsx (1)
  • TabContentCalendarDayEvents (11-89)
apps/desktop/src/components/main/body/calendars/day-sessions.tsx (1)
  • TabContentCalendarDaySessions (8-24)
apps/desktop/src/components/main/body/calendars.tsx (1)
  • TabContentCalendarDayMore (467-502)
apps/desktop/src/components/main/body/calendars/calendar-day.tsx (5)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/desktop/src/components/main/body/calendars/day-events.tsx (1)
  • TabContentCalendarDayEvents (11-89)
apps/desktop/src/components/main/body/calendars/day-sessions.tsx (1)
  • TabContentCalendarDaySessions (8-24)
apps/desktop/src/components/main/body/calendars/day-more.tsx (1)
  • TabContentCalendarDayMore (10-45)
apps/desktop/src/components/main/body/calendars.tsx (1)
  • TabContentCalendarDay (209-322)
apps/desktop/src/components/main/body/contacts/organization-details.tsx (1)
apps/desktop/src/components/main/body/contacts/shared.tsx (1)
  • getInitials (7-17)
apps/desktop/src/components/main/body/calendars/day-sessions.tsx (2)
apps/desktop/src/store/zustand/tabs/index.ts (1)
  • useTabs (21-30)
apps/desktop/src/components/main/body/calendars.tsx (1)
  • TabContentCalendarDaySessions (411-465)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/shared.ts (1)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/index.tsx (1)
  • MeetingMetadata (15-91)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/index.tsx (5)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (1)
  • MeetingLocation (5-20)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/link.tsx (1)
  • MeetingLink (15-54)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/date.tsx (1)
  • MeetingDate (5-13)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/participants.tsx (1)
  • MeetingParticipants (111-389)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/description.tsx (1)
  • MeetingDescription (3-15)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/participants.tsx (3)
apps/desktop/src/components/main/body/contacts/shared.tsx (1)
  • getInitials (7-17)
apps/desktop/src/store/zustand/tabs/index.ts (1)
  • useTabs (21-30)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/desktop/src/components/main/body/sessions/outer-header/share.tsx (1)
packages/ui/src/components/ui/popover.tsx (1)
  • PopoverContent (29-29)
🪛 Biome (2.1.2)
apps/desktop/src/components/main/body/calendars/tab-content-calendar.tsx

[error] 29-29: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 31-31: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 33-33: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)


[error] 35-35: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.

Hooks should not be called after an early return.

For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level

(lint/correctness/useHookAtTopLevel)

⏰ 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: ci (macos, macos-14)
🔇 Additional comments (21)
apps/desktop/src/components/main/body/sessions/floating/shared.tsx (2)

20-21: LGTM! Styling updates are appropriate.

The Button styling changes align with the UI enhancement objectives. The single static className doesn't require the cn utility per coding guidelines (which apply when there are many classNames with conditional logic).


26-31: LGTM! Enhanced visual feedback.

The animated ping effect provides better visual feedback than the previous implementation. The nested div structure with absolute positioning is the standard pattern for Tailwind ping animations.

apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx (1)

37-39: LGTM! Clean conditional rendering with proper dropdown closure.

The addition of the setOpen prop ensures the dropdown closes after a folder is selected, improving UX.

apps/desktop/src/components/main/body/sessions/outer-header/metadata/link.tsx (2)

8-11: LGTM!

The import reordering improves readability without affecting functionality.


25-25: Verify that the negative margin doesn't cause layout issues.

The addition of -ml-2.5 (negative left margin) could potentially cause the content to overlap with adjacent elements or break the layout at different viewport sizes. Ensure this adjustment has been tested across various screen sizes and contexts.

apps/desktop/src/components/main/body/sessions/outer-header/share.tsx (1)

78-78: LGTM!

The addition of rounded-lg classes enhances visual consistency across the UI components without affecting functionality.

Also applies to: 228-228

apps/desktop/src/components/main/body/sessions/outer-header/metadata/shared.ts (1)

3-11: LGTM!

The export of MeetingMetadata type and the addition of optional location and description fields properly extend the metadata schema.

apps/desktop/src/store/tinybase/persisted.ts (1)

45-45: LGTM!

The addition of the memo field to both the humanSchema and SCHEMA.table.humans follows the existing pattern for optional fields and is implemented consistently with other optional string fields in the schema.

Also applies to: 174-174

apps/desktop/src/components/main/body/sessions/outer-header/metadata/date.tsx (1)

2-2: LGTM!

The blank line improves import organization and readability.

apps/desktop/src/components/main/body/contacts/shared.tsx (1)

89-89: Verify that removing text truncation doesn't cause layout issues.

The truncate class was removed from the column header title, which means long titles will now wrap or potentially overflow instead of being truncated with an ellipsis. Ensure this doesn't break the layout, especially for long organization or contact names.

apps/desktop/src/components/main/body/contacts/organizations.tsx (1)

51-56: LGTM!

The addition of truncate to the button text and shrink-0 to the icon ensures proper text overflow handling while keeping the icon visible. The use of the cn utility follows the coding guidelines.

apps/desktop/src/components/main/body/contacts/index.tsx (1)

132-132: Prop signature verified. The OrganizationDetailsColumn component correctly accepts and handles the onPersonClick prop as an optional callback with signature (personId: string) => void. The component safely invokes it using optional chaining when a person card is clicked, passing the person's ID.

apps/desktop/src/components/main/body/calendars/calendar-checkbox-row.tsx (1)

5-20: LGTM!

The component is well-structured with proper fallback handling for missing calendar names and correct prop typing.

apps/desktop/src/components/main/body/calendars/tab-item-calendar.tsx (1)

6-28: LGTM!

The component correctly implements the TabItem interface with proper type extraction and handler forwarding.

apps/desktop/src/components/main/body/calendars/day-more.tsx (1)

10-45: LGTM!

The component properly handles overflow items with a clean popover UI and correct composition of event and session subcomponents.

apps/desktop/src/components/main/body/sessions/outer-header/metadata/others.tsx (2)

11-21: LGTM on interface refactoring.

The decision to make MeetingParticipant non-exported and move it to module-internal scope is appropriate, as the type is now encapsulated within this module. The extended fields (job_title, linkedin_username, organization) are properly typed with optional nullable fields.


117-243: Well-structured participant management component.

The ParticipantsSection component implements comprehensive functionality including:

  • Search with keyboard navigation (arrow keys, enter, escape)
  • Hover cards with rich participant details
  • Inline participant removal
  • Backspace to remove last participant

The implementation handles edge cases well and provides good UX.

apps/desktop/src/components/main/body/calendars/index.tsx (1)

1-7: Barrel looks good.

Consolidated exports are clear and minimal.

apps/desktop/src/components/main/body/sessions/outer-header/metadata/participants.tsx (1)

35-63: ParticipantChip onClick is declared but never used.

Users can’t open the contact from the chip. Wire the handler to the wrapper.

-<div className="group inline-flex items-center gap-1.5 px-1 py-0.5 bg-neutral-200 rounded-md hover:bg-neutral-200 transition-colors cursor-pointer">
+<div
+  className="group inline-flex items-center gap-1.5 px-1 py-0.5 bg-neutral-200 rounded-md hover:bg-neutral-200 transition-colors cursor-pointer"
+  onClick={onClick}
+>

Likely an incorrect or invalid review comment.

apps/desktop/src/components/main/body/calendars/day-sessions.tsx (2)

1-6: LGTM!

Imports are clean and all are used appropriately in the component.


18-23: Nice simplification of the calendar session UI!

The refactoring from the Popover-based implementation to a simpler Button component aligns well with the PR's objective of UI enhancements and modularization. The title determination logic correctly prioritizes event titles when available and provides a sensible "Untitled" fallback.

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

♻️ Duplicate comments (1)
apps/desktop/src/components/main/body/contacts/organization-details.tsx (1)

88-100: Handle the promise returned by openUrl.

The stopPropagation() call correctly prevents event bubbling (addressing a previous concern), but the promise returned by openUrl at line 95 is not handled. This creates an unhandled promise rejection risk.

Apply this diff:

-                                        openUrl(`mailto:${human.email}`);
+                                        void openUrl(`mailto:${human.email}`);
🧹 Nitpick comments (8)
apps/desktop/src/components/main/body/contacts/organization-details.tsx (1)

170-190: Consider using cn utility for improved readability.

While the coding guidelines emphasize using cn for classNames "with conditional logic" (which doesn't apply here), the long className string at line 187 could benefit from the cn utility for improved readability and maintainability.

If you choose to refactor:

-      className="border-none shadow-none p-0 h-8 text-lg font-semibold focus-visible:ring-0 focus-visible:ring-offset-0"
+      className={cn([
+        "border-none shadow-none p-0 h-8",
+        "text-lg font-semibold",
+        "focus-visible:ring-0 focus-visible:ring-offset-0"
+      ])}

As per coding guidelines

apps/desktop/src/components/main/body/calendars/tab-content-calendar.tsx (4)

16-16: Import useEffect for state reconciliation.

You’ll need useEffect for the selectedCalendars sync below.

-import { useState } from "react";
+import { useEffect, useState } from "react";

36-40: Reconcile selectedCalendars when calendars are removed.

If a calendar is deleted, stale IDs remain selected. Prune on calendarIds change. Decide separately whether to auto-select newly added calendars.

   const calendarIds = persisted.UI.useRowIds("calendars", persisted.STORE_ID);
 
   const [selectedCalendars, setSelectedCalendars] = useState<Set<string>>(() => new Set(calendarIds));
+
+  // Keep selection consistent if calendars are removed from the store.
+  useEffect(() => {
+    setSelectedCalendars(prev => {
+      // Remove any IDs no longer present. Do not auto-add new ones.
+      const next = new Set([...prev].filter(id => calendarIds.includes(id)));
+      return next;
+    });
+  }, [calendarIds]);

Would you prefer auto-selecting newly created calendars? If yes, I can adjust this to only add truly new IDs (and keep user-toggled-off ones off) using a ref to diff previous IDs.


110-117: Add aria-labels to icon-only navigation buttons.

Improves screen reader support.

-                <Button
+                <Button
                   variant="outline"
                   size="icon"
                   onClick={handlePreviousMonth}
                   className="shadow-none"
+                  aria-label="Previous month"
                 >
                   <ChevronLeft size={16} />
                 </Button>
@@
-                <Button
+                <Button
                   variant="outline"
                   size="icon"
                   onClick={handleNextMonth}
                   className="shadow-none"
+                  aria-label="Next month"
                 >
                   <ChevronRight size={16} />
                 </Button>

Also applies to: 128-135


80-83: Remove redundant variant condition.

Inside the open sidebar, this button is always rendered while sidebarOpen is true.

-              <Button size="icon" variant={sidebarOpen ? "default" : "ghost"} onClick={() => setSidebarOpen(false)}>
+              <Button size="icon" variant="default" onClick={() => setSidebarOpen(false)}>
apps/desktop/src/components/main/body/calendars/day-events.tsx (3)

24-25: Avoid subscribing to a “dummy” row if API allows undefined.

If persisted.UI.useRow supports an undefined/null id, prefer that to reduce unnecessary subscriptions.

Please confirm the hook signature. If it accepts undefined:

-  const linkedSession = persisted.UI.useRow("sessions", linkedSessionId || "dummy", persisted.STORE_ID);
+  const linkedSession = persisted.UI.useRow("sessions", linkedSessionId, persisted.STORE_ID);

If not, consider a dedicated no-op path in the hook to avoid binding to a fake row id.


32-33: crypto.randomUUID environment check.

Some Electron/Node targets may lack crypto.randomUUID. Add a safe fallback.

-      openNew({ type: "sessions", id: crypto.randomUUID(), state: { editor: "raw" } });
+      openNew({
+        type: "sessions",
+        id: (globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(16).slice(2)}`),
+        state: { editor: "raw" },
+      });

Confirm minimum Electron/Node versions; if guaranteed, you can keep crypto.randomUUID.


56-64: Add an accessible name to the event trigger.

Icon + title is visually clear but add aria-label for SRs.

-        <Button
+        <Button
           variant="ghost"
           className={cn(
             ["w-full justify-start px-1 text-neutral-600 h-6", open && "bg-neutral-100 hover:bg-neutral-100"],
           )}
+          aria-label={`Open event: ${title}`}
         >
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0f9bf07 and 211459a.

📒 Files selected for processing (11)
  • apps/desktop/src/components/main/body/calendars/calendar-day.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/day-events.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/day-sessions.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/tab-content-calendar.tsx (1 hunks)
  • apps/desktop/src/components/main/body/contacts/organization-details.tsx (4 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/date.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/description.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/link.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/overflow.tsx (3 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/description.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/link.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/date.tsx
  • apps/desktop/src/components/main/body/calendars/day-sessions.tsx
  • apps/desktop/src/components/main/body/calendars/calendar-day.tsx
🧰 Additional context used
📓 Path-based instructions (1)
apps/desktop/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (apps/desktop/.cursor/rules/style.mdc)

apps/desktop/**/*.{tsx,jsx}: When there are many Tailwind classNames with conditional logic, use the utility cn imported as import { cn } from "@hypr/utils"
When using cn for Tailwind classNames, always pass an array
Group Tailwind classNames by logical sections when using cn (split array items by grouping)

Files:

  • apps/desktop/src/components/main/body/calendars/day-events.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx
  • apps/desktop/src/components/main/body/contacts/organization-details.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/overflow.tsx
  • apps/desktop/src/components/main/body/calendars/tab-content-calendar.tsx
🧬 Code graph analysis (5)
apps/desktop/src/components/main/body/calendars/day-events.tsx (2)
apps/desktop/src/store/zustand/tabs/index.ts (1)
  • useTabs (21-30)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (1)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/shared.ts (1)
  • useMeetingMetadata (13-37)
apps/desktop/src/components/main/body/contacts/organization-details.tsx (1)
apps/desktop/src/components/main/body/contacts/shared.tsx (1)
  • getInitials (7-17)
apps/desktop/src/components/main/body/sessions/outer-header/overflow.tsx (1)
apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx (1)
  • SearchableFolderSubmenuContent (45-65)
apps/desktop/src/components/main/body/calendars/tab-content-calendar.tsx (5)
apps/desktop/src/store/zustand/tabs/index.ts (1)
  • useTabs (21-30)
apps/desktop/src/components/main/body/index.tsx (1)
  • StandardTabWrapper (272-284)
apps/desktop/src/components/main/body/calendars/calendar-checkbox-row.tsx (1)
  • CalendarCheckboxRow (5-20)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/desktop/src/components/main/body/calendars/calendar-day.tsx (1)
  • TabContentCalendarDay (10-143)
⏰ 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: ci (macos, macos-14)
🔇 Additional comments (11)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (2)

5-10: Previous critical issue resolved.

The null handling has been properly implemented as suggested in the previous review. The non-null assertion has been removed and optional chaining is now used to safely check for metadata and location, preventing potential runtime errors.


12-19: Clean and well-structured rendering logic.

The component properly renders the location with a map pin icon and applies appropriate styling. The truncate class prevents text overflow, and the layout is semantic and accessible.

apps/desktop/src/components/main/body/sessions/outer-header/overflow.tsx (1)

2-2: LGTM! Controlled dropdown state properly implemented.

The changes correctly establish controlled state management for the dropdown menu, enabling it to close after folder selection. The open state and setOpen handler are properly wired through the component hierarchy: OverflowButtonFolderSearchableFolderSubmenuContent.

Also applies to: 19-19, 22-22, 30-30, 56-56, 63-63

apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx (1)

45-47: LGTM! Previous review feedback addressed.

The changes correctly pass setOpen through to SearchableFolderContent, enabling the parent dropdown to close after folder selection. This resolves the UX inconsistency flagged in the previous review where the submenu variant didn't close on selection while the dropdown variant did.

Also applies to: 60-62

apps/desktop/src/components/main/body/contacts/organization-details.tsx (5)

4-6: LGTM!

The new imports correctly support the enhanced UI features: organization avatar, email/LinkedIn action buttons, and external URL opening.


11-19: LGTM!

The new onPersonClick prop appropriately enables parent components to handle person selection, following standard React callback patterns.


34-51: LGTM! Previous issue resolved.

The header section is well-structured, and the optional chaining at line 45 properly guards against peopleInOrg being undefined, resolving the previous critical issue flagged in past reviews.


102-118: LGTM! Previous issues resolved.

The LinkedIn button correctly implements all fixes from past reviews: stopPropagation() prevents event bubbling, the void operator handles the promise, and the URL construction defensively handles both full URLs and username-only formats.


130-155: LGTM!

The Danger Zone section appropriately separates the destructive delete action with clear warning UI, and the event handling is defensive with both preventDefault and stopPropagation.

apps/desktop/src/components/main/body/calendars/tab-content-calendar.tsx (1)

24-31: Hooks-order issue resolved.

Wrapper + inner component removes conditional hook calls. Good fix.

apps/desktop/src/components/main/body/calendars/day-events.tsx (1)

16-17: Good defensive handling for missing/invalid data.

Title fallback and time formatter null/NaN checks prevent crashes; trigger now uses safe title. Nicely done.

Also applies to: 36-51, 63-64

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

♻️ Duplicate comments (3)
apps/desktop/src/components/main/body/contacts/organization-details.tsx (1)

93-96: Add void prefix to handle the promise consistently.

Line 112 correctly uses void openUrl(href), but line 95 is missing the void prefix. This creates an inconsistency and leaves an unhandled promise warning.

Apply this diff:

                                      onClick={(e) => {
                                        e.stopPropagation();
-                                       openUrl(`mailto:${human.email}`);
+                                       void openUrl(`mailto:${human.email}`);
                                      }}
apps/desktop/src/components/main/body/calendars/day-sessions.tsx (1)

12-12: Good fixes: removed “dummy” fallback and sized icon consistently

Empty-string fallback avoids wasted queries; StickyNote size={12} matches surrounding UI. Resolved prior feedback.

Also applies to: 20-20

apps/desktop/src/components/main/body/calendars/tab-content-calendar.tsx (1)

24-31: Hooks guard refactor looks correct

Wrapper returns early; hooks live in TabContentCalendarInner. Fixes Rules of Hooks issue.

🧹 Nitpick comments (7)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/date.tsx (1)

8-10: Simplify the guard condition for clarity.

The guard uses && (both fields must be falsy), but since useMeetingMetadata returns null if either startedAt or endedAt is missing, when meta is non-null, both fields are guaranteed to be present. A simpler check would be more defensive and clearer.

Apply this diff to simplify the guard:

-  if (!meta?.started_at && !meta?.ended_at) {
+  if (!meta) {
     return null;
   }
apps/desktop/src/components/main/body/contacts/organization-details.tsx (2)

75-75: Consider safer type handling for name/email.

The type assertion pattern human.name as string || human.email as string works but could be simplified and made more type-safe.

Consider this refactor:

-                                   {getInitials(human.name as string || human.email as string)}
+                                   {getInitials((human.name || human.email) as string)}

Or guard the call:

{getInitials(String(human.name || human.email || ""))}

67-121: Consider adding keyboard navigation support.

The person cards use clickable divs without keyboard navigation. Adding role="button", tabIndex={0}, and onKeyDown handlers would improve accessibility.

Example enhancement:

                           <div
                             key={humanId}
                             className="p-4 rounded-lg border border-neutral-200 hover:shadow-sm transition-all bg-white cursor-pointer"
+                            role="button"
+                            tabIndex={0}
                             onClick={() => onPersonClick?.(humanId)}
+                            onKeyDown={(e) => {
+                              if (e.key === "Enter" || e.key === " ") {
+                                e.preventDefault();
+                                onPersonClick?.(humanId);
+                              }
+                            }}
                           >
apps/desktop/src/components/main/body/calendars/day-sessions.tsx (1)

21-21: Consistent copy suggestion: prefer “Untitled Note”

Align with day-events.tsx (“Untitled Note”) for consistency.

-      <p className="truncate">{event && eventId ? event.title : session?.title || "Untitled"}</p>
+      <p className="truncate">{event && eventId ? event.title : session?.title || "Untitled Note"}</p>
apps/desktop/src/components/main/body/calendars/calendar-day.tsx (2)

1-1: Parse ISO dates safely to avoid TZ off‑by‑one

new Date("yyyy-MM-dd") is UTC in many engines; use parseISO for stable local-day computations.

-import { cn, format, getDay } from "@hypr/utils";
+import { cn, format, getDay, parseISO } from "@hypr/utils";
@@
-  const dayNumber = format(new Date(day), "d");
+  const dayNumber = format(parseISO(day), "d");
   const isToday = format(new Date(), "yyyy-MM-dd") === day;
-  const dayOfWeek = getDay(new Date(day));
+  const dayOfWeek = getDay(parseISO(day));

Also applies to: 46-49


51-73: Recompute layout on container resize, not just window resize

Sidebars/panels can change cell height without a window resize. Observe the cell/content with ResizeObserver.

   // Measure actual available height and calculate max visible items
   useEffect(() => {
-    const measureHeight = () => {
+    const measureHeight = () => {
       if (cellRef.current && contentRef.current) {
         const cellHeight = cellRef.current.clientHeight;
         const contentTop = contentRef.current.offsetTop;
         const availableHeight = cellHeight - contentTop;
         const EVENT_HEIGHT = 20; // height of each event item (h-5)
         const SPACING = 4; // space-y-1

         // Calculate how many items can fit
         const itemsWithSpacing = Math.floor((availableHeight + SPACING) / (EVENT_HEIGHT + SPACING));
         // Reserve space for "+x more" if needed
         setMaxVisibleItems(Math.max(1, itemsWithSpacing));
       }
     };
 
     measureHeight();
 
-    // Re-measure on window resize
-    window.addEventListener("resize", measureHeight);
-    return () => window.removeEventListener("resize", measureHeight);
+    // Re-measure on window resize and element resize
+    window.addEventListener("resize", measureHeight);
+    const ro = new ResizeObserver(() => measureHeight());
+    if (cellRef.current) ro.observe(cellRef.current);
+    if (contentRef.current) ro.observe(contentRef.current);
+    return () => {
+      window.removeEventListener("resize", measureHeight);
+      ro.disconnect();
+    };
   }, []);
apps/desktop/src/components/main/body/calendars/tab-content-calendar.tsx (1)

36-39: Keep selectedCalendars in sync when calendars list changes

If calendars are added later, they won’t be auto-selected. Union new IDs to preserve user choices.

   const calendarIds = persisted.UI.useRowIds("calendars", persisted.STORE_ID);
 
   const [selectedCalendars, setSelectedCalendars] = useState<Set<string>>(() => new Set(calendarIds));
+
+  // Add newly created calendars to the selection without overwriting user choices.
+  useEffect(() => {
+    setSelectedCalendars(prev => {
+      const next = new Set(prev);
+      for (const id of calendarIds) next.add(id);
+      return next;
+    });
+  }, [calendarIds]);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 211459a and b970ffb.

📒 Files selected for processing (11)
  • apps/desktop/src/components/main/body/calendars/calendar-day.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/day-events.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/day-sessions.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/tab-content-calendar.tsx (1 hunks)
  • apps/desktop/src/components/main/body/contacts/organization-details.tsx (4 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/date.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/description.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/link.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/overflow.tsx (3 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/link.tsx
  • apps/desktop/src/components/main/body/calendars/day-events.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/description.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx
🧰 Additional context used
📓 Path-based instructions (1)
apps/desktop/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (apps/desktop/.cursor/rules/style.mdc)

apps/desktop/**/*.{tsx,jsx}: When there are many Tailwind classNames with conditional logic, use the utility cn imported as import { cn } from "@hypr/utils"
When using cn for Tailwind classNames, always pass an array
Group Tailwind classNames by logical sections when using cn (split array items by grouping)

Files:

  • apps/desktop/src/components/main/body/calendars/calendar-day.tsx
  • apps/desktop/src/components/main/body/calendars/tab-content-calendar.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx
  • apps/desktop/src/components/main/body/calendars/day-sessions.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/date.tsx
  • apps/desktop/src/components/main/body/contacts/organization-details.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/overflow.tsx
🧬 Code graph analysis (7)
apps/desktop/src/components/main/body/calendars/calendar-day.tsx (5)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/desktop/src/components/main/body/calendars/day-events.tsx (1)
  • TabContentCalendarDayEvents (11-91)
apps/desktop/src/components/main/body/calendars/day-sessions.tsx (1)
  • TabContentCalendarDaySessions (8-24)
apps/desktop/src/components/main/body/calendars/day-more.tsx (1)
  • TabContentCalendarDayMore (10-45)
apps/desktop/src/components/main/body/calendars.tsx (1)
  • TabContentCalendarDay (209-322)
apps/desktop/src/components/main/body/calendars/tab-content-calendar.tsx (4)
apps/desktop/src/store/zustand/tabs/index.ts (1)
  • useTabs (21-30)
apps/desktop/src/components/main/body/index.tsx (1)
  • StandardTabWrapper (272-284)
apps/desktop/src/components/main/body/calendars/calendar-checkbox-row.tsx (1)
  • CalendarCheckboxRow (5-20)
apps/desktop/src/components/main/body/calendars/calendar-day.tsx (1)
  • TabContentCalendarDay (10-143)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (1)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/shared.ts (1)
  • useMeetingMetadata (13-37)
apps/desktop/src/components/main/body/calendars/day-sessions.tsx (2)
apps/desktop/src/store/zustand/tabs/index.ts (1)
  • useTabs (21-30)
apps/desktop/src/components/main/body/calendars.tsx (1)
  • TabContentCalendarDaySessions (411-465)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/date.tsx (1)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/shared.ts (1)
  • useMeetingMetadata (13-37)
apps/desktop/src/components/main/body/contacts/organization-details.tsx (1)
apps/desktop/src/components/main/body/contacts/shared.tsx (1)
  • getInitials (7-17)
apps/desktop/src/components/main/body/sessions/outer-header/overflow.tsx (1)
apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx (1)
  • SearchableFolderSubmenuContent (45-65)
⏰ 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: ci (macos, macos-14)
🔇 Additional comments (10)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (1)

5-20: Previous null-safety issue successfully resolved.

The non-null assertion issue flagged in the previous review has been properly addressed. The code now correctly handles the nullable return from useMeetingMetadata using optional chaining on line 8, preventing potential runtime errors when metadata is unavailable.

apps/desktop/src/components/main/body/sessions/outer-header/overflow.tsx (2)

2-2: Controlled dropdown pattern implemented correctly.

The addition of local state and conversion to a controlled DropdownMenu is clean and follows React best practices. This enables the dropdown to be closed programmatically after actions like folder selection.

Also applies to: 19-22


30-30: Clean prop threading for dropdown control.

The setOpen callback is appropriately threaded through the component hierarchy (FolderSearchableFolderSubmenuContent), enabling child components to close the dropdown after selection. The optional typing is appropriate and provides flexibility for future use cases.

Also applies to: 56-56, 63-63

apps/desktop/src/components/main/body/contacts/organization-details.tsx (7)

1-9: LGTM!

All imports are appropriate for the new functionality.


11-28: LGTM!

The onPersonClick prop is correctly typed as optional and properly integrated with the TinyBase store hooks.


35-51: LGTM!

The header section correctly guards against undefined peopleInOrg using optional chaining, addressing previous review concerns.


53-64: LGTM!

The conditional check on line 57 properly guards the .map() call on line 60, ensuring peopleInOrg is defined and non-empty.


126-126: LGTM!

Clear empty-state message.


130-155: LGTM!

The Danger Zone section properly isolates the destructive delete action with appropriate styling, clear warnings, and correct event handling to prevent bubbling.


170-190: LGTM!

The editable field component is clean and straightforward. While line 187 has many Tailwind classes, the coding guidelines specify using cn for classes "with conditional logic," which is not present here.

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

♻️ Duplicate comments (1)
apps/desktop/src/components/main/body/contacts/organization-details.tsx (1)

93-96: Add void to handle the promise returned by openUrl.

Line 95 calls openUrl() without handling the returned promise, while line 112 correctly uses void openUrl(...). This inconsistency was flagged in previous reviews.

Apply this diff:

                                      onClick={(e) => {
                                        e.stopPropagation();
-                                       openUrl(`mailto:${human.email}`);
+                                       void openUrl(`mailto:${human.email}`);
                                      }}
🧹 Nitpick comments (10)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/link.tsx (3)

24-26: Refactor guard to enable TypeScript type narrowing.

The optional chaining in !meta?.meeting_link prevents TypeScript from narrowing the types in subsequent code, forcing non-null assertions later (lines 39, 51).

Apply this diff to improve type safety:

- if (!meta?.meeting_link) {
+ if (!meta || !meta.meeting_link) {
   return null;
 }

With explicit null checks, TypeScript will correctly narrow both meta and meta.meeting_link to non-null values, eliminating the need for ! assertions below.


34-34: Remove redundant condition.

The check meta.meeting_link && is redundant because the early return at line 24-26 already guarantees meta.meeting_link is truthy at this point.

Apply this diff:

- {meta.meeting_link && renderURL(meta.meeting_link)}
+ {renderURL(meta.meeting_link)}

39-39: Non-null assertions can be avoided.

The non-null assertions (meta.meeting_link!) are safe due to the guard at line 24-26, but are only necessary because TypeScript cannot infer type narrowing from optional chaining. If the guard is refactored as suggested (lines 24-26), these assertions can be removed.

After refactoring the guard to use explicit checks, update these lines:

- onClick={() => openUrl(meta.meeting_link!)}
+ onClick={() => openUrl(meta.meeting_link)}

Also applies to: 51-51

apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (1)

12-19: Clean rendering implementation.

The component structure is well-designed with proper flex layout, icon sizing, and text truncation. The flex-shrink-0 on the icon prevents layout issues, and the truncate class handles overflow gracefully.

Optionally, you could add aria-hidden="true" to the icon since it's decorative and the location text provides the semantic meaning:

-      <MapPinIcon size={16} className="flex-shrink-0 text-neutral-700" />
+      <MapPinIcon size={16} className="flex-shrink-0 text-neutral-700" aria-hidden="true" />
apps/desktop/src/components/main/body/sessions/outer-header/overflow.tsx (4)

42-54: TODO: Implement copy link functionality.

The copy link feature is not yet implemented. This should be tracked and completed.

Do you want me to generate an implementation for the copy link functionality or open a new issue to track this task?


68-80: TODO: Implement export to PDF functionality.

The export to PDF feature is not yet implemented. This should be tracked and completed.

Do you want me to generate an implementation for the export to PDF functionality or open a new issue to track this task?


105-120: TODO: Implement delete note functionality.

The delete note feature is not yet implemented. This is critical functionality that should be tracked and completed, ideally with a confirmation dialog to prevent accidental deletions.

Do you want me to generate an implementation for the delete note functionality (including confirmation dialog) or open a new issue to track this task?


122-137: TODO: Implement delete recording functionality.

The delete recording feature is not yet implemented. This is critical functionality that should be tracked and completed, ideally with a confirmation dialog to prevent accidental deletions.

Do you want me to generate an implementation for the delete recording functionality (including confirmation dialog) or open a new issue to track this task?

apps/desktop/src/components/main/body/contacts/organization-details.tsx (1)

67-71: Consider making person cards keyboard-accessible.

The cards use a clickable div which isn't keyboard-navigable. For better accessibility, consider wrapping the card in a button element or adding role="button", tabIndex={0}, and keyboard event handlers.

apps/desktop/src/components/main/body/calendars/day-events.tsx (1)

24-24: Consider using empty string instead of "dummy" for consistency.

Line 24 uses "dummy" as a fallback when linkedSessionId is undefined, which may trigger an unnecessary query for a non-existent row. For consistency with the approach used in day-sessions.tsx (line 12), consider using an empty string fallback instead.

Apply this diff for consistency:

-  const linkedSession = persisted.UI.useRow("sessions", linkedSessionId || "dummy", persisted.STORE_ID);
+  const linkedSession = persisted.UI.useRow("sessions", linkedSessionId || "", persisted.STORE_ID);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b970ffb and 87d2b64.

📒 Files selected for processing (11)
  • apps/desktop/src/components/main/body/calendars/calendar-day.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/day-events.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/day-sessions.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/tab-content-calendar.tsx (1 hunks)
  • apps/desktop/src/components/main/body/contacts/organization-details.tsx (4 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/date.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/description.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/link.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/overflow.tsx (3 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/description.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/date.tsx
🧰 Additional context used
📓 Path-based instructions (1)
apps/desktop/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (apps/desktop/.cursor/rules/style.mdc)

apps/desktop/**/*.{tsx,jsx}: When there are many Tailwind classNames with conditional logic, use the utility cn imported as import { cn } from "@hypr/utils"
When using cn for Tailwind classNames, always pass an array
Group Tailwind classNames by logical sections when using cn (split array items by grouping)

Files:

  • apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx
  • apps/desktop/src/components/main/body/calendars/calendar-day.tsx
  • apps/desktop/src/components/main/body/calendars/day-sessions.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/link.tsx
  • apps/desktop/src/components/main/body/contacts/organization-details.tsx
  • apps/desktop/src/components/main/body/calendars/day-events.tsx
  • apps/desktop/src/components/main/body/calendars/tab-content-calendar.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/overflow.tsx
🧬 Code graph analysis (8)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (1)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/shared.ts (1)
  • useMeetingMetadata (13-37)
apps/desktop/src/components/main/body/calendars/calendar-day.tsx (5)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/desktop/src/components/main/body/calendars/day-events.tsx (1)
  • TabContentCalendarDayEvents (11-91)
apps/desktop/src/components/main/body/calendars/day-sessions.tsx (1)
  • TabContentCalendarDaySessions (8-25)
apps/desktop/src/components/main/body/calendars/day-more.tsx (1)
  • TabContentCalendarDayMore (10-45)
apps/desktop/src/components/main/body/calendars.tsx (1)
  • TabContentCalendarDay (209-322)
apps/desktop/src/components/main/body/calendars/day-sessions.tsx (3)
apps/desktop/src/components/main/body/calendars/index.tsx (1)
  • TabContentCalendarDaySessions (5-5)
apps/desktop/src/store/zustand/tabs/index.ts (1)
  • useTabs (21-30)
packages/ui/src/components/ui/button.tsx (1)
  • Button (53-53)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/link.tsx (1)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/shared.ts (1)
  • useMeetingMetadata (13-37)
apps/desktop/src/components/main/body/contacts/organization-details.tsx (1)
apps/desktop/src/components/main/body/contacts/shared.tsx (1)
  • getInitials (7-17)
apps/desktop/src/components/main/body/calendars/day-events.tsx (2)
apps/desktop/src/store/zustand/tabs/index.ts (1)
  • useTabs (21-30)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/desktop/src/components/main/body/calendars/tab-content-calendar.tsx (4)
apps/desktop/src/store/zustand/tabs/index.ts (1)
  • useTabs (21-30)
apps/desktop/src/components/main/body/index.tsx (1)
  • StandardTabWrapper (272-284)
apps/desktop/src/components/main/body/calendars/calendar-checkbox-row.tsx (1)
  • CalendarCheckboxRow (5-20)
apps/desktop/src/components/main/body/calendars/calendar-day.tsx (1)
  • TabContentCalendarDay (10-143)
apps/desktop/src/components/main/body/sessions/outer-header/overflow.tsx (1)
apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx (1)
  • SearchableFolderSubmenuContent (45-65)
⏰ 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: ci (macos, macos-14)
🔇 Additional comments (7)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (1)

5-10: Previous critical issue successfully resolved!

The non-null assertion and unsafe null handling flagged in the previous review have been properly fixed. The code now safely handles null metadata with optional chaining and no longer uses the dangerous ! operator.

apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx (1)

45-47: Previous issue resolved: submenu now closes after folder selection.

The addition of the optional setOpen parameter and its propagation to SearchableFolderContent correctly addresses the prior review feedback. When a user selects a folder via the submenu, handleSelect will invoke setOpen?.(false) (line 74), closing the parent dropdown.

Also applies to: 60-62

apps/desktop/src/components/main/body/sessions/outer-header/overflow.tsx (3)

2-2: LGTM: Controlled dropdown state management.

Converting to a controlled dropdown with explicit open state enables the parent to programmatically close the menu (e.g., after folder selection), which is the correct approach for this use case.

Also applies to: 19-19, 22-22


30-30: LGTM: Correct setOpen propagation chain.

The threading of setOpen from the parent dropdown through Folder to SearchableFolderSubmenuContent is implemented correctly and enables the menu to close after folder selection, maintaining consistent UX.

Also applies to: 56-56, 63-63


82-103: Verify if listening toggle should close the dropdown.

Unlike folder selection (which now closes the menu via setOpen), toggling the listening state keeps the dropdown open. This may be intentional to allow users to see the state change, but it creates inconsistent UX compared to other menu actions.

If the menu should close after toggling listening, add setOpen?.(false) after the toggle:

-function Listening({ sessionId }: { sessionId: string }) {
+function Listening({ sessionId, setOpen }: { sessionId: string; setOpen?: (open: boolean) => void }) {
   const { stop, isListening } = useListener((state) => ({
     stop: state.stop,
     isListening: state.status === "running_active",
   }));
   const startListening = useStartListening(sessionId);

   const handleToggleListening = () => {
     if (isListening) {
       stop();
     } else {
       startListening();
     }
+    setOpen?.(false);
   };

And update the call site:

-        <Listening sessionId={sessionId} />
+        <Listening sessionId={sessionId} setOpen={setOpen} />
apps/desktop/src/components/main/body/contacts/organization-details.tsx (1)

170-189: LGTM!

The inline editable field integrates cleanly with the persisted store and handles edge cases properly (fallback to empty string). The styling is appropriate for an inline-edit experience.

apps/desktop/src/components/main/body/calendars/tab-content-calendar.tsx (1)

36-38: Verify behavior when new calendars are added after mount.

The selectedCalendars state is initialized with the current calendarIds on mount, but the initializer only runs once. If new calendars are added later (causing calendarIds to change), they won't be automatically selected.

Please verify whether newly added calendars should:

  • Option A: Be automatically selected (current behavior may be a bug)
  • Option B: Require manual selection (current behavior is correct)

If Option A is desired, consider synchronizing the state when calendarIds changes.

If automatic selection is desired, you can use an effect to sync new calendars:

useEffect(() => {
  setSelectedCalendars((prev) => {
    const next = new Set(prev);
    calendarIds.forEach((id) => {
      if (!prev.has(id)) {
        next.add(id);
      }
    });
    return next;
  });
}, [calendarIds]);

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: 4

🧹 Nitpick comments (7)
apps/desktop/src/components/main/body/contacts/organizations.tsx (1)

51-57: Make truncation reliable in flex row

Truncating the button container is brittle. Add min-w-0 to the button and move truncate to the label span to ensure ellipsis with flex.

- className={cn([
-   "w-full text-left px-3 py-2 rounded-md text-sm truncate flex items-center gap-2 hover:bg-neutral-100 transition-colors",
+ className={cn([
+   "w-full text-left px-3 py-2 rounded-md text-sm flex items-center gap-2 hover:bg-neutral-100 transition-colors min-w-0",
    !selectedOrganization && "bg-neutral-100",
 ])}
 ...
- <User className="h-4 w-4 text-neutral-500 shrink-0" />
- All People
+ <User className="h-4 w-4 text-neutral-500 shrink-0" />
+ <span className="truncate">All People</span>
apps/desktop/src/store/tinybase/persisted.ts (1)

45-46: memo field added — confirm query exposure if UI needs it

Schema addition looks good. If any UI reads human memos, consider selecting memo in QUERIES.visibleHumans to avoid ad-hoc row reads.

Also applies to: 174-175

apps/desktop/src/components/main/body/calendars/index.tsx (1)

61-75: Keep selected calendars in sync with store changes

selectedCalendars is seeded once; new/removed calendars won’t reflect. Reconcile on calendarIds changes.

- const [selectedCalendars, setSelectedCalendars] = useState<Set<string>>(() => new Set(calendarIds));
+ const [selectedCalendars, setSelectedCalendars] = useState<Set<string>>(
+   () => new Set(calendarIds),
+ );
+ useEffect(() => {
+   setSelectedCalendars((prev) => {
+     const next = new Set<string>();
+     // Keep only ids that still exist
+     calendarIds.forEach((id) => {
+       // Auto-add newly discovered calendars by default
+       next.add(id);
+     });
+     return next;
+   });
+ }, [calendarIds]);
apps/desktop/src/components/main/body/sessions/outer-header/metadata/participants.tsx (1)

321-324: Follow cn array style per repo guideline

Use cn with an array and group logical sections. This keeps consistency with apps/desktop Tailwind conventions. As per coding guidelines.

- className={cn(
-   "flex flex-wrap ... min-h-[36px]",
-   isFocused && "border-neutral-300",
- )}
+ className={cn([
+   "flex flex-wrap ... min-h-[36px]",
+   isFocused && "border-neutral-300",
+ ])}
...
- className={cn(
-   "flex items-center justify-between px-3 py-2 text-sm text-left transition-colors w-full",
-   selectedIndex === index ? "bg-neutral-200" : "hover:bg-neutral-100",
- )}
+ className={cn([
+   "flex items-center justify-between px-3 py-2 text-sm text-left transition-colors w-full",
+   selectedIndex === index ? "bg-neutral-200" : "hover:bg-neutral-100",
+ ])}

Also applies to: 366-369

apps/desktop/src/components/main/body/contacts/shared.tsx (1)

88-91: Header title truncation removed—verify overflow in small widths

With h-12, long titles may clip. Parent has min-w-0, but consider responsive truncate to avoid overflow in tight layouts.

- <h3 className="text-sm font-medium">{title}</h3>
+ <h3 className="text-sm font-medium md:truncate">{title}</h3>
apps/desktop/src/components/main/body/sessions/outer-header/metadata/shared.ts (1)

34-35: Remove redundant type casting.

The explicit as string | null | undefined casts are redundant since useMeetingMetadata already returns MeetingMetadata | null and the interface defines these fields with the same type.

Apply this diff:

   return {
     tood: eventNote ?? "",
     meeting_link: meetingLink,
     title,
     started_at: startedAt,
     ended_at: endedAt,
-    location: location as string | null | undefined,
-    description: description as string | null | undefined,
+    location,
+    description,
   };
apps/desktop/src/components/main/body/contacts/details.tsx (1)

98-109: AI-generated summary placeholder is well-documented.

The placeholder clearly communicates the intended future functionality. Consider tracking this implementation in a TODO or issue.

Would you like me to open an issue to track the implementation of the AI-generated summary feature?

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 87d2b64 and 690dad4.

📒 Files selected for processing (27)
  • apps/desktop/src/components/main/body/calendars.tsx (0 hunks)
  • apps/desktop/src/components/main/body/calendars/calendar-checkbox-row.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/calendar-day.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/day-events.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/day-more.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/day-sessions.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/index.tsx (1 hunks)
  • apps/desktop/src/components/main/body/contacts/details.tsx (9 hunks)
  • apps/desktop/src/components/main/body/contacts/index.tsx (1 hunks)
  • apps/desktop/src/components/main/body/contacts/organization-details.tsx (4 hunks)
  • apps/desktop/src/components/main/body/contacts/organizations.tsx (1 hunks)
  • apps/desktop/src/components/main/body/contacts/shared.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/floating/shared.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/date.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/description.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/index.tsx (3 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/link.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/others.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/participants.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/shared.ts (3 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/overflow.tsx (3 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/share.tsx (2 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx (2 hunks)
  • apps/desktop/src/components/settings/calendar.tsx (0 hunks)
  • apps/desktop/src/store/tinybase/persisted.ts (2 hunks)
  • plugins/notification/src/handler.rs (1 hunks)
💤 Files with no reviewable changes (2)
  • apps/desktop/src/components/settings/calendar.tsx
  • apps/desktop/src/components/main/body/calendars.tsx
🚧 Files skipped from review as they are similar to previous changes (11)
  • plugins/notification/src/handler.rs
  • apps/desktop/src/components/main/body/sessions/outer-header/share.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/description.tsx
  • apps/desktop/src/components/main/body/calendars/day-sessions.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/overflow.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/link.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/others.tsx
  • apps/desktop/src/components/main/body/sessions/floating/shared.tsx
  • apps/desktop/src/components/main/body/calendars/calendar-day.tsx
  • apps/desktop/src/components/main/body/calendars/day-events.tsx
  • apps/desktop/src/components/main/body/calendars/day-more.tsx
🧰 Additional context used
📓 Path-based instructions (1)
apps/desktop/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (apps/desktop/.cursor/rules/style.mdc)

apps/desktop/**/*.{tsx,jsx}: When there are many Tailwind classNames with conditional logic, use the utility cn imported as import { cn } from "@hypr/utils"
When using cn for Tailwind classNames, always pass an array
Group Tailwind classNames by logical sections when using cn (split array items by grouping)

Files:

  • apps/desktop/src/components/main/body/contacts/shared.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/date.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx
  • apps/desktop/src/components/main/body/contacts/organizations.tsx
  • apps/desktop/src/components/main/body/contacts/index.tsx
  • apps/desktop/src/components/main/body/contacts/details.tsx
  • apps/desktop/src/components/main/body/calendars/calendar-checkbox-row.tsx
  • apps/desktop/src/components/main/body/calendars/index.tsx
  • apps/desktop/src/components/main/body/contacts/organization-details.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/index.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/participants.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx
🧬 Code graph analysis (8)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/date.tsx (1)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/shared.ts (1)
  • useMeetingMetadata (13-37)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (1)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/shared.ts (1)
  • useMeetingMetadata (13-37)
apps/desktop/src/components/main/body/contacts/details.tsx (1)
apps/desktop/src/components/main/body/contacts/shared.tsx (1)
  • getInitials (7-17)
apps/desktop/src/components/main/body/calendars/index.tsx (6)
apps/desktop/src/components/main/body/shared.tsx (2)
  • TabItem (26-26)
  • TabItemBase (28-103)
apps/desktop/src/store/zustand/tabs/index.ts (1)
  • useTabs (21-30)
apps/desktop/src/components/main/body/index.tsx (1)
  • StandardTabWrapper (272-293)
apps/desktop/src/components/main/body/calendars/calendar-checkbox-row.tsx (1)
  • CalendarCheckboxRow (5-20)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/desktop/src/components/main/body/calendars/calendar-day.tsx (1)
  • TabContentCalendarDay (10-143)
apps/desktop/src/components/main/body/contacts/organization-details.tsx (1)
apps/desktop/src/components/main/body/contacts/shared.tsx (1)
  • getInitials (7-17)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/index.tsx (2)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (1)
  • MeetingLocation (5-20)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/description.tsx (1)
  • MeetingDescription (3-15)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/shared.ts (1)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/index.tsx (1)
  • MeetingMetadata (15-91)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/participants.tsx (3)
apps/desktop/src/components/main/body/contacts/shared.tsx (1)
  • getInitials (7-17)
apps/desktop/src/store/zustand/tabs/index.ts (1)
  • useTabs (21-30)
packages/utils/src/cn.ts (1)
  • cn (20-22)
⏰ 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: ci (macos, macos-14)
🔇 Additional comments (11)
apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx (1)

45-47: Excellent fix! Previous issue resolved with consistent pattern.

The changes properly address the previous review comment about the submenu not closing after folder selection. The implementation correctly:

  • Accepts the optional setOpen prop in the function signature
  • Passes it through to SearchableFolderContent
  • Maintains pattern consistency with SearchableFolderDropdown (lines 37-39)

The submenu will now close after folder selection, providing consistent UX across both dropdown and submenu variants.

Also applies to: 60-62

apps/desktop/src/components/main/body/contacts/index.tsx (1)

129-134: Good wiring for per-person selection from org details

Passing onPersonClick={setSelectedPerson} cleanly switches the right pane to person details. LGTM.

Confirm OrganizationDetailsColumn prop is optional and typed as onPersonClick?: (personId: string) => void.

apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (1)

5-20: LGTM! Null handling is correct.

The null guard on Line 8 properly prevents runtime errors when metadata is unavailable. The component cleanly renders the location with appropriate styling.

apps/desktop/src/components/main/body/sessions/outer-header/metadata/index.tsx (2)

29-31: Clean conditional rendering logic.

Deriving these boolean flags upfront makes the JSX more readable and avoids repeated null checks.


51-54: Correct usage of cn utility.

The cn utility is properly used with an array to group Tailwind classes, following the coding guidelines.

apps/desktop/src/components/main/body/contacts/details.tsx (2)

62-80: Clean header layout with inline editing.

The refactor to inline editing with direct persisted data binding improves the UX by removing the separate edit mode. The avatar and initials rendering is well-structured.


291-317: Memo field is properly defined in the schema.

Verification confirms the memo field exists in both the Zod schema (as optional string) and TinyBase schema definitions in apps/desktop/src/store/tinybase/persisted.ts (lines 45, 174). The component's usage is correct.

apps/desktop/src/components/main/body/contacts/organization-details.tsx (4)

45-46: Proper null guards applied.

The optional chaining and nullish coalescing on peopleInOrg?.length ?? 0 correctly prevent runtime errors when the store value is undefined.


57-127: Grid layout and null handling are well-implemented.

The 3-column grid layout provides a clean visual structure, and the consistent null guard on Line 57 prevents crashes. The empty state message is user-friendly.


88-119: Event propagation correctly stopped on action buttons.

Both the Mail and LinkedIn buttons properly call e.stopPropagation() to prevent the parent onPersonClick from firing. The LinkedIn URL handling also correctly normalizes the username format.


170-190: Inline editing pattern is consistent.

The EditableOrganizationNameField follows the same pattern as the person detail fields, providing a consistent editing experience across the contacts UI.

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

Caution

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

⚠️ Outside diff range comments (2)
apps/desktop/src/components/main/body/contacts/details.tsx (1)

319-365: Verify organization selector handles "Create new" action.

Lines 438-454 show a "Create" button when no organizations match the search term, but the onClick handler is empty (onClick={() => {}}). This means users can see the option but clicking it does nothing.

Implement the create functionality or remove the "Create" button to avoid misleading users:

-                  onClick={() => {}}
+                  onClick={() => {
+                    // TODO: Implement organization creation
+                    // For now, you could create the org and call onChange with the new ID
+                  }}

Alternatively, if creation isn't ready yet, remove this button entirely until the functionality is implemented.

apps/desktop/src/components/main/body/sessions/outer-header/metadata/others.tsx (1)

11-105: Significant code duplication with participants.tsx.

The MeetingParticipant interface and ParticipantChip component are nearly identical to those in participants.tsx (lines 15-109). This duplication increases maintenance burden and the risk of divergent behavior.

Consider extracting the shared types and component to a common module:

// shared-participant-ui.tsx
export interface MeetingParticipant {
  id: string;
  full_name?: string | null;
  email?: string | null;
  job_title?: string | null;
  linkedin_username?: string | null;
  organization?: {
    id: string;
    name: string;
  } | null;
}

export interface ParticipantChipProps {
  participant: MeetingParticipant;
  currentUserId?: string;
  attended?: boolean;
  onClick?: () => void;
  onRemove?: () => void;
}

export function ParticipantChip({ ... }: ParticipantChipProps) {
  // existing implementation
}

Then import from both others.tsx and participants.tsx.

♻️ Duplicate comments (3)
apps/desktop/src/components/main/body/calendars/calendar-day.tsx (1)

58-64: Fix EVENT_HEIGHT constant to match actual rendered height.

The EVENT_HEIGHT is set to 20, but the actual button components (TabContentCalendarDayEvents and TabContentCalendarDaySessions in their respective files) use the h-6 Tailwind class, which equals 24px. This mismatch will cause incorrect calculation of maxVisibleItems, potentially showing the "+X more" indicator prematurely or rendering more items than can fit.

Apply this diff to fix the height constant:

-        const EVENT_HEIGHT = 20; // height of each event item (h-5)
+        const EVENT_HEIGHT = 24; // height of each event item (h-6)
         const SPACING = 4; // space-y-1
apps/desktop/src/components/main/body/sessions/outer-header/metadata/others.tsx (1)

38-42: onClick prop not wired to the chip wrapper.

The ParticipantChip receives an onClick prop but the wrapper div on line 38 never invokes it, making chips non-interactive despite accepting the handler. This is the same issue flagged in participants.tsx.

Apply this diff to wire the onClick handler:

       <HoverCardTrigger asChild>
-        <div className="group inline-flex items-center gap-1.5 px-1 py-0.5 bg-neutral-200 rounded-md hover:bg-neutral-200 transition-colors cursor-pointer">
+        <div
+          onClick={onClick}
+          className="group inline-flex items-center gap-1.5 px-1 py-0.5 bg-neutral-200 rounded-md hover:bg-neutral-200 transition-colors cursor-pointer"
+        >
apps/desktop/src/components/main/body/sessions/outer-header/metadata/participants.tsx (1)

42-63: onClick prop not wired to the chip wrapper.

The ParticipantChip component accepts an onClick prop (line 31) and passes it to handleParticipantClick (line 332), but the wrapper div on line 42 never invokes it. This makes the chip non-interactive for the intended click action, despite the cursor-pointer styling suggesting it should be clickable.

Apply this diff to wire the onClick handler:

       <HoverCardTrigger asChild>
-        <div className="group inline-flex items-center gap-1.5 px-1 py-0.5 bg-neutral-200 rounded-md hover:bg-neutral-200 transition-colors cursor-pointer">
+        <div
+          onClick={onClick}
+          className="group inline-flex items-center gap-1.5 px-1 py-0.5 bg-neutral-200 rounded-md hover:bg-neutral-200 transition-colors cursor-pointer"
+        >

Note: Ensure the remove button continues to call stopPropagation() (line 55) to prevent triggering the chip click when removing.

🧹 Nitpick comments (3)
apps/desktop/src/components/main/body/calendars/index.tsx (1)

56-64: Consider syncing selectedCalendars with new calendars.

The selectedCalendars state is initialized once with new Set(calendarIds), but won't automatically include calendars added after the component mounts. Users would need to manually toggle newly added calendars.

Consider using a useEffect to sync the state when new calendars are added:

  const [selectedCalendars, setSelectedCalendars] = useState<Set<string>>(() => new Set(calendarIds));
+
+ useEffect(() => {
+   setSelectedCalendars(prev => {
+     const updated = new Set(prev);
+     calendarIds.forEach(id => updated.add(id));
+     return updated;
+   });
+ }, [calendarIds]);

This ensures newly created/synced calendars are automatically selected and visible.

apps/desktop/src/components/main/body/contacts/organization-details.tsx (1)

59-59: Consider responsive grid layout.

The grid uses a fixed 3-column layout (grid-cols-3), which may not work well on smaller viewports or when the sidebar is expanded.

Consider using responsive grid classes:

-                      <div className="grid grid-cols-3 gap-4">
+                      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">

This would adapt the layout to available space, showing 1 column on small screens, 2 on medium, and 3 on large screens.

apps/desktop/src/components/main/body/sessions/outer-header/metadata/participants.tsx (1)

161-236: Remove unnecessary async modifier from synchronous queryFn.

The queryFn is marked async but performs only synchronous TinyBase store operations. The async keyword adds no value here and may mislead readers into expecting asynchronous behavior.

Apply this diff to remove the async modifier:

   const participantSearch = useQuery({
     enabled: !!store && !!indexes && !!participantSearchQuery.trim(),
     deps: [store, indexes, participantSearchQuery, sessionId] as const,
-    queryFn: async (store, indexes, query, sessionId) => {
+    queryFn: (store, indexes, query, sessionId) => {
       const results: MeetingParticipant[] = [];
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1dda9b2 and c56ae7a.

📒 Files selected for processing (27)
  • apps/desktop/src/components/main/body/calendars.tsx (0 hunks)
  • apps/desktop/src/components/main/body/calendars/calendar-checkbox-row.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/calendar-day.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/day-events.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/day-more.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/day-sessions.tsx (1 hunks)
  • apps/desktop/src/components/main/body/calendars/index.tsx (1 hunks)
  • apps/desktop/src/components/main/body/contacts/details.tsx (9 hunks)
  • apps/desktop/src/components/main/body/contacts/index.tsx (1 hunks)
  • apps/desktop/src/components/main/body/contacts/organization-details.tsx (4 hunks)
  • apps/desktop/src/components/main/body/contacts/organizations.tsx (1 hunks)
  • apps/desktop/src/components/main/body/contacts/shared.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/floating/shared.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/date.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/description.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/index.tsx (3 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/link.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/others.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/participants.tsx (1 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/shared.ts (3 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/overflow.tsx (3 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/share.tsx (2 hunks)
  • apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx (2 hunks)
  • apps/desktop/src/components/settings/calendar.tsx (0 hunks)
  • apps/desktop/src/store/tinybase/persisted.ts (2 hunks)
  • plugins/notification/src/handler.rs (1 hunks)
💤 Files with no reviewable changes (2)
  • apps/desktop/src/components/settings/calendar.tsx
  • apps/desktop/src/components/main/body/calendars.tsx
🚧 Files skipped from review as they are similar to previous changes (12)
  • plugins/notification/src/handler.rs
  • apps/desktop/src/components/main/body/contacts/index.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/share.tsx
  • apps/desktop/src/store/tinybase/persisted.ts
  • apps/desktop/src/components/main/body/calendars/day-more.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/shared.ts
  • apps/desktop/src/components/main/body/calendars/calendar-checkbox-row.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/date.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/description.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/overflow.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/link.tsx
  • apps/desktop/src/components/main/body/calendars/day-events.tsx
🧰 Additional context used
📓 Path-based instructions (1)
apps/desktop/**/*.{tsx,jsx}

📄 CodeRabbit inference engine (apps/desktop/.cursor/rules/style.mdc)

apps/desktop/**/*.{tsx,jsx}: When there are many Tailwind classNames with conditional logic, use the utility cn imported as import { cn } from "@hypr/utils"
When using cn for Tailwind classNames, always pass an array
Group Tailwind classNames by logical sections when using cn (split array items by grouping)

Files:

  • apps/desktop/src/components/main/body/contacts/organization-details.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/others.tsx
  • apps/desktop/src/components/main/body/contacts/shared.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/index.tsx
  • apps/desktop/src/components/main/body/sessions/floating/shared.tsx
  • apps/desktop/src/components/main/body/calendars/calendar-day.tsx
  • apps/desktop/src/components/main/body/calendars/day-sessions.tsx
  • apps/desktop/src/components/main/body/contacts/organizations.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx
  • apps/desktop/src/components/main/body/calendars/index.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/participants.tsx
  • apps/desktop/src/components/main/body/contacts/details.tsx
🧬 Code graph analysis (8)
apps/desktop/src/components/main/body/contacts/organization-details.tsx (1)
apps/desktop/src/components/main/body/contacts/shared.tsx (1)
  • getInitials (7-17)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/index.tsx (4)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (1)
  • MeetingLocation (5-20)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/link.tsx (1)
  • MeetingLink (15-58)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/date.tsx (1)
  • MeetingDate (5-17)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/description.tsx (1)
  • MeetingDescription (3-15)
apps/desktop/src/components/main/body/calendars/calendar-day.tsx (4)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/desktop/src/components/main/body/calendars/day-events.tsx (1)
  • TabContentCalendarDayEvents (11-91)
apps/desktop/src/components/main/body/calendars/day-sessions.tsx (1)
  • TabContentCalendarDaySessions (8-25)
apps/desktop/src/components/main/body/calendars/day-more.tsx (1)
  • TabContentCalendarDayMore (10-45)
apps/desktop/src/components/main/body/calendars/day-sessions.tsx (1)
apps/desktop/src/store/zustand/tabs/index.ts (1)
  • useTabs (21-30)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (1)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/shared.ts (1)
  • useMeetingMetadata (13-37)
apps/desktop/src/components/main/body/calendars/index.tsx (5)
apps/desktop/src/components/main/body/shared.tsx (2)
  • TabItem (26-26)
  • TabItemBase (28-103)
apps/desktop/src/store/zustand/tabs/index.ts (1)
  • useTabs (21-30)
apps/desktop/src/components/main/body/index.tsx (1)
  • StandardTabWrapper (272-293)
apps/desktop/src/components/main/body/calendars/calendar-checkbox-row.tsx (1)
  • CalendarCheckboxRow (5-24)
apps/desktop/src/components/main/body/calendars/calendar-day.tsx (1)
  • TabContentCalendarDay (10-143)
apps/desktop/src/components/main/body/sessions/outer-header/metadata/participants.tsx (3)
apps/desktop/src/components/main/body/contacts/shared.tsx (1)
  • getInitials (7-17)
apps/desktop/src/store/zustand/tabs/index.ts (1)
  • useTabs (21-30)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/desktop/src/components/main/body/contacts/details.tsx (1)
apps/desktop/src/components/main/body/contacts/shared.tsx (1)
  • getInitials (7-17)
🔇 Additional comments (23)
apps/desktop/src/components/main/body/sessions/outer-header/shared/folder.tsx (1)

45-47: Previous issue successfully resolved!

The submenu now correctly accepts and forwards the setOpen prop to SearchableFolderContent, ensuring the parent menu closes after folder selection. The implementation matches the dropdown variant pattern and maintains consistent UX across both menu types.

Also applies to: 60-62

apps/desktop/src/components/main/body/sessions/floating/shared.tsx (3)

20-21: LGTM!

The styling enhancements improve the button appearance consistently with the PR's UI polish objectives.


26-31: LGTM! Nice animation enhancement.

The two-layer pulsing animation creates a more noticeable visual indicator. The nested absolute inset-0 divs correctly overlap to produce the pulsing effect.


37-41: Verify edge case handling for formatTime.

The core logic correctly formats valid positive seconds into MM:SS format. However, consider whether edge cases need handling:

  • Negative values would produce unexpected results
  • NaN, undefined, or null would return "NaN:NaN"
  • Infinity would return "Infinity:NaN"

If the function is always called with valid positive numbers (e.g., from a timer), the current implementation is fine. Otherwise, consider adding input validation.

apps/desktop/src/components/main/body/contacts/shared.tsx (1)

89-89: Verify removal of truncate is intentional.

Removing truncate from the title will cause long organization/contact names to wrap or overflow instead of being truncated with an ellipsis. This could impact the layout, especially with lengthy names.

Please confirm this change is intentional and that the layout handles long titles appropriately. Consider testing with long organization names to ensure the UI remains usable.

apps/desktop/src/components/main/body/contacts/organizations.tsx (1)

48-57: LGTM! Proper truncation handling.

The addition of truncate to the button and shrink-0 to the icon ensures that long labels are properly truncated while keeping the icon visible. This follows Tailwind best practices and the coding guidelines.

apps/desktop/src/components/main/body/calendars/day-sessions.tsx (1)

8-24: LGTM! Proper null-safety guards in place.

The component correctly guards against missing session data using optional chaining, and the event fetching logic safely handles cases where event_id is undefined by using the ?? "" fallback. The title display logic appropriately cascades through event title, session title, and "Untitled" fallback.

apps/desktop/src/components/main/body/calendars/calendar-day.tsx (1)

99-123: LGTM! Proper use of cn utility.

The component correctly uses the cn utility with arrays and groups Tailwind classes by logical sections (layout, borders, background), which aligns with the coding guidelines for this path pattern.

apps/desktop/src/components/main/body/contacts/details.tsx (2)

62-80: LGTM! Clean header layout with inline editing.

The header section properly integrates the avatar with the inline editable name field and user badge. The layout is well-structured and guards appropriately check for selectedPersonData existence.


291-317: LGTM! Proper Textarea usage for memo field.

The EditablePersonMemoField correctly uses the Textarea component from @hypr/ui with appropriate styling to disable borders and shadows for an inline editing experience. The field properly binds to the persisted memo cell.

apps/desktop/src/components/main/body/calendars/index.tsx (1)

162-176: LGTM! Proper use of cn utility.

The weekday header properly uses the cn utility with an array, grouping classes by logical sections (layout, styling, conditional colors). This follows the coding guidelines.

apps/desktop/src/components/main/body/contacts/organization-details.tsx (2)

35-51: LGTM! Clean header with inline editing and proper guards.

The header section correctly uses EditableOrganizationNameField for inline name editing and properly guards the people count with optional chaining (peopleInOrg?.length ?? 0), preventing runtime errors when the data is unavailable.


88-119: LGTM! Proper event handling and URL normalization.

The action buttons correctly:

  • Stop event propagation with e.stopPropagation() to prevent triggering the parent card's onPersonClick
  • Handle the openUrl promise with void to avoid unhandled promise warnings
  • Normalize LinkedIn URLs to handle both full URLs and username-only formats
apps/desktop/src/components/main/body/sessions/outer-header/metadata/others.tsx (1)

177-180: Follow coding guidelines for cn() usage.

When using cn for Tailwind classNames, always pass an array. The current implementation correctly follows this pattern.

Based on coding guidelines.

apps/desktop/src/components/main/body/sessions/outer-header/metadata/location.tsx (2)

5-10: LGTM! Null handling properly implemented.

The component correctly handles null metadata by checking meta?.location before accessing the location property. This addresses the previous review concern about unsafe non-null assertions.


12-19: Clean and accessible UI implementation.

The location display with icon and truncation is well-implemented. The flex layout and truncate class ensure the UI degrades gracefully with long location strings.

apps/desktop/src/components/main/body/sessions/outer-header/metadata/index.tsx (3)

29-31: Good use of derived boolean flags.

Deriving hasLocation, hasMeetingLink, and hasDescription from the metadata makes the conditional rendering logic clearer and more maintainable.


57-86: Conditional rendering structure improves UX.

The conditional sections with inline dividers ensure that only relevant metadata is displayed, preventing empty sections and unnecessary dividers. The layout adapts cleanly to available data.


51-54: Proper cn() usage follows coding guidelines.

The cn() utility is correctly invoked with an array, grouping Tailwind classes by logical sections (flex, shadow/size, padding).

Based on coding guidelines.

apps/desktop/src/components/main/body/sessions/outer-header/metadata/participants.tsx (4)

321-324: Proper cn() usage follows coding guidelines.

The cn() utility is correctly invoked with an array, and classes are grouped logically (layout, conditional border).

Based on coding guidelines.


128-156: Efficient participant data enrichment.

The useMemo hook correctly derives participant data from mappings, humans, and organizations tables. The dependency array ensures recomputation only when needed.


238-265: Well-implemented keyboard navigation.

The handleKeyDown function provides intuitive keyboard controls: Backspace to remove the last chip, Arrow keys for dropdown navigation, Enter to select, and Escape to dismiss. This enhances accessibility and UX.


289-289: No action needed; review comment is based on incorrect assumptions about application context.

This is a Tauri desktop application, not a web browser application. Tauri bundles a controlled Chromium runtime (Windows/macOS) or WebKit (Linux), and crypto.randomUUID() is fully supported in all modern versions. The TypeScript target is ES2020, which natively supports this API. Browser compatibility concerns about older environments or non-secure origins are not applicable to desktop Tauri applications. The code uses crypto.randomUUID() consistently throughout the codebase without polyfills, confirming it's safe for this platform.

Likely an incorrect or invalid review comment.

@yujonglee yujonglee merged commit 3800c4f into main Oct 24, 2025
7 checks passed
@yujonglee yujonglee deleted the jj-branch-27 branch October 24, 2025 13:11
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.

3 participants