Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refac: meetings page #674

Merged
merged 12 commits into from
Dec 9, 2024
Merged

refac: meetings page #674

merged 12 commits into from
Dec 9, 2024

Conversation

deewakar-k
Copy link
Contributor

What did you ship?

Fixes:

  • #XXX (GitHub issue number)
  • MAR-XXX (Linear issue number - should be visible at the bottom of the GitHub issue description)

Checklist:

  • I have self-reviewed the code (A decent size PR without self-review might be rejected)
  • I pinky swear that my codes gonna work as I have testing every possible scenario.
  • I ignored Coderabbit suggestion because it does not make any sense.
  • I took Coderabbit suggestion under consideration as some of it makes sense.
  • I have commented my code, particularly in hard-to-understand areas.

OR:

  • shut up and let me cook.

Copy link

coderabbitai bot commented Dec 6, 2024

📝 Walkthrough

Walkthrough

The pull request introduces several updates across multiple components within the frontend application. Key changes include enhancements to the ItemPage component for improved prop handling in the MeetingPage, the introduction of new components like ActionHeader, MetaDetails, and SpaceEditor, and the restructuring of existing components such as MeetNotes and MeetingsPage to utilize custom hooks for better state management. Additionally, a new reducer for managing meeting state is added, along with several utility functions and hooks to streamline functionality related to meetings.

Changes

File Path Change Summary
apps/frontend/src/app/(app)/spaces/[spaceId]/blocks/[blockId]/items/[itemId]/page.tsx Updated ItemPage to pass spaceId and blockId to MeetingPage for enhanced data context.
apps/frontend/src/app/api/auth/google-calendar/url/route.ts Added console log and changed Google client ID environment variable to public-facing.
apps/frontend/src/components/ActionHeader.tsx Introduced ActionHeader component with props for loading state and actions.
apps/frontend/src/components/Notes/NotesPage.tsx Replaced NoteHeader with ActionHeader, updated close and add handlers.
apps/frontend/src/components/Notes/components/NoteModal/NotesModal.tsx Updated import path for NoteLink.
apps/frontend/src/components/editor/space-editor.tsx Added SpaceEditor component for editing meeting notes with metadata display.
apps/frontend/src/components/header/action-header.tsx Renamed NoteActions to ActionHeader, updated props interface and internal logic.
apps/frontend/src/components/header/meta-details.tsx Introduced MetaDetails component for displaying meeting metadata.
apps/frontend/src/components/meetings/InitialMeet.tsx Enhanced loading message styling and removed unnecessary console log.
apps/frontend/src/components/meetings/MeetNotes.tsx Restructured to use custom hooks for state management and effects.
apps/frontend/src/components/meetings/MeetingsPage.tsx Updated to use new state management hooks and refined rendering logic.
apps/frontend/src/components/meetings/Stack.tsx Removed Stack component.
apps/frontend/src/components/meetings/reducer.ts Introduced a new reducer for managing meeting state with actions.
apps/frontend/src/components/meetings/useMeetEffects.ts Added custom hook for managing meeting-related effects.
apps/frontend/src/components/meetings/useMeetHandlers.ts Introduced hook for handling meeting functionalities.
apps/frontend/src/components/meetings/useMeetState.ts Added custom hook for managing meeting state with useReducer.
apps/frontend/src/components/modals/StackModal.tsx Introduced StackModal component for displaying a modal with notes.
apps/frontend/src/utils/meet.ts Updated calculateMeetDuration function to accept string parameters and return formatted duration.

Possibly related PRs

Suggested labels

ready to be merged

Suggested reviewers

  • sajdakabir
  • oliursahin

Poem

🐇 In the meadow where meetings bloom,
New components chase away the gloom.
With headers and modals, oh what a sight,
Editing notes feels just right!
So hop along, let’s code and play,
For every change brings a brighter day! 🌼


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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

🧹 Outside diff range and nitpick comments (21)
apps/frontend/src/components/meetings/MeetNotes.tsx (1)

20-26: Consider including all used meetData properties in the dependency array.

Currently, the useEffect depends on [meetData?.id], but accesses other properties like meetData.title and meetData.description. To ensure the effect runs when any relevant meetData property changes, consider including meetData in the dependency array.

Apply this diff to update the dependency array:

- }, [meetData?.id])
+ }, [meetData])
apps/frontend/src/components/header/meta-details.tsx (1)

11-13: Handle cases where url is undefined to prevent rendering issues.

Since url is optional, if it's undefined, the anchor tag's href will be undefined, which may cause unexpected behavior. Consider conditionally rendering the anchor tag only when url is provided.

Apply this diff to handle undefined url:

       <Details>
-        <a href={url} target="_blank" rel="noopener noreferrer" className="flex items-center gap-2">
-          <span>{url}</span>, {duration}
-        </a>
+        {url ? (
+          <a href={url} target="_blank" rel="noopener noreferrer" className="flex items-center gap-2">
+            <span>{url}</span>, {duration}
+          </a>
+        ) : (
+          <span>{duration}</span>
+        )}
       </Details>
apps/frontend/src/app/api/auth/google-calendar/url/route.ts (1)

6-6: Remove debugging console.log statement.

The console.log("hi from api") appears to be for debugging and should be removed to clean up console output in production.

Apply this diff to remove the statement:

   export async function GET(request: NextRequest) {
-    console.log("hi from api")
apps/frontend/src/components/header/action-header.tsx (1)

Line range hint 23-27: Handle the case when onAdd is undefined.

Since onAdd is optional, if it's not provided, the "Add" button will have an onClick of undefined, which may lead to unexpected behavior. Consider disabling the button or conditionally rendering it when onAdd is available.

Apply this diff to conditionally render the "Add" button:

       {!loading ? (
+        onAdd ? (
           <button
             onClick={onAdd}
             className="hover-bg flex items-center gap-1 truncate rounded-md px-1 text-secondary-foreground"
           >
             <PlusIcon />
           </button>
+        ) : null
         ) : (
           <div className="flex items-center gap-1 rounded-md px-1 text-secondary-foreground">
             <span>loading...</span>
           </div>
         )}
apps/frontend/src/components/ActionHeader.tsx (2)

9-9: Consider using a more specific return type for onAdd

The Promise<any> type is too permissive and could lead to type-safety issues. Consider defining a more specific return type based on the actual value being returned.

-  onAdd: () => Promise<any>
+  onAdd: () => Promise<void> // or the specific return type

23-36: Enhance accessibility for loading state

The loading state could be improved for better accessibility:

  1. Add aria-busy attribute
  2. Use a more semantic loading indicator
-        <div className="flex items-center gap-1 rounded-md px-1 text-secondary-foreground">
-          <span>loading...</span>
+        <div 
+          className="flex items-center gap-1 rounded-md px-1 text-secondary-foreground"
+          role="status"
+          aria-busy="true"
+        >
+          <span className="sr-only">Loading</span>
+          <span aria-hidden="true">loading...</span>
apps/frontend/src/components/meetings/useMeetState.ts (2)

9-26: Consider memoizing setter functions

Since these setter functions are passed down to child components, memoizing them with useCallback would prevent unnecessary re-renders.

+import { useReducer, useCallback } from "react"

-  const setMeet = (newMeet: Meet) =>
+  const setMeet = useCallback((newMeet: Meet) =>
     dispatch({ type: "SET_MEET", payload: newMeet })
+  , [])

-  const setTitle = (newTitle: string) =>
+  const setTitle = useCallback((newTitle: string) =>
     dispatch({ type: "SET_TITLE", payload: newTitle })
+  , [])

1-5: Add type definitions for action creators

Consider adding TypeScript type definitions for the action creators to improve type safety and developer experience.

type MeetAction = 
  | { type: "SET_MEET"; payload: Meet }
  | { type: "SET_TITLE"; payload: string }
  | { type: "SET_CONTENT"; payload: string }
  // ... other action types

const setMeet = useCallback((newMeet: Meet): void => {
  dispatch({ type: "SET_MEET", payload: newMeet } as MeetAction)
}, [])
apps/frontend/src/components/modals/StackModal.tsx (1)

33-50: Enhance modal accessibility and performance

The modal container needs accessibility improvements and potential performance optimizations:

  1. Add ARIA attributes for modal role
  2. Consider virtualizing the list if it can grow large
-<div className="fixed right-[150px] top-[38%] z-50 h-[535px] w-[371px] -translate-y-1/2 overflow-y-scroll rounded-lg border border-border bg-background p-6">
+<div 
+  role="dialog"
+  aria-modal="true"
+  aria-label="Stack of Notes"
+  className="fixed right-[150px] top-[38%] z-50 h-[535px] w-[371px] -translate-y-1/2 overflow-y-auto rounded-lg border border-border bg-background p-6">
apps/frontend/src/components/meetings/reducer.ts (2)

26-36: Document initial state choices and add error handling

The initial state could benefit from documentation explaining certain choices (e.g., why content is initialized with "<p></p>"). Consider adding error handling state.

 const initialState: MeetState = {
   meet: null,
   title: "",
   content: "<p></p>",  // Consider documenting why this specific initial value
   isSaved: true,
   isLoading: false,
   notFound: false,
+  error: null,  // Add error state
   closeToggle: true,
   isInitialLoad: true,
   isEditingTitle: false,
 }

15-24: Consider adding error action type

The action types could be extended to handle error states for better error management.

 type Action =
   | { type: "SET_MEET"; payload: Meet }
   | { type: "SET_TITLE"; payload: string }
   | { type: "SET_CONTENT"; payload: string }
   | { type: "SET_SAVED"; payload: boolean }
   | { type: "SET_LOADING"; payload: boolean }
   | { type: "SET_NOT_FOUND"; payload: boolean }
   | { type: "SET_CLOSED"; payload: boolean }
   | { type: "SET_INITIAL_LOAD"; payload: boolean }
-  | { type: "SET_EDITING_TITLE"; payload: boolean }
+  | { type: "SET_EDITING_TITLE"; payload: boolean }
+  | { type: "SET_ERROR"; payload: Error | null }
apps/frontend/src/utils/meet.ts (1)

40-42: Consider using date-fns for duration calculation

Since the codebase already uses date-fns (imported at the top), consider using its differenceInMinutes function for consistency and reliability.

-  const minutes = Math.round(
-    (endDate.getTime() - startDate.getTime()) / (1000 * 60)
-  )
+  const minutes = differenceInMinutes(endDate, startDate)
apps/frontend/src/components/editor/space-editor.tsx (1)

10-20: Improve TypeScript type safety and props consistency

The props interface could be improved in the following ways:

  1. Add explicit return type for handlers
  2. Be consistent with optional props - some handlers are optional while others aren't, but there's no clear pattern why
 interface SpaceEditorProps {
   note: Meet
   title: string
   editor: Editor | null
-  handleTitleChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void
-  handleTextareaKeyDown: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void
-  handleTitleFocus?: () => void
-  handleTitleBlur?: () => void
-  handleSaveNote?: () => void
+  handleTitleChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => Promise<void>
+  handleTextareaKeyDown: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void
+  handleTitleFocus: () => void
+  handleTitleBlur: () => void
+  handleSaveNote: () => Promise<void>
   textareaRef: React.RefObject<HTMLTextAreaElement>
 }
apps/frontend/src/components/meetings/useMeetHandlers.ts (2)

12-21: Consider adding retry logic for server operations

The saveMeetToServer function could benefit from retry logic for transient failures, especially since it's a network operation.

 const saveMeetToServer = useCallback(
   async (meet: Meet): Promise<void> => {
+    const maxRetries = 3;
+    let retries = 0;
     try {
-      await updateMeet(session, meet, meet.id)
+      while (retries < maxRetries) {
+        try {
+          await updateMeet(session, meet, meet.id)
+          break
+        } catch (error) {
+          retries++
+          if (retries === maxRetries) throw error
+          await new Promise(resolve => setTimeout(resolve, 1000 * retries))
+        }
+      }
     } catch (error) {
       console.error("Error saving meet:", error)
+      throw error
     }
   },
   [session, updateMeet]
 )

23-69: Reduce code duplication in handlers

The handleSaveContent and handleSaveTitle functions share similar structure. Consider extracting the common logic into a reusable function.

+ const handleSaveField = useCallback(
+   async (fieldName: 'title' | 'description', value: string) => {
+     if (!meet) return
+     try {
+       dispatch({ type: "SET_SAVED", payload: false })
+       await updateMeet(session, { ...meet, [fieldName]: value }, meet.id)
+       dispatch({ type: "SET_SAVED", payload: true })
+     } catch (error) {
+       console.error(`Error saving ${fieldName}:`, error)
+     }
+   },
+   [meet, session, updateMeet, dispatch]
+ )

  const handleSaveTitle = useCallback(
    async (newTitle: string) => {
-     if (!meet) return
-     try {
-       dispatch({ type: "SET_SAVED", payload: false })
-       await updateMeet(session, { ...meet, title: newTitle }, meet.id)
-       dispatch({ type: "SET_SAVED", payload: true })
-     } catch (error) {
-       console.error("Error saving title:", error)
-     }
+     await handleSaveField('title', newTitle)
    },
-   [meet, session, updateMeet, dispatch]
+   [handleSaveField]
  )
apps/frontend/src/components/meetings/MeetingsPage.tsx (2)

26-28: Consider using a more descriptive state name

The closeToggle state name could be more descriptive. Consider something like isStackModalVisible to better reflect its purpose.

- const [closeToggle, setCloseToggle] = usePersistedState("closeToggle", true)
+ const [isStackModalVisible, setIsStackModalVisible] = usePersistedState("isStackModalVisible", false)

74-76: Add loading skeleton for better UX

Instead of showing a simple "loading..." text, consider implementing a loading skeleton to improve user experience.

- if (isLoading && meets.length === 0) {
-   return <div>loading...</div>
- }
+ if (isLoading && meets.length === 0) {
+   return (
+     <div className="flex size-full gap-16 bg-background p-10 pl-60">
+       <div className="animate-pulse space-y-4">
+         <div className="h-4 w-48 bg-gray-200 rounded"></div>
+         <div className="h-32 w-96 bg-gray-200 rounded"></div>
+       </div>
+     </div>
+   )
+ }
apps/frontend/src/components/meetings/InitialMeet.tsx (1)

Line range hint 21-115: Consider consolidating loading states

The component has multiple loading states (loading, isLoading, userLoading) which could be consolidated for better maintainability. Consider creating a custom hook to manage loading states.

Example approach:

const useLoadingState = () => {
  const [loading, setLoading] = useState(true)
  const { isLoading: userLoading } = useUserStore()
  
  const isLoading = loading || userLoading
  
  return { loading, setLoading, isLoading }
}
apps/frontend/src/components/Notes/NotesPage.tsx (3)

126-130: Consider adding type definitions for ActionHeader props

The ActionHeader component is used without visible prop types. Consider adding explicit type definitions for better type safety and documentation.

interface ActionHeaderProps {
  closeToggle: boolean
  loading: boolean
  onAdd: () => void
  onClose: () => void
}

Line range hint 21-164: Consider breaking down NotesPage component

The component has grown quite large and handles multiple responsibilities (note editing, state management, UI rendering). Consider breaking it down into smaller, more focused components.

Suggested structure:

  • NotePageContainer (main layout and state management)
  • NoteEditorContainer (editor-specific logic)
  • NoteHeaderSection (header and actions)
  • NoteSidePanel (stack modal logic)

Line range hint 126-164: Optimize performance with useMemo for handlers

Consider memoizing the event handlers passed to ActionHeader to prevent unnecessary re-renders.

+ const memoizedHandlers = useMemo(() => ({
+   onAdd: addNewNote,
+   onClose: handleClose
+ }), [addNewNote, handleClose]);

  <ActionHeader
    closeToggle={closeToggle}
    loading={loading}
-   onAdd={addNewNote}
-   onClose={handleClose}
+   {...memoizedHandlers}
  />
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between f118282 and ab27303.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • apps/frontend/src/app/(app)/spaces/[spaceId]/blocks/[blockId]/items/[itemId]/page.tsx (1 hunks)
  • apps/frontend/src/app/api/auth/google-calendar/url/route.ts (1 hunks)
  • apps/frontend/src/components/ActionHeader.tsx (1 hunks)
  • apps/frontend/src/components/Notes/NotesPage.tsx (2 hunks)
  • apps/frontend/src/components/Notes/components/NoteModal/NotesModal.tsx (1 hunks)
  • apps/frontend/src/components/editor/space-editor.tsx (1 hunks)
  • apps/frontend/src/components/header/action-header.tsx (2 hunks)
  • apps/frontend/src/components/header/meta-details.tsx (1 hunks)
  • apps/frontend/src/components/meetings/InitialMeet.tsx (1 hunks)
  • apps/frontend/src/components/meetings/MeetNotes.tsx (2 hunks)
  • apps/frontend/src/components/meetings/MeetingsPage.tsx (1 hunks)
  • apps/frontend/src/components/meetings/Stack.tsx (0 hunks)
  • apps/frontend/src/components/meetings/reducer.ts (1 hunks)
  • apps/frontend/src/components/meetings/useMeetEffects.ts (1 hunks)
  • apps/frontend/src/components/meetings/useMeetHandlers.ts (1 hunks)
  • apps/frontend/src/components/meetings/useMeetState.ts (1 hunks)
  • apps/frontend/src/components/modals/StackModal.tsx (1 hunks)
  • apps/frontend/src/utils/meet.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/frontend/src/components/meetings/Stack.tsx
✅ Files skipped from review due to trivial changes (1)
  • apps/frontend/src/components/Notes/components/NoteModal/NotesModal.tsx
🔇 Additional comments (8)
apps/frontend/src/components/meetings/MeetNotes.tsx (3)

5-7: Imports of new hooks are correctly added.

The new hooks useMeetEffects, useMeetHandlers, and useMeetState are imported properly, enhancing modularity and code organization.


13-13: Props are explicitly typed for MeetNotes component.

Defining the prop types enhances type safety and code clarity, preventing potential type-related bugs.


91-100: Props passed to SpaceEditor are appropriate.

The SpaceEditor component is correctly utilized with the necessary props, ensuring seamless integration.

apps/frontend/src/app/api/auth/google-calendar/url/route.ts (1)

10-10: ⚠️ Potential issue

Verify the exposure of GOOGLE_CLIENT_ID to the client.

Changing process.env.GOOGLE_CLIENT_ID to process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID exposes the client ID to the browser. Ensure that this is intentional and doesn't introduce security concerns.

Run the following script to check client-side usage of NEXT_PUBLIC_GOOGLE_CLIENT_ID:

✅ Verification successful

Using NEXT_PUBLIC_GOOGLE_CLIENT_ID is appropriate in this context

The usage of NEXT_PUBLIC_GOOGLE_CLIENT_ID is correct and secure in this context because:

  1. The client ID is used in two places:
    • In layout.tsx for Google OAuth client-side integration
    • In the server-side route url/route.ts to generate the OAuth authorization URL

Google Client IDs are meant to be public and are designed to be exposed in client-side code for OAuth flows. The critical secret that must be kept private is the client secret, which is correctly handled in the backend (apps/backend/src/loaders/google.loader.js) using the non-public GOOGLE_CLIENT_ID environment variable.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for usages of NEXT_PUBLIC_GOOGLE_CLIENT_ID in the client-side code.

rg 'NEXT_PUBLIC_GOOGLE_CLIENT_ID' --type js --type ts --type jsx --type tsx

Length of output: 103


Script:

#!/bin/bash
# Search for usages of NEXT_PUBLIC_GOOGLE_CLIENT_ID with ripgrep, without file type filters
rg "NEXT_PUBLIC_GOOGLE_CLIENT_ID"

# Also search for GOOGLE_CLIENT_ID to understand the context of its usage
rg "GOOGLE_CLIENT_ID"

# Let's also check the file content to understand the context better
cat "apps/frontend/src/app/api/auth/google-calendar/url/route.ts"

Length of output: 2437

apps/frontend/src/components/header/action-header.tsx (1)

6-18: Component props have been updated appropriately.

The ActionHeader component's props have been renamed and updated to improve clarity and reusability.

apps/frontend/src/components/ActionHeader.tsx (1)

26-26: Verify custom CSS classes

The hover-bg and hover-text classes are used but may need verification to ensure they're properly defined in your CSS system.

Also applies to: 38-40

✅ Verification successful

Let me check the actual CSS definitions to ensure these classes are properly defined.


CSS classes are properly defined and functional

The hover-bg and hover-text classes are correctly defined in apps/frontend/src/styles/main.css:

  • hover-bg applies Tailwind's hover:bg-background-hover and active:bg-background-active styles
  • hover-text applies Tailwind's hover:text-primary-foreground and active:text-foreground styles

Both classes are properly integrated with the theme system using CSS variables for colors.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for hover-bg and hover-text class definitions
rg -g '*.css' -g '*.scss' -g '*.module.*' 'hover-(bg|text)'

Length of output: 486


Script:

#!/bin/bash
# Get the content of the CSS file to see the actual definitions
cat apps/frontend/src/styles/main.css

Length of output: 5049

apps/frontend/src/app/(app)/spaces/[spaceId]/blocks/[blockId]/items/[itemId]/page.tsx (1)

35-35: LGTM! Verify prop usage in MeetingPage

The addition of spaceId and blockId props maintains consistency with the NotesPage implementation.

✅ Verification successful

Props are properly typed and used in MeetingPage component

The verification confirms that spaceId and blockId props are:

  • Correctly defined in the MeetingPageProps interface
  • Properly passed to the component
  • Utilized within the component for generating note links in simplifiedNotes
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Verify MeetingPage component properly handles these props
ast-grep --pattern 'interface $interface {
  $$$
  spaceId: string
  blockId: string
  $$$
}' src/components/meetings/MeetingsPage.tsx

Length of output: 224


Script:

#!/bin/bash
# First, let's find the correct path for MeetingPage component
fd -t f "Meeting.*\.(tsx|ts)"

# Then, let's search for prop interfaces or type definitions
ast-grep --pattern 'interface $interface {
  $$$
  meetId: string
  $$$
}'

# Also search for direct prop destructuring
ast-grep --pattern 'function $name({ $$$, spaceId, blockId, $$$}'

# Backup search with ripgrep
rg "MeetingPage.*=.*{.*spaceId.*blockId.*}" -A 5

Length of output: 512


Script:

#!/bin/bash
# Let's examine the MeetingsPage component content
cat apps/frontend/src/components/meetings/MeetingsPage.tsx

# Also search for MeetingPage usage with better regex
rg "MeetingPage.*props" -A 3

# And check prop types/interfaces with a more specific pattern
ast-grep --pattern 'type $name = {
  $$$
  spaceId: string
  $$$
}'

ast-grep --pattern 'export function MeetingsPage($props) {
  $$$
}'

Length of output: 3065

apps/frontend/src/components/editor/space-editor.tsx (1)

64-64: LGTM! Good use of memoization

Memoizing the component is a good practice to prevent unnecessary re-renders, especially for an editor component.

Comment on lines +26 to +35
useEffect(() => {
if (!meetData?._id || meetData._id === state.meet?._id) return

dispatch({ type: "SET_MEET", payload: meetData })
dispatch({ type: "SET_TITLE", payload: meetData.title || "" })
dispatch({
type: "SET_CONTENT",
payload: meetData.description || "<p></p>",
})
}, [meetData?._id, state.meet?._id]) // Only depend on IDs changing
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor redundant useEffect hooks for initializing state.

There are two useEffect hooks initializing state when meetData changes. Combining them improves readability and prevents potential inconsistencies.

Apply this diff to combine the useEffect hooks:

-useEffect(() => {
-  if (!meetData?._id || meetData._id === state.meet?._id) return
-
-  dispatch({ type: "SET_MEET", payload: meetData })
-  dispatch({ type: "SET_TITLE", payload: meetData.title || "" })
-  dispatch({
-    type: "SET_CONTENT",
-    payload: meetData.description || "<p></p>",
-  })
-}, [meetData?._id, state.meet?._id]) // Only depend on IDs changing
-
-// Initialize state once when meetData changes
-useEffect(() => {
-  if (!meetData || meet?.id === meetData.id) return
-
-  const newContent = meetData.description || "<p></p>"
-  dispatch({ type: "SET_MEET", payload: meetData })
-  dispatch({ type: "SET_TITLE", payload: meetData.title || "" })
-  dispatch({ type: "SET_CONTENT", payload: newContent })
-}, [meetData?.id]) // Only depend on the ID changing
+useEffect(() => {
+  if (!meetData || meetData._id === state.meet?._id) return
+
+  const newContent = meetData.description || "<p></p>"
+  dispatch({ type: "SET_MEET", payload: meetData })
+  dispatch({ type: "SET_TITLE", payload: meetData.title || "" })
+  dispatch({ type: "SET_CONTENT", payload: newContent })
+}, [meetData]) // Depend on the entire meetData object

Also applies to: 48-55

const MetaDetails: React.FC<NoteDetailsProps> = ({ url, duration }) => {
return (
<Details>
<a href={url} target="_blank" className="flex items-center gap-2">
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add rel="noopener noreferrer" to the anchor tag.

When using target="_blank", it's recommended to include rel="noopener noreferrer" to prevent security risks like reverse tabnabbing.

Apply this diff to fix the issue:

-      <a href={url} target="_blank" className="flex items-center gap-2">
+      <a href={url} target="_blank" rel="noopener noreferrer" className="flex items-center gap-2">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<a href={url} target="_blank" className="flex items-center gap-2">
<a href={url} target="_blank" rel="noopener noreferrer" className="flex items-center gap-2">

Comment on lines +21 to +32
<div>
<div
className="fixed inset-0 z-50 cursor-default"
role="button"
onClick={handleClose}
onKeyDown={(e) => {
if (e.key === "Escape" || e.key === "Esc") {
handleClose()
}
}}
tabIndex={0}
/>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add ARIA attributes for improved accessibility

The modal backdrop should have proper ARIA attributes for better screen reader support.

 <div>
   <div
     className="fixed inset-0 z-50 cursor-default"
     role="button"
+    aria-hidden="true"
     onClick={handleClose}
     onKeyDown={(e) => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div>
<div
className="fixed inset-0 z-50 cursor-default"
role="button"
onClick={handleClose}
onKeyDown={(e) => {
if (e.key === "Escape" || e.key === "Esc") {
handleClose()
}
}}
tabIndex={0}
/>
<div>
<div
className="fixed inset-0 z-50 cursor-default"
role="button"
aria-hidden="true"
onClick={handleClose}
onKeyDown={(e) => {
if (e.key === "Escape" || e.key === "Esc") {
handleClose()
}
}}
tabIndex={0}
/>

Comment on lines +36 to 56
export const calculateMeetDuration = (start: string, end: string): string => {
const startDate = new Date(start)
const endDate = new Date(end)

const minutes = Math.round(
(endDate.getTime() - startDate.getTime()) / (1000 * 60)
)

if (minutes < 60) {
return `${minutes}min`
}

const hours = Math.floor(minutes / 60)
const remainingMinutes = minutes % 60

if (remainingMinutes === 0) {
return `${hours}h`
}

return `${hours}h ${remainingMinutes}min`
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add input validation and handle edge cases

The calculateMeetDuration function needs input validation and edge case handling:

  1. Validate input strings
  2. Handle invalid dates
  3. Handle negative durations
  4. Handle same start/end times
 export const calculateMeetDuration = (start: string, end: string): string => {
+  if (!start || !end) {
+    throw new Error('Start and end times are required');
+  }
+
   const startDate = new Date(start)
   const endDate = new Date(end)
 
+  if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
+    throw new Error('Invalid date format');
+  }
+
   const minutes = Math.round(
     (endDate.getTime() - startDate.getTime()) / (1000 * 60)
   )
 
+  if (minutes < 0) {
+    throw new Error('End time must be after start time');
+  }
+
+  if (minutes === 0) {
+    return '0min';
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const calculateMeetDuration = (start: string, end: string): string => {
const startDate = new Date(start)
const endDate = new Date(end)
const minutes = Math.round(
(endDate.getTime() - startDate.getTime()) / (1000 * 60)
)
if (minutes < 60) {
return `${minutes}min`
}
const hours = Math.floor(minutes / 60)
const remainingMinutes = minutes % 60
if (remainingMinutes === 0) {
return `${hours}h`
}
return `${hours}h ${remainingMinutes}min`
}
export const calculateMeetDuration = (start: string, end: string): string => {
if (!start || !end) {
throw new Error('Start and end times are required');
}
const startDate = new Date(start)
const endDate = new Date(end)
if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
throw new Error('Invalid date format');
}
const minutes = Math.round(
(endDate.getTime() - startDate.getTime()) / (1000 * 60)
)
if (minutes < 0) {
throw new Error('End time must be after start time');
}
if (minutes === 0) {
return '0min';
}
if (minutes < 60) {
return `${minutes}min`
}
const hours = Math.floor(minutes / 60)
const remainingMinutes = minutes % 60
if (remainingMinutes === 0) {
return `${hours}h`
}
return `${hours}h ${remainingMinutes}min`
}

textareaRef,
}: SpaceEditorProps) => {
return (
<div onBlur={handleSaveNote}>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Reconsider save trigger implementation

The onBlur event on the outer div could trigger unexpected saves when clicking between inner elements. Consider moving the save trigger to specific user actions or using a debounced auto-save approach.

Comment on lines +43 to +44
/* eslint-disable-next-line jsx-a11y/no-autofocus */
autoFocus={!title || title.trim() === ""}
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Consider removing autoFocus or adding skip navigation

The codebase shows consistent use of autoFocus in input components, primarily in "add item" or editor scenarios. While this pattern improves usability for keyboard users, it can be disorienting for screen reader users. Consider either:

  • Removing autoFocus and letting users manually focus using standard navigation
  • Adding a skip navigation link that allows screen reader users to bypass these auto-focused inputs
  • Limiting autoFocus to only the most critical scenarios (e.g., main editor) rather than all input fields

Affected files:

  • apps/frontend/src/components/Inbox/InboxAddItem.tsx
  • apps/frontend/src/components/Notes/components/NoteEditor/NoteEditor.tsx
  • apps/frontend/src/components/Reading/AddItemForm.tsx
  • apps/frontend/src/components/atoms/ItemAdd.tsx
🔗 Analysis chain

Review accessibility implications of autoFocus

While the eslint rule is disabled, consider if autoFocus is really necessary here. It can be disorienting for screen reader users and should be used sparingly.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for other autoFocus usage in the codebase
rg -i "autoFocus.*=.*true" --type=tsx

Length of output: 67


Script:

#!/bin/bash
# Check for autoFocus usage in the codebase with proper file extension
rg -i "autoFocus.*=.*" --type=typescript --type=tsx -A 2

Length of output: 93


Script:

#!/bin/bash
# Find all TypeScript/TSX files that might contain autoFocus
fd -e tsx -e ts | xargs grep -l "autoFocus"

Length of output: 320


Script:

#!/bin/bash
# Use ast-grep to find JSX attributes with autoFocus
ast-grep --pattern 'autoFocus={$_}'

Length of output: 37


Script:

#!/bin/bash
# Check the content of files that use autoFocus to understand the context
for file in apps/frontend/src/components/Inbox/InboxAddItem.tsx \
           apps/frontend/src/components/Notes/components/NoteEditor/NoteEditor.tsx \
           apps/frontend/src/components/Reading/AddItemForm.tsx \
           apps/frontend/src/components/atoms/ItemAdd.tsx; do
    echo "=== $file ==="
    rg -A 3 -B 3 "autoFocus" "$file"
done

Length of output: 3926


Script:

#!/bin/bash
# Check if there are any accessibility-related comments or patterns near autoFocus
rg -i "a11y|accessibility|screen.?reader" --type=ts --type=tsx -A 2 -B 2

Length of output: 102

Comment on lines +7 to +11
export const useMeetHandlers = (state, dispatch) => {
const { session } = useAuth()
const { updateMeet } = useMeetsStore()
const { meet, title, content, isSaved } = state

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add TypeScript types for state and dispatch parameters

The hook parameters lack TypeScript types, which could lead to runtime errors. Consider adding proper types for better type safety.

- export const useMeetHandlers = (state, dispatch) => {
+ interface MeetState {
+   meet: Meet | null
+   title: string
+   content: string
+   isSaved: boolean
+ }
+ type MeetDispatch = React.Dispatch<{ type: string; payload: any }>
+ export const useMeetHandlers = (state: MeetState, dispatch: MeetDispatch) => {

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +37 to +52
const initialize = async () => {
if (!session || !meetId) return

setLoading(true)
try {
await Promise.all([
!currentMeeting || currentMeeting.id !== meetId
? fetchMeetByid(session, meetId)
: Promise.resolve(),
meets.length === 0 ? fetchMeets(session) : Promise.resolve(),
])
// Fetch meets list if needed
if (meets.length === 0) {
await fetchMeets(session)
}
// Fetch specific meet
await fetchMeetByid(session, meetId)
} finally {
setInitialLoading(false)
if (mounted) {
setLoading(false)
setIsInitialLoad(false)
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling and user feedback

The initialization process should handle errors gracefully and provide feedback to users when operations fail.

 const initialize = async () => {
   if (!session || !meetId) return
   
   setLoading(true)
   try {
     if (meets.length === 0) {
       await fetchMeets(session)
     }
     await fetchMeetByid(session, meetId)
   } catch (error) {
+    console.error('Failed to initialize meeting:', error)
+    // Consider adding a toast notification or error state
+    if (mounted) {
+      setError(error.message)
+    }
   } finally {
     if (mounted) {
       setLoading(false)
       setIsInitialLoad(false)
     }
   }
 }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +111 to +115
return (
<p className="size-full overflow-auto bg-background px-8 text-secondary-foreground">
loading...
</p>
)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add accessibility attributes to loading states

The loading state should include proper accessibility attributes to improve screen reader support.

-  return (
-    <p className="size-full overflow-auto bg-background px-8 text-secondary-foreground">
+  return (
+    <p 
+      className="size-full overflow-auto bg-background px-8 text-secondary-foreground"
+      role="status"
+      aria-live="polite"
+    >
       loading...
     </p>
   )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (
<p className="size-full overflow-auto bg-background px-8 text-secondary-foreground">
loading...
</p>
)
return (
<p
className="size-full overflow-auto bg-background px-8 text-secondary-foreground"
role="status"
aria-live="polite"
>
loading...
</p>
)

@sajdakabir sajdakabir merged commit 0a71553 into preview Dec 9, 2024
4 of 5 checks passed
@sajdakabir sajdakabir deleted the refac/meetings-page branch December 9, 2024 04:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants