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

[WEB-3096] feat: stickies page #6380

Merged
merged 22 commits into from
Jan 16, 2025
Merged

[WEB-3096] feat: stickies page #6380

merged 22 commits into from
Jan 16, 2025

Conversation

gakshita
Copy link
Collaborator

@gakshita gakshita commented Jan 10, 2025

Description

This PR includes the following changes-

Features

  1. New webpage for stickies.

Improvements

  1. New empty states for stickies.
  2. Improved background colors with different colors for dark mode.

Refactor

  1. Updated stickies store types and function names.
  2. Updated sticky types to match the API response.

Type of Change

  • Feature (non-breaking change which adds functionality)
  • Improvement (change that would cause existing functionality to not work as expected)
  • Code refactoring

Summary by CodeRabbit

Release Notes

  • New Features

    • Added stickies functionality with infinite scrolling and drag-and-drop support.
    • Introduced workspace-specific sticky notes management.
    • Enhanced empty states for stickies and home widgets.
    • New components for displaying stickies in various layouts.
    • Added customizable button options in the content overflow component.
  • Improvements

    • Refined sticky note search and filtering capabilities, including debounced search.
    • Updated pagination and loading mechanisms for stickies.
    • Improved user interface for sticky note creation and management.
    • Enhanced styling for checkbox elements in task lists.
    • Introduced new color palette for sticky notes.
  • Bug Fixes

    • Resolved issues with sticky note sorting and positioning.
    • Enhanced error handling for sticky operations.
  • Chores

    • Refactored components and services related to sticky notes.
    • Updated type definitions and constants.
    • Cleaned up unused variables and imports in various components.

Copy link
Contributor

coderabbitai bot commented Jan 10, 2025

Walkthrough

This pull request introduces significant enhancements to the sticky notes functionality within the Plane application. Key changes include modifications to the WorkspaceStickyViewSet class for filtering and sorting sticky notes, improvements to the Sticky model for handling HTML content, and the addition of new React components for better UI interaction. The changes also encompass updates to constants, types, and services, streamlining the management and display of sticky notes across the application.

Changes

File Change Summary
apiserver/plane/app/views/workspace/sticky.py Updated list method to filter stickies by description_stripped and order by sort_order
apiserver/plane/db/models/sticky.py Added logic to strip HTML tags from description_html and set description_stripped
packages/constants/src/index.ts, packages/constants/src/stickies.ts Added new export and constant STICKIES_PER_PAGE with value 30
packages/types/src/stickies.d.ts Added sort_order: number to TSticky type
web/core/services/sticky.service.ts Modified getStickies method to use STICKIES_PER_PAGE and updated parameter handling
web/core/store/sticky/sticky.store.ts Refactored sticky store with new pagination and loading management
web/app/[workspaceSlug]/(projects)/stickies/header.tsx, web/app/[workspaceSlug]/(projects)/stickies/layout.tsx, web/app/[workspaceSlug]/(projects)/stickies/page.tsx Introduced new React components: WorkspaceStickyHeader, WorkspaceStickiesLayout, and WorkspaceStickiesPage for improved UI structure
web/core/components/stickies/layout/stickies-infinite.tsx, web/core/components/stickies/layout/stickies-list.tsx, web/core/components/stickies/layout/stickies-truncated.tsx Added components for managing sticky notes display and pagination
web/core/components/stickies/modal/search.tsx, web/core/components/stickies/modal/stickies.tsx Enhanced search functionality and modified sticky creation logic
web/core/components/stickies/sticky/use-operations.tsx Updated sticky operations to include new methods and improved error handling

Possibly related PRs

  • [WEB-3048]feat: API endpoints for stickies #6335: The changes in this PR introduce new API endpoints for managing stickies, which directly relates to the modifications made in the WorkspaceStickyViewSet class in the main PR that also deals with sticky notes.
  • [WEB-3088] fix: home edits #6357: This PR enhances the list method of the WorkspaceStickyViewSet class by adding filtering capabilities based on a query parameter, which aligns with the changes in the main PR that also modifies the list method's logic for retrieving sticky notes.
  • [WEB-3078] chore: empty state config #6397: This PR adds new entries to the EmptyStateType enum, which could be relevant to how empty states for stickies are handled in the application, linking it to the changes in the main PR regarding sticky management.

Suggested labels

🧹chore, ⚙️backend

Suggested reviewers

  • rahulramesha
  • SatishGandham
  • sriramveeraghanta

Poem

🐰 Sticky notes dance and sway,
With drag and drop, they find their way
Infinite scroll, a rabbit's delight
Sorting magic, making data bright!
Code hops forward, stickies take flight 🚀


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 generate docstrings to generate docstrings for this PR. (Beta)
  • @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
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 15

🧹 Nitpick comments (26)
web/core/components/stickies/sticky/sticky-item-drag-handle.tsx (1)

18-20: Consider accessibility improvements for drag handle.

The drag handle's visibility relies solely on hover state (group-hover). This might cause accessibility issues for keyboard and touch device users.

Consider adding keyboard focus visibility:

-"hidden group-hover/sticky:flex absolute top-3 left-1/2 -translate-x-1/2 items-center justify-center rounded text-custom-sidebar-text-400 cursor-grab mr-2 rotate-90"
+"hidden group-hover/sticky:flex focus-within:flex absolute top-3 left-1/2 -translate-x-1/2 items-center justify-center rounded text-custom-sidebar-text-400 cursor-grab mr-2 rotate-90"
web/core/components/stickies/sticky/inputs.tsx (2)

76-76: Consider using CSS custom properties for dimensions.

Hardcoding dimensions in class names makes it harder to maintain consistent spacing across components.

Consider using CSS variables:

-containerClassName={"px-0 text-base min-h-[250px] w-full text-[#455068]"}
+containerClassName={"px-0 text-base min-h-[var(--sticky-min-height)] w-full text-[var(--text-color-secondary)]"}

78-78: Consider adding a default value for showToolbar prop.

The showToolbar prop is passed through without a default value, which could lead to undefined behavior.

Add a default value in the destructuring:

-showToolbar={showToolbar}
+showToolbar={showToolbar ?? true}
web/core/components/stickies/sticky/root.tsx (2)

24-25: Consider memoizing pathname check.

The pathname check could cause unnecessary re-renders when the path changes but doesn't affect the sticky functionality.

Consider using useMemo:

-const pathName = usePathname();
+const showDragHandle = useMemo(() => {
+  const pathName = usePathname();
+  return pathName?.includes("stickies") ?? false;
+}, [usePathname()]);

69-69: Remove unnecessary string literal.

The empty string literal after the conditional rendering is unnecessary.

-{pathName?.includes("stickies") && <StickyItemDragHandle isDragging={false} />}{" "}
+{pathName?.includes("stickies") && <StickyItemDragHandle isDragging={false} />}
web/core/components/editor/sticky-editor/editor.tsx (1)

85-106: Consider extracting toolbar visibility logic.

The toolbar visibility logic combines multiple concerns: conditional rendering, animation, and state management.

Consider extracting this into a separate component:

interface ToolbarWrapperProps {
  show: boolean;
  isFocused: boolean;
  editorRef: EditorRefApi | null;
  handleDelete: () => void;
  handleColorChange: (data: Partial<TSticky>) => Promise<void>;
}

const ToolbarWrapper: React.FC<ToolbarWrapperProps> = ({
  show,
  isFocused,
  editorRef,
  handleDelete,
  handleColorChange,
}) => {
  if (!show) return null;
  
  return (
    <div
      className={cn(
        "transition-all duration-300 ease-out origin-top",
        isFocused ? "max-h-[200px] opacity-100 scale-y-100 mt-3" : "max-h-0 opacity-0 scale-y-0 invisible"
      )}
    >
      <Toolbar
        executeCommand={(item) => {
          editorRef?.executeMenuItemCommand({
            itemKey: item.itemKey,
            ...item.extraProps,
          });
        }}
        handleDelete={handleDelete}
        handleColorChange={handleColorChange}
        editorRef={editorRef}
      />
    </div>
  );
};
web/core/components/stickies/layout/stickies-truncated.tsx (1)

17-21: Consider adding error handling for SWR data fetching

The SWR configuration disables revalidation, but there's no error handling or loading state management visible.

Consider adding error and loading states:

 useSWR(
   workspaceSlug ? `WORKSPACE_STICKIES_${workspaceSlug}` : null,
   workspaceSlug ? () => fetchWorkspaceStickies(workspaceSlug.toString()) : null,
-  { revalidateIfStale: false, revalidateOnFocus: false }
+  {
+    revalidateIfStale: false,
+    revalidateOnFocus: false,
+    onError: (error) => {
+      // Handle error appropriately
+      console.error("Failed to fetch stickies:", error);
+    }
+  }
web/core/components/stickies/widget.tsx (1)

41-41: Consider adding loading state for StickiesTruncated

The StickiesTruncated component might benefit from a loading state while data is being fetched.

-        <StickiesTruncated />
+        {/* Add a loading state wrapper */}
+        <Suspense fallback={<LoadingSpinner />}>
+          <StickiesTruncated />
+        </Suspense>
web/core/components/stickies/modal/stickies.tsx (1)

63-63: Add aria-label for better accessibility

The scrollable content area should have an appropriate aria-label.

-      <div className="mb-4 max-h-[625px] overflow-scroll">
+      <div 
+        className="mb-4 max-h-[625px] overflow-scroll"
+        aria-label="Stickies content"
+      >
web/app/[workspaceSlug]/(projects)/stickies/layout.tsx (1)

6-13: LGTM! Consider using type alias for props

The layout implementation is clean and follows best practices. Consider extracting the props type to improve reusability:

type WorkspaceStickiesLayoutProps = {
  children: React.ReactNode;
};

export default function WorkspaceStickiesLayout({ children }: WorkspaceStickiesLayoutProps) {
  // ... rest of the implementation
}
web/app/[workspaceSlug]/(projects)/stickies/page.tsx (1)

14-14: Improve type safety for workspaceSlug

Instead of type casting, consider using type guard for better type safety:

const workspaceSlug = typeof routeWorkspaceSlug === 'string' ? routeWorkspaceSlug : undefined;
apiserver/plane/db/models/sticky.py (1)

39-44: Consider handling potential HTML processing errors.

While the HTML stripping logic is correct, it should handle potential exceptions from the strip_tags function to prevent save failures.

 def save(self, *args, **kwargs):
     # Strip the html tags using html parser
-    self.description_stripped = (
-        None
-        if (self.description_html == "" or self.description_html is None)
-        else strip_tags(self.description_html)
-    )
+    try:
+        self.description_stripped = (
+            None
+            if (self.description_html == "" or self.description_html is None)
+            else strip_tags(self.description_html)
+        )
+    except Exception as e:
+        # Log the error but don't block the save
+        self.description_stripped = None
web/core/components/stickies/layout/sticky.helpers.ts (1)

18-45: Consider adding error boundaries for edge cases.

The function handles multiple scenarios well, but could benefit from additional error handling for malformed data.

 export const getInstructionFromPayload = (
   dropTarget: TDropTarget,
   source: TDropTarget,
   location: IPragmaticPayloadLocation
 ): InstructionType | undefined => {
+  // Early validation of required parameters
+  if (!dropTarget || !source || !location) {
+    console.warn('Invalid parameters provided to getInstructionFromPayload');
+    return undefined;
+  }

   const dropTargetData = dropTarget?.data as TargetData;
   const sourceData = source?.data as TargetData;
   const allDropTargets = location?.current?.dropTargets;
apiserver/plane/app/views/workspace/sticky.py (1)

42-44: Consider adding index for performance optimization.

The ordering and filtering on sort_order and description_stripped fields would benefit from database indexes for better query performance.

Consider adding the following indexes to your model:

class Meta:
    indexes = [
        models.Index(fields=['-sort_order']),
        models.Index(fields=['description_stripped']),
    ]
web/core/components/stickies/layout/stickies-infinite.tsx (2)

29-32: Add debouncing to prevent rapid load more calls.

The handleLoadMore function should be debounced to prevent multiple rapid calls when scrolling.

+import { debounce } from 'lodash';

-const handleLoadMore = () => {
+const handleLoadMore = debounce(() => {
   if (loader === "pagination") return;
   fetchNextWorkspaceStickies(workspaceSlug?.toString());
-};
+}, 300);

46-57: Consider adding aria-label for accessibility.

The intersection element should include appropriate ARIA attributes for screen readers.

 <div
   className={cn("flex min-h-[300px] box-border p-2 w-full")}
   ref={setElementRef}
   id="intersection-element"
+  role="status"
+  aria-label="Loading more stickies"
 >
web/core/components/stickies/modal/search.tsx (1)

41-46: Consider memoizing dependencies for debounced search.

The debouncedSearch callback depends on fetchStickies, but fetchStickies itself depends on workspaceSlug and fetchWorkspaceStickies. Consider including these dependencies in the dependency array to prevent stale closures.

  const debouncedSearch = useCallback(
    debounce(async () => {
      await fetchStickies();
    }, 500),
-    [fetchWorkspaceStickies]
+    [fetchWorkspaceStickies, workspaceSlug]
  );
web/core/components/stickies/sticky/use-operations.tsx (1)

23-26: Cache random color generation function.

The getRandomStickyColor function should be memoized to prevent unnecessary recalculations.

+ const memoizedGetRandomStickyColor = useMemo(() => {
    const getRandomStickyColor = (): string => {
      const randomIndex = Math.floor(Math.random() * STICKY_COLORS.length);
      return STICKY_COLORS[randomIndex];
    };
+   return getRandomStickyColor;
+ }, []);
web/core/components/core/content-overflow-HOC.tsx (1)

12-12: Add type constraints for customButton prop.

The customButton prop should have stricter type constraints to ensure proper rendering.

- customButton?: ReactNode;
+ customButton?: JSX.Element;
web/core/components/stickies/layout/stickies-list.tsx (1)

114-122: Extract breakpoint values as named constants

The magic numbers in the column count logic could be more maintainable as named constants.

Consider this refactoring:

+  const BREAKPOINTS = {
+    SM: 640,
+    MD: 768,
+    LG: 1024,
+    XL: 1280,
+  } as const;
+
+  const COLUMN_COUNTS = {
+    DEFAULT: 4,
+    SM: 2,
+    MD: 3,
+    LG: 4,
+    XL: 5,
+    XXL: 6,
+  } as const;
+
   const getColumnCount = (width: number | null): number => {
-    if (width === null) return 4;
+    if (width === null) return COLUMN_COUNTS.DEFAULT;
 
-    if (width < 640) return 2; // sm
-    if (width < 768) return 3; // md
-    if (width < 1024) return 4; // lg
-    if (width < 1280) return 5; // xl
-    return 6; // 2xl and above
+    if (width < BREAKPOINTS.SM) return COLUMN_COUNTS.SM;
+    if (width < BREAKPOINTS.MD) return COLUMN_COUNTS.MD;
+    if (width < BREAKPOINTS.LG) return COLUMN_COUNTS.LG;
+    if (width < BREAKPOINTS.XL) return COLUMN_COUNTS.XL;
+    return COLUMN_COUNTS.XXL;
   };
web/core/components/stickies/empty.tsx (2)

12-46: Enhance accessibility for empty state

The empty state could benefit from improved accessibility.

Consider these improvements:

-    <div className="flex justify-center min-h-[500px] rounded border-[1.5px] border-custom-border-100 mx-2 h-full">
+    <div 
+      className="flex justify-center min-h-[500px] rounded border-[1.5px] border-custom-border-100 mx-2 h-full"
+      role="region"
+      aria-label={query ? "No search results" : "Empty stickies state"}
+    >
       <div className="m-auto">
         <div
           className={`mb-2 rounded-full mx-auto last:rounded-full w-[50px] h-[50px] flex items-center justify-center bg-custom-background-80/40 transition-transform duration-300`}
+          aria-hidden="true"
         >
           <StickyIcon className="size-[30px] rotate-90 text-custom-text-350/20" />
         </div>

36-44: Improve loading state accessibility

The loading spinner needs proper ARIA attributes.

Consider this enhancement:

             {creatingSticky && (
-              <div className="flex items-center justify-center ml-2">
+              <div 
+                className="flex items-center justify-center ml-2"
+                role="status"
+                aria-live="polite"
+              >
                 <div
                   className={`w-4 h-4 border-2 border-t-transparent rounded-full animate-spin border-custom-primary-100`}
-                  role="status"
-                  aria-label="loading"
+                  aria-hidden="true"
                 />
+                <span className="sr-only">Creating sticky note...</span>
               </div>
             )}
web/core/components/stickies/layout/sticky-dnd-wrapper.tsx (3)

113-113: Remove isDragging from useEffect dependency array

Including isDragging in the dependency array causes the useEffect hook to re-run whenever isDragging changes, leading to unnecessary re-executions and potential issues with event registrations. Consider removing isDragging from the dependency array.

Apply this diff to adjust the dependency array:

- }, [stickyId, isDragging]);
+ }, [stickyId]);

69-69: Eliminate unnecessary .toString() on workspaceSlug

The workspaceSlug is already a string as per its type definition, so calling .toString() is unnecessary.

Apply this diff to simplify the code:

- workspaceSlug={workspaceSlug.toString()}
+ workspaceSlug={workspaceSlug}

124-124: Simplify the key prop usage

Since stickyId is always a string (as it's a required prop), the fallback "new" is unnecessary. You can directly use stickyId as the key.

Apply this diff:

- <StickyNote key={stickyId || "new"} workspaceSlug={workspaceSlug} stickyId={stickyId} />
+ <StickyNote key={stickyId} workspaceSlug={workspaceSlug} stickyId={stickyId} />
web/core/store/sticky/sticky.store.ts (1)

139-143: Improve loader state management

Setting this.loader inside conditional blocks may lead to inconsistent loader states if multiple requests are initiated simultaneously. This can cause UI flickering or incorrect loading indicators.

Consider refactoring the loader state management to handle multiple concurrent requests more gracefully, possibly by implementing a request counter or using separate loader states for different requests.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 95175ab and 1fe548a.

📒 Files selected for processing (30)
  • apiserver/plane/app/views/workspace/sticky.py (1 hunks)
  • apiserver/plane/db/models/sticky.py (2 hunks)
  • packages/constants/src/index.ts (1 hunks)
  • packages/constants/src/stickies.ts (1 hunks)
  • packages/types/src/stickies.d.ts (1 hunks)
  • web/app/[workspaceSlug]/(projects)/stickies/header.tsx (1 hunks)
  • web/app/[workspaceSlug]/(projects)/stickies/layout.tsx (1 hunks)
  • web/app/[workspaceSlug]/(projects)/stickies/page.tsx (1 hunks)
  • web/core/components/core/content-overflow-HOC.tsx (3 hunks)
  • web/core/components/editor/sticky-editor/editor.tsx (1 hunks)
  • web/core/components/home/widgets/links/link-detail.tsx (3 hunks)
  • web/core/components/home/widgets/links/use-links.tsx (0 hunks)
  • web/core/components/stickies/empty.tsx (1 hunks)
  • web/core/components/stickies/index.ts (1 hunks)
  • web/core/components/stickies/layout/index.ts (1 hunks)
  • web/core/components/stickies/layout/stickies-infinite.tsx (1 hunks)
  • web/core/components/stickies/layout/stickies-list.tsx (1 hunks)
  • web/core/components/stickies/layout/stickies-truncated.tsx (1 hunks)
  • web/core/components/stickies/layout/sticky-dnd-wrapper.tsx (1 hunks)
  • web/core/components/stickies/layout/sticky.helpers.ts (1 hunks)
  • web/core/components/stickies/modal/search.tsx (3 hunks)
  • web/core/components/stickies/modal/stickies.tsx (4 hunks)
  • web/core/components/stickies/stickies-layout.tsx (0 hunks)
  • web/core/components/stickies/sticky/inputs.tsx (2 hunks)
  • web/core/components/stickies/sticky/root.tsx (3 hunks)
  • web/core/components/stickies/sticky/sticky-item-drag-handle.tsx (1 hunks)
  • web/core/components/stickies/sticky/use-operations.tsx (3 hunks)
  • web/core/components/stickies/widget.tsx (3 hunks)
  • web/core/services/sticky.service.ts (2 hunks)
  • web/core/store/sticky/sticky.store.ts (4 hunks)
💤 Files with no reviewable changes (2)
  • web/core/components/home/widgets/links/use-links.tsx
  • web/core/components/stickies/stickies-layout.tsx
✅ Files skipped from review due to trivial changes (3)
  • packages/constants/src/stickies.ts
  • web/core/components/stickies/index.ts
  • web/core/components/stickies/layout/index.ts
🧰 Additional context used
🪛 Biome (1.9.4)
web/core/components/stickies/layout/stickies-truncated.tsx

[error] 27-27: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.

(lint/complexity/noUselessFragments)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: lint-apiserver
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (9)
web/core/components/editor/sticky-editor/editor.tsx (1)

94-96: Address TODO comment and type assertion.

The TODO comment and type assertion indicate potential technical debt that should be addressed.

Could you provide more details about the toolbar homogenization plan? This would help in properly typing the executeCommand function.

packages/constants/src/index.ts (1)

13-13: LGTM! Export follows consistent pattern

The addition of the stickies export follows the established pattern in the file.

web/app/[workspaceSlug]/(projects)/stickies/header.tsx (1)

17-20: Add error handling for workspaceSlug

The workspaceSlug from useParams should be validated before use in stickyOperations.

const workspaceSlug = params.workspaceSlug?.toString();
if (!workspaceSlug) {
  console.error("Workspace slug is required");
  return null;
}
apiserver/plane/db/models/sticky.py (1)

8-10: LGTM! Clean import organization.

The addition of strip_tags from the html processor module is well-placed in the third-party imports section.

web/core/components/stickies/layout/sticky.helpers.ts (1)

4-9: LGTM! Well-structured type definition.

The TargetData type clearly defines the shape of drag-and-drop target data with appropriate types for each property.

web/core/components/stickies/layout/stickies-list.tsx (2)

77-78: 🛠️ Refactor suggestion

Fix type mismatch with Masonry component

The @ts-expect-error comment suggests an unresolved type issue.

Let's check the type definitions:

Consider:

  1. Updating to a newer version of react-masonry-component with better TypeScript support
  2. Creating proper type definitions if they don't exist
  3. Using an alternative library with better TypeScript support
✅ Verification successful

Type mismatch with Masonry is expected and properly handled

The @ts-expect-error comment is an appropriate solution here since react-masonry-component doesn't provide TypeScript definitions. While better typing would be ideal, the current approach is a valid workaround for this third-party dependency.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for Masonry type definitions
rg -t ts "export.*Masonry" node_modules/react-masonry-component

Length of output: 142


Script:

#!/bin/bash
# Find Masonry imports
rg -g '*.{ts,tsx}' "import.*Masonry"

# Check package.json for masonry-related dependencies
cat package.json | grep -i masonry

# Look for type definitions
fd -e d.ts -e ts -e tsx | xargs rg "Masonry.*interface|interface.*Masonry|type.*Masonry"

Length of output: 385


3-5: Consider using public types instead of internal types from @atlaskit

The use of internal types (DropTargetRecord, DragLocationHistory) from @atlaskit/pragmatic-drag-and-drop/dist/types/internal-types could be risky as they might change without notice in future updates.

Let's check if public alternatives exist:

web/core/store/sticky/sticky.store.ts (1)

225-245: Ensure resultSequence remains within valid range

The calculation of resultSequence may lead to negative values when moving a sticky below another with a lower sort_order. Negative sort_order values might cause unexpected behavior in sorting or database constraints.

Please verify that resultSequence is always a positive number and adjust the calculation to prevent negative values. Consider using a minimum sequence value or adjusting the logic to handle edge cases appropriately.

packages/types/src/stickies.d.ts (1)

8-8: Confirm that sort_order is always defined

The sort_order property is added as a required field in the TSticky type. If existing sticky notes or API responses might lack this field (e.g., due to older data), it could lead to runtime errors.

Ensure that all sticky notes will have a defined sort_order. If not, consider making it optional:

- sort_order: number;
+ sort_order?: number;

This adjustment will prevent potential undefined errors when accessing sort_order.

✅ Verification successful

sort_order should remain a required field

The field is used for core sorting functionality and appears to be managed by the backend. The API accepts partial updates and likely handles default values, making it safe to keep as required in the type definition.

🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Search for files containing TSticky type usage
echo "=== Files containing TSticky type ==="
rg "TSticky" -l

echo -e "\n=== TSticky type definition and context ==="
rg "TSticky" -B 3 -A 10

echo -e "\n=== Files containing sort_order in relation to sticky notes ==="
rg "sort_order.*sticky" -i

echo -e "\n=== Database related files ==="
fd "schema|migration" --type f

Length of output: 30802

web/core/components/stickies/layout/stickies-truncated.tsx Outdated Show resolved Hide resolved
web/core/components/stickies/layout/stickies-truncated.tsx Outdated Show resolved Hide resolved
web/core/components/stickies/widget.tsx Show resolved Hide resolved
web/app/[workspaceSlug]/(projects)/stickies/page.tsx Outdated Show resolved Hide resolved
web/core/services/sticky.service.ts Show resolved Hide resolved
web/core/components/stickies/layout/sticky-dnd-wrapper.tsx Outdated Show resolved Hide resolved
web/core/store/sticky/sticky.store.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
web/core/components/stickies/layout/stickies-list.tsx (1)

44-61: ⚠️ Potential issue

Simplify handleDrop logic and improve error handling

The nested conditionals make the code harder to follow, and the error handling could be more robust.

The previous review comment's suggestion to refactor this function is still valid and should be implemented.

web/core/components/stickies/layout/sticky-dnd-wrapper.tsx (1)

45-117: ⚠️ Potential issue

Ensure proper cleanup in useEffect

The previous review comment's suggestion about adding proper cleanup is still valid and should be implemented.

Add error handling in drag preview rendering

The drag preview rendering lacks error handling which could cause issues if the rendering fails.

  render: ({ container }) => {
+   try {
      const root = createRoot(container);
      root.render(
        // ... existing code
      );
      return () => root.unmount();
+   } catch (error) {
+     console.error("Failed to render drag preview:", error);
+     return () => {};
+   }
  },
🧹 Nitpick comments (3)
web/core/components/stickies/layout/stickies-list.tsx (1)

73-84: Add loading state during sticky creation

The empty state handles the creation action but doesn't show a loading indicator while the sticky is being created. This could lead to a poor user experience if the creation takes time.

Consider adding a loading state:

  return (
    <EmptyState
      query={searchQuery}
      creatingSticky={creatingSticky}
      handleCreate={() => {
+       if (creatingSticky) return;
        toggleShowNewSticky(true);
        stickyOperations.create();
      }}
+     isLoading={creatingSticky}
    />
  );
web/core/components/stickies/layout/sticky-dnd-wrapper.tsx (2)

21-44: Add prop validation for required fields

While the props are well-typed, runtime validation for required props would improve reliability.

Consider adding validation:

+  if (!stickyId || !workspaceSlug) {
+    console.error("stickyId and workspaceSlug are required");
+    return null;
+  }

86-116: Improve type safety for instruction system

The instruction system could benefit from stronger type safety to prevent potential runtime errors.

Consider adding type guards:

+ const isValidInstruction = (instruction: unknown): instruction is InstructionType => {
+   return instruction === "make-child" || instruction === "reorder-above" || instruction === "reorder-below";
+ };

  onDrag: ({ self, source, location }) => {
    const instruction = getInstructionFromPayload(self, source, location);
+   if (!isValidInstruction(instruction)) return;
    setInstruction(instruction);
  },
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1fe548a and 45c1099.

📒 Files selected for processing (2)
  • web/core/components/stickies/layout/stickies-list.tsx (1 hunks)
  • web/core/components/stickies/layout/sticky-dnd-wrapper.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: lint-apiserver
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (2)
web/core/components/stickies/layout/stickies-list.tsx (2)

1-24: LGTM! Well-structured type definitions and imports.

The type definitions are clear and the dependencies are appropriate for implementing a masonry layout with drag-and-drop functionality.


111-146: LGTM! Well-implemented responsive layout.

The StickiesLayout component effectively handles:

  • Responsive column count based on container width
  • Proper cleanup of ResizeObserver
  • Clear breakpoint definitions

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
web/core/components/home/widgets/empty-states/links.tsx (1)

1-13: Consider implementing a compound component pattern

Given that this component is part of a larger feature change, consider implementing a compound component pattern to maintain flexibility. This would allow different parts of the application to either use this static version or wrap it with interactive features as needed.

Example structure:

const Links = {
  EmptyState: LinksEmptyState,
  // Other related components can be added here
  // Interactive: LinksInteractive,
  // Container: LinksContainer,
};

export default Links;
web/core/components/home/widgets/empty-states/recents.tsx (2)

4-4: Consider using literal type for better type safety

Instead of using a generic string type, consider using a union type of specific values to ensure type safety.

-export const RecentsEmptyState = ({ type }: { type: string }) => {
+type RecentsType = 'project' | 'page' | 'issue' | 'all item';
+export const RecentsEmptyState = ({ type }: { type: RecentsType }) => {

5-28: Extract constants for better maintainability

Consider extracting the display content configurations into constants to improve maintainability and reusability.

+const DISPLAY_CONTENT_CONFIG = {
+  project: {
+    icon: <Briefcase size={30} className="text-custom-text-400/40" />,
+    text: "Your recent projects will appear here once you visit one.",
+  },
+  page: {
+    icon: <FileText size={30} className="text-custom-text-400/40" />,
+    text: "Your recent pages will appear here once you visit one.",
+  },
+  issue: {
+    icon: <LayersIcon className="text-custom-text-400/40 w-[30px] h-[30px]" />,
+    text: "Your recent issues will appear here once you visit one.",
+  },
+  default: {
+    icon: <History size={30} className="text-custom-text-400/40" />,
+    text: "You don't have any recent items yet.",
+  },
+};

 const getDisplayContent = () => {
-  switch (type) {
-    case "project":
-      return {
-        icon: <Briefcase size={30} className="text-custom-text-400/40" />,
-        text: "Your recent projects will appear here once you visit one.",
-      };
-    // ... other cases
-    default:
-      return {
-        icon: <History size={30} className="text-custom-text-400/40" />,
-        text: "You don't have any recent items yet.",
-      };
-  }
+  return DISPLAY_CONTENT_CONFIG[type as keyof typeof DISPLAY_CONTENT_CONFIG] 
+    ?? DISPLAY_CONTENT_CONFIG.default;
 };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 45c1099 and f7c6b03.

📒 Files selected for processing (3)
  • web/core/components/home/widgets/empty-states/links.tsx (1 hunks)
  • web/core/components/home/widgets/empty-states/recents.tsx (1 hunks)
  • web/core/components/home/widgets/recents/index.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: lint-apiserver
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Analyze (python)
🔇 Additional comments (6)
web/core/components/home/widgets/empty-states/links.tsx (3)

7-9: Well-structured empty state message!

The combination of the rotated icon and descriptive text provides clear guidance to users while maintaining visual appeal through consistent styling.


1-1: Verify the relocation of link creation functionality

The component has been simplified to a static state by removing props and the link creation handler. Please ensure that this functionality has been properly relocated elsewhere in the codebase.

Also applies to: 3-3

✅ Verification successful

Link creation functionality is properly relocated

The link creation functionality has been correctly moved to a more maintainable architecture:

  • UI components handle presentation
  • Business logic is managed in the use-links hook
  • Data operations are handled by the link store
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for components that might have inherited the link creation functionality
rg -t tsx -t ts "handleCreate.*link" --glob '!*.test.*' --glob '!*.spec.*'

Length of output: 104


Script:

#!/bin/bash
# Search for link-related handler functions and components
rg "handleCreate.*[Ll]ink|create[Ll]ink|onLink" --type-add 'ts:*.{ts,tsx}' -t ts

# Find files that might be related to link management
fd -e ts -e tsx . | grep -i "link"

Length of output: 8906


5-6: Verify visual consistency with reduced height

The container height has been reduced from 200px to 110px. Please ensure this change maintains visual consistency with other empty states in the application.

✅ Verification successful

Height change aligns with widget empty states

The reduced height of 110px is consistent with other widget empty states in the application, such as the recents widget (120px). This appears to be a deliberate design pattern for widget-level empty states.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other empty state components to compare heights
rg -t tsx "min-h-\[.*px\].*empty" --glob 'web/**/*empty*.tsx'

Length of output: 91


Script:

#!/bin/bash
# Search for empty states and height patterns without file type restriction
echo "=== Searching for empty states with min-height ==="
rg "min-h-\[.*px\].*empty" web/

echo -e "\n=== Searching for empty state components ==="
fd "empty" web/ --type f

echo -e "\n=== Searching for similar height patterns in components ==="
rg "min-h-\[" web/core/components/

Length of output: 7603

web/core/components/home/widgets/recents/index.tsx (1)

77-77: Verify type compatibility between filter and RecentsEmptyState prop

The filter value being passed as type prop should be type-checked against the expected type in RecentsEmptyState. Consider using a shared enum or type definition for both components to ensure type safety.

✅ Verification successful

Type compatibility is properly maintained

The implementation is type-safe as RecentsEmptyState accepts a string type and handles all possible filter values, including a default case. The filter state is properly managed through FiltersDropdown component.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for type definitions related to recent activity filters
ast-grep --pattern 'type $name = $$$' | rg -A 2 'TRecentActivityFilterKeys|RecentsEmptyState'

Length of output: 95


Script:

#!/bin/bash
# Find RecentsEmptyState component definition and its props
ast-grep --pattern 'interface $props {
  $$$
}' | rg -B2 -A5 'RecentsEmptyState'

# Find the component definition
ast-grep --pattern 'const RecentsEmptyState = $$$' 

# Look for filter-related types/enums
ast-grep --pattern 'type $name = $$$|enum $name {
  $$$
}' | rg -B1 -A5 'filter|Filter|Recent'

# Get context of the current file to see filter definition
rg -B10 -A2 "RecentsEmptyState.*type={filter}" web/core/components/home/widgets/recents/index.tsx

Length of output: 3840


Script:

#!/bin/bash
# Fix the regex and get context around filter usage
rg -B10 -A2 'RecentsEmptyState.*type=\{filter\}' web/core/components/home/widgets/recents/index.tsx

# Look for filter declaration
ast-grep --pattern 'const filter = $$$' web/core/components/home/widgets/recents/index.tsx

Length of output: 801


Script:

#!/bin/bash
# Look for FiltersDropdown component and its props
ast-grep --pattern 'interface $props {
  $$$
}' | rg -B2 -A5 'FiltersDropdown'

# Find filters array definition
rg -B5 -A2 'const filters =' web/core/components/home/widgets/recents/index.tsx

Length of output: 160

web/core/components/home/widgets/empty-states/recents.tsx (2)

32-37: LGTM! Clean and consistent layout implementation

The flex layout implementation with consistent spacing and styling looks good. The min-height ensures proper empty state visibility.


1-38: Verify PR scope and documentation

These changes to the recent activity components seem unrelated to the PR's main objective of implementing sticky notes functionality. Consider:

  1. Moving these changes to a separate PR if they're unrelated
  2. Updating the PR description to clarify the relationship between these changes and the sticky notes feature
  3. Adding test scenarios to verify the empty state behavior

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

♻️ Duplicate comments (1)
web/core/components/stickies/modal/search.tsx (1)

76-79: ⚠️ Potential issue

Add error handling for debouncedSearch call.

The search operation could fail, but errors are not handled.

   onChange={(e) => {
     updateSearchQuery(e.target.value);
-    debouncedSearch();
+    debouncedSearch().catch((error) => {
+      console.error("Search failed:", error);
+      // Consider showing a user-friendly error message
+    });
   }}
🧹 Nitpick comments (13)
web/core/components/home/widgets/empty-states/recents.tsx (3)

4-4: Consider using a union type for better type safety.

Instead of using a generic string type, consider defining specific valid values to prevent potential runtime errors.

-export const RecentsEmptyState = ({ type }: { type: string }) => {
+type RecentsType = 'project' | 'page' | 'issue' | 'default';
+export const RecentsEmptyState = ({ type }: { type: RecentsType }) => {

5-28: LGTM! Well-structured content generation logic.

The switch statement is well-organized with consistent object structure and appropriate default case handling.

Consider standardizing icon styling approach.

While the implementation works, there's a slight inconsistency in how the LayersIcon styling is applied compared to other icons.

-          icon: <LayersIcon className="text-custom-text-400/40 w-[30px] h-[30px]" />,
+          icon: <LayersIcon size={30} className="text-custom-text-400/40" />,

31-37: Consider adding ARIA labels for better accessibility.

The layout and styling look good, but consider adding appropriate ARIA labels to improve accessibility.

-    <div className="min-h-[120px] flex w-full justify-center py-6 bg-custom-border-100 rounded">
+    <div 
+      className="min-h-[120px] flex w-full justify-center py-6 bg-custom-border-100 rounded"
+      role="status"
+      aria-label={`No recent ${type === 'default' ? 'items' : type + 's'}`}
+    >
web/core/components/stickies/sticky/use-operations.tsx (2)

23-38: Consider a more SSR-friendly approach for HTML content validation.

The current implementation uses DOM manipulation which could cause issues in SSR environments. Consider using a regex-based approach or a lightweight HTML parser library.

-const isHtmlContentEmpty = (html: string): boolean => {
-  // Create a temporary DOM element
-  const tempElement = document.createElement("div");
-  tempElement.innerHTML = html;
-
-  // Get text content and trim whitespace
-  const textContent = tempElement.textContent || "";
-  const trimmedContent = textContent.trim();
-
-  return trimmedContent.length === 0;
-};
+const isHtmlContentEmpty = (html: string): boolean => {
+  // Remove HTML tags and decode entities
+  const textContent = html
+    .replace(/<[^>]*>/g, '')
+    .replace(/&nbsp;/g, ' ')
+    .replace(/&amp;/g, '&')
+    .replace(/&lt;/g, '<')
+    .replace(/&gt;/g, '>');
+  return textContent.trim().length === 0;
+};

40-43: Consider memoizing color selection for performance.

If this function is called frequently, consider memoizing the random color selection or moving it to a constant if the color doesn't need to change during the session.

+const memoizedRandomColor = useMemo(() => {
+  const randomIndex = Math.floor(Math.random() * STICKY_COLORS.length);
+  return STICKY_COLORS[randomIndex];
+}, []); // Color stays constant during session
+
 export const getRandomStickyColor = (): string => {
-  const randomIndex = Math.floor(Math.random() * STICKY_COLORS.length);
-  return STICKY_COLORS[randomIndex];
+  return memoizedRandomColor;
 };
web/core/components/stickies/modal/search.tsx (2)

Line range hint 3-18: Consider memoizing the fetchStickies function.

The fetchStickies function is recreated on every render but used in the useCallback hook's dependency array. This could lead to unnecessary re-renders.

-  const fetchStickies = async () => {
-    await fetchWorkspaceStickies(workspaceSlug.toString());
-  };

+  const fetchStickies = useCallback(async () => {
+    await fetchWorkspaceStickies(workspaceSlug.toString());
+  }, [fetchWorkspaceStickies, workspaceSlug]);

41-46: Consider adding a loading state during search.

The debounced search operation could take time, but there's no visual feedback to users.

+ const [isSearching, setIsSearching] = useState(false);

  const debouncedSearch = useCallback(
    debounce(async () => {
+     setIsSearching(true);
      try {
        await fetchStickies();
+     } finally {
+       setIsSearching(false);
+     }
    }, 500),
    [fetchWorkspaceStickies]
  );
web/core/store/sticky/sticky.store.ts (1)

102-135: Consider adding retry logic for fetchNextWorkspaceStickies.

The method handles errors but doesn't provide any retry mechanism, which could be useful for handling temporary network issues.

+ const MAX_RETRIES = 3;
+ const RETRY_DELAY = 1000;

  fetchNextWorkspaceStickies = async (workspaceSlug: string) => {
+   let retries = 0;
    try {
      if (!this.paginationInfo?.next_cursor || !this.paginationInfo.next_page_results || this.loader === "pagination") {
        return;
      }
      this.loader = "pagination";
+     while (retries < MAX_RETRIES) {
        try {
          const response = await this.stickyService.getStickies(
            workspaceSlug,
            this.paginationInfo.next_cursor,
            this.searchQuery
          );
          // ... rest of the success logic ...
+         break;
+       } catch (e) {
+         retries++;
+         if (retries === MAX_RETRIES) throw e;
+         await new Promise(resolve => setTimeout(resolve, RETRY_DELAY));
+       }
+     }
    } catch (e) {
      console.error(e);
      runInAction(() => {
        this.loader = "loaded";
      });
    }
  };
web/core/components/stickies/layout/stickies-truncated.tsx (1)

10-13: Add type safety for workspaceSlug parameter.

Consider adding type safety by explicitly typing the params object:

-  const { workspaceSlug } = useParams();
+  const { workspaceSlug } = useParams() as { workspaceSlug: string };
web/core/components/home/widgets/empty-states/links.tsx (1)

4-4: Consider accessibility improvements

The component could benefit from semantic HTML and accessibility attributes.

Consider this enhancement:

-    <div className="min-h-[110px] flex w-full justify-center py-6 bg-custom-border-100 rounded">
+    <section aria-label="Quick Links Empty State" className="min-h-[110px] flex w-full justify-center py-6 bg-custom-border-100 rounded">
web/core/components/stickies/layout/sticky-dnd-wrapper.tsx (1)

24-42: Add prop validation for required props

The component accepts several required props but lacks validation. Consider adding runtime checks to provide helpful error messages during development.

 export const StickyDNDWrapper = observer(
   ({
     stickyId,
     workspaceSlug,
     itemWidth,
     isLastChild,
     isInFirstRow,
     isInLastRow,
     handleDrop,
   }: {
     stickyId: string;
     workspaceSlug: string;
     itemWidth: string;
     isLastChild: boolean;
     isInFirstRow: boolean;
     isInLastRow: boolean;
     handleDrop: (self: DropTargetRecord, source: ElementDragPayload, location: DragLocationHistory) => void;
   }) => {
+    if (!stickyId || !workspaceSlug) {
+      console.error('StickyDNDWrapper: stickyId and workspaceSlug are required');
+      return null;
+    }
web/core/components/stickies/layout/stickies-list.tsx (2)

36-42: Memoize row position calculations for better performance

The getRowPositions function is recreated on every render and performs calculations that could be memoized.

+  const getRowPositions = useMemo(() => (index: number) => {
     const currentRow = Math.floor(index / columnCount);
     return {
       isInFirstRow: currentRow === 0,
       isInLastRow: currentRow === totalRows - 1 || index >= workspaceStickies.length - columnCount,
     };
-  };
+  }, [columnCount, totalRows, workspaceStickies.length]);

130-138: Extract breakpoints and column counts to configuration

The breakpoint values and corresponding column counts should be extracted to configuration constants for better maintainability.

+const BREAKPOINTS = {
+  SM: 640,
+  MD: 768,
+  LG: 1024,
+  XL: 1280,
+} as const;
+
+const COLUMN_COUNTS = {
+  DEFAULT: 4,
+  SM: 2,
+  MD: 3,
+  LG: 4,
+  XL: 5,
+  XXL: 6,
+} as const;
+
 const getColumnCount = (width: number | null): number => {
-    if (width === null) return 4;
+    if (width === null) return COLUMN_COUNTS.DEFAULT;

-    if (width < 640) return 2; // sm
-    if (width < 768) return 3; // md
-    if (width < 1024) return 4; // lg
-    if (width < 1280) return 5; // xl
-    return 6; // 2xl and above
+    if (width < BREAKPOINTS.SM) return COLUMN_COUNTS.SM;
+    if (width < BREAKPOINTS.MD) return COLUMN_COUNTS.MD;
+    if (width < BREAKPOINTS.LG) return COLUMN_COUNTS.LG;
+    if (width < BREAKPOINTS.XL) return COLUMN_COUNTS.XL;
+    return COLUMN_COUNTS.XXL;
   };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f7c6b03 and 323089c.

📒 Files selected for processing (12)
  • web/app/[workspaceSlug]/(projects)/stickies/header.tsx (1 hunks)
  • web/core/components/home/widgets/empty-states/links.tsx (1 hunks)
  • web/core/components/home/widgets/empty-states/recents.tsx (1 hunks)
  • web/core/components/home/widgets/links/links.tsx (1 hunks)
  • web/core/components/stickies/layout/stickies-list.tsx (1 hunks)
  • web/core/components/stickies/layout/stickies-truncated.tsx (1 hunks)
  • web/core/components/stickies/layout/sticky-dnd-wrapper.tsx (1 hunks)
  • web/core/components/stickies/modal/search.tsx (4 hunks)
  • web/core/components/stickies/sticky/root.tsx (3 hunks)
  • web/core/components/stickies/sticky/use-operations.tsx (3 hunks)
  • web/core/components/stickies/widget.tsx (3 hunks)
  • web/core/store/sticky/sticky.store.ts (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • web/core/components/stickies/sticky/root.tsx
  • web/core/components/stickies/widget.tsx
  • web/app/[workspaceSlug]/(projects)/stickies/header.tsx
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (14)
web/core/components/home/widgets/empty-states/recents.tsx (2)

1-2: LGTM! Clean import organization.

The imports are well-organized and correctly separated by source.


29-29: LGTM! Clean destructuring.

The destructuring is clear and effective.

web/core/components/stickies/sticky/use-operations.tsx (2)

2-2: LGTM! Well-structured type definitions and imports.

The type definitions are clear and type-safe, particularly the new updatePosition method signature in the TOperations type.

Also applies to: 4-4, 8-16


131-148: Enhance error handling in updatePosition.

This is a duplicate of a previous review comment. The error handling needs improvement to be more specific and provide better error messages.

 updatePosition: async (
   workspaceSlug: string,
   sourceId: string,
   droppedId: string,
   instruction: InstructionType
 ) => {
   try {
     if (!workspaceSlug) throw new Error("Missing required fields");
+    if (!sourceId || !droppedId) throw new Error("Missing sticky IDs");
     await updateStickyPosition(workspaceSlug, sourceId, droppedId, instruction);
   } catch (error) {
     setToast({
-      message: "The sticky could not be updated",
+      message: error instanceof Error ? error.message : "Failed to update sticky position",
       type: TOAST_TYPE.ERROR,
       title: "Sticky not updated",
     });
     throw error;
   }
 },
web/core/store/sticky/sticky.store.ts (1)

218-265: LGTM! Good error handling with state rollback.

The updateStickyPosition method properly handles errors by rolling back to the previous sort order state when the update fails.

web/core/components/stickies/layout/stickies-truncated.tsx (4)

1-9: LGTM! Imports are well-organized.

All necessary dependencies are properly imported and organized.


37-37: Add null check for workspaceSlug prop.

The workspaceSlug prop passed to StickiesLayout could be undefined.

-      <StickiesLayout workspaceSlug={workspaceSlug?.toString()} />
+      <StickiesLayout workspaceSlug={workspaceSlug?.toString() ?? ""} />

15-19: ⚠️ Potential issue

Add null check before toString() call.

The toString() call could throw if workspaceSlug is undefined.

-    workspaceSlug ? () => fetchWorkspaceStickies(workspaceSlug.toString()) : null,
+    workspaceSlug ? () => fetchWorkspaceStickies(workspaceSlug?.toString() ?? "") : null,

Likely invalid or redundant comment.


18-18: Verify the data revalidation strategy.

The current setup disables both stale data revalidation and focus revalidation. Please verify if this aligns with the application's data freshness requirements.

✅ Verification successful

SWR configuration is appropriate for this component

The current configuration aligns with the established patterns across the codebase, particularly for similar components handling relatively static data. This approach is consistently used in related components like stickies-infinite.tsx and various issue layouts.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other SWR configurations in the codebase to ensure consistency
rg -A 2 'revalidateIfStale|revalidateOnFocus' --type ts --type tsx

Length of output: 96


Script:

#!/bin/bash
# Search for SWR configurations with correct file type handling
rg -t typescript -t tsx 'revalidateIfStale|revalidateOnFocus' -A 2

# Search for SWR configuration files or setup
fd -e ts -e tsx -E '*.d.ts' -E '*.test.*' | rg -l 'swr|SWR'

# Search for any comments or documentation about data freshness
rg -t typescript -t tsx -B 2 'revalidateIfStale|revalidateOnFocus' --context 3

Length of output: 289


Script:

#!/bin/bash
# Search for SWR configurations using file extensions
rg 'revalidateIfStale|revalidateOnFocus' --glob '*.{ts,tsx}'

# Look for SWR setup files and configurations
rg 'SWRConfig|SWRConfiguration' --glob '*.{ts,tsx}'

# Search for useSWR hook usage patterns
rg 'useSWR.*{' --glob '*.{ts,tsx}' -A 2

Length of output: 6549

web/core/components/home/widgets/links/links.tsx (2)

30-30: Consider UX implications of removing link creation from empty state

The empty state no longer provides a direct way to create links. This might confuse users who see the message "Add any links you need for quick access" but have no obvious way to do so.

Consider:

  1. Adding a clear path for users to create links
  2. Updating the empty state message if link creation is intentionally disabled
  3. Documenting the new intended workflow for link management

23-24: Verify the removal of link creation functionality

The removal of toggleLinkModal suggests that link creation capability has been intentionally removed. This architectural change might impact user experience if there's no alternative way to create links.

Let's verify if there's an alternative implementation for link creation:

web/core/components/stickies/layout/sticky-dnd-wrapper.tsx (2)

48-120: Ensure proper cleanup in useEffect to prevent memory leaks


48-120: ⚠️ Potential issue

Add missing dependencies to useEffect

The useEffect hook is missing several dependencies that could lead to stale closures:

  • pathName
  • handleDrop
  • isLastChild
  • workspaceSlug
-    }, [stickyId, isDragging]);
+    }, [stickyId, isDragging, pathName, handleDrop, isLastChild, workspaceSlug]);

Likely invalid or redundant comment.

web/core/components/stickies/layout/stickies-list.tsx (1)

1-24: LGTM! Clean and well-organized imports and type definitions.

web/core/components/stickies/sticky/use-operations.tsx Outdated Show resolved Hide resolved
web/core/components/stickies/sticky/use-operations.tsx Outdated Show resolved Hide resolved
web/core/components/stickies/modal/search.tsx Show resolved Hide resolved
web/core/store/sticky/sticky.store.ts Outdated Show resolved Hide resolved
web/core/components/stickies/layout/stickies-list.tsx Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
web/core/components/stickies/sticky/use-operations.tsx (2)

64-85: ⚠️ Potential issue

Fix the empty sticky check logic.

The empty sticky check has several issues that need to be addressed.

The condition getWorkspaceStickies(workspaceSlug).length >= 0 is always true, and there's a redundant call to getWorkspaceStickies. This should be fixed as suggested in the previous review.


151-151: ⚠️ Potential issue

Add missing dependencies to useMemo.

The dependency array is missing several dependencies that are used within the memoized operations.

This issue was previously identified and should be fixed as suggested in the earlier review.

🧹 Nitpick comments (2)
web/core/components/stickies/sticky/use-operations.tsx (2)

23-38: Consider optimizing HTML content check for SSR compatibility.

The current implementation uses DOM manipulation which could cause issues in SSR environments. Consider using a regex-based approach or a lightweight HTML parser library.

-const isHtmlContentEmpty = (html: string): boolean => {
-  // Create a temporary DOM element
-  const tempElement = document.createElement("div");
-  tempElement.innerHTML = html;
-
-  // Get text content and trim whitespace
-  const textContent = tempElement.textContent || "";
-  const trimmedContent = textContent.trim();
-
-  return trimmedContent.length === 0;
-};
+const isHtmlContentEmpty = (html: string): boolean => {
+  // Remove HTML tags and decode entities
+  const textContent = html
+    .replace(/<[^>]*>/g, '')
+    .replace(/&nbsp;/g, ' ')
+    .replace(/&amp;/g, '&')
+    .replace(/&lt;/g, '<')
+    .replace(/&gt;/g, '>');
+  
+  return textContent.trim().length === 0;
+};

40-43: Consider memoizing the random color selection.

The getRandomStickyColor function is simple and effective. However, if it's called frequently (e.g., during rapid sticky creation), consider memoizing the result for a short duration.

+const colorCache = {
+  color: '',
+  timestamp: 0,
+  TTL: 1000, // 1 second
+};
+
 export const getRandomStickyColor = (): string => {
+  const now = Date.now();
+  if (now - colorCache.timestamp < colorCache.TTL) {
+    return colorCache.color;
+  }
+
   const randomIndex = Math.floor(Math.random() * STICKY_COLORS.length);
-  return STICKY_COLORS[randomIndex];
+  colorCache.color = STICKY_COLORS[randomIndex];
+  colorCache.timestamp = now;
+  return colorCache.color;
 };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 323089c and 6336a8c.

📒 Files selected for processing (1)
  • web/core/components/stickies/sticky/use-operations.tsx (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (2)
web/core/components/stickies/sticky/use-operations.tsx (2)

2-2: LGTM! Type definitions are well-structured.

The type modifications and new imports are appropriate for the added functionality. The updatePosition method signature is well-defined with clear parameter types.

Also applies to: 4-4, 8-16


132-149: Enhance error handling in updatePosition.

The error handling in the updatePosition method could be more specific.

Consider implementing the previously suggested improvements to provide better error messages and validation.

@pushya22 pushya22 marked this pull request as draft January 15, 2025 10:50
@aaryan610 aaryan610 changed the title Feat stickies page feat: stickies page Jan 15, 2025
@aaryan610 aaryan610 added this to the v0.24.0 milestone Jan 15, 2025
@aaryan610 aaryan610 marked this pull request as ready for review January 16, 2025 07:51
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🔭 Outside diff range comments (1)
web/core/components/home/widgets/empty-states/root.tsx (1)

Line range hint 33-40: Add aria-label to the create project button for better accessibility.

The button's onClick handler should also have proper error handling and feedback for users without permissions.

 cta: {
   text: "Create Project",
+  ariaLabel: "Create a new project",
   onClick: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
     if (!canCreateProject) {
+      e.preventDefault();
+      e.stopPropagation();
+      // Show a tooltip or notification explaining why the action is not allowed
+      return;
     }
     e.preventDefault();
     e.stopPropagation();
     setTrackElement("Sidebar");
     toggleCreateProjectModal(true);
   },
 },
🧹 Nitpick comments (13)
web/core/components/stickies/action-bar.tsx (2)

33-36: Add null checks and improve color fallback logic.

The derived value helps centralize color management, but consider these improvements:

  1. Add null check for stickies[recentStickyId] to prevent runtime errors
  2. Consider a more explicit fallback color instead of relying on the first color in the list
   const recentStickyBackgroundColor = recentStickyId
-    ? STICKY_COLORS_LIST.find((c) => c.key === stickies[recentStickyId].background_color)?.backgroundColor
-    : STICKY_COLORS_LIST[0].backgroundColor;
+    ? STICKY_COLORS_LIST.find((c) => c.key === stickies[recentStickyId]?.background_color)?.backgroundColor
+      ?? '#E2E8F0' // explicit fallback color
+    : '#E2E8F0';

Line range hint 78-92: Consider accessibility improvements for tooltips.

While the tooltip implementation is good, consider these accessibility enhancements:

  1. Add aria-label to buttons
  2. Consider adding keyboard navigation support for the tooltip content
   <button
     className="btn btn--icon rounded-full w-10 h-10 flex items-center justify-center shadow-sm bg-custom-background-100"
     onClick={() => setShowRecentSticky(true)}
+    aria-label="Show recent sticky note"
     style={{ color: recentStickyBackgroundColor }}
   >
web/core/components/stickies/sticky/inputs.tsx (2)

38-45: Add error handling to form submission.

The form submission callback should include error handling to gracefully handle potential failures.

Consider updating the implementation:

 const handleFormSubmit = useCallback(
   async (formdata: Partial<TSticky>) => {
+    try {
       await handleUpdate({
         description_html: formdata.description_html ?? "<p></p>",
       });
+    } catch (error) {
+      console.error("Failed to update sticky:", error);
+      // Consider adding user notification here
+    }
   },
   [handleUpdate]
 );

71-74: Consider improvements to editor configuration.

A few suggestions to enhance the editor configuration:

  1. The placeholder text could be more descriptive for better user guidance
  2. Consider making the min-height configurable through props

Consider these improvements:

-placeholder="Click to type here"
+placeholder="Enter your note content here..."
-containerClassName="px-0 text-base min-h-[250px] w-full"
+containerClassName={`px-0 text-base min-h-[${minHeight ?? '250px'}] w-full`}

Add the minHeight prop to TProps:

type TProps = {
  // ... existing props
  minHeight?: string;
};
web/core/components/home/widgets/manage/widget-item.tsx (2)

49-49: Add null check for widget title lookup.

Consider adding a fallback for cases where the widget key might not exist in HOME_WIDGETS_LIST.

-  const widgetTitle = HOME_WIDGETS_LIST[widget.key]?.title;
+  const widgetTitle = HOME_WIDGETS_LIST[widget.key]?.title ?? widget.key.replaceAll("_", " ");

Line range hint 52-119: Consider adding error boundaries for drag-and-drop operations.

The drag-and-drop implementation is robust, but adding error boundaries would help gracefully handle any potential failures during drag operations and prevent the entire widget management interface from breaking.

Would you like me to provide an example implementation of an error boundary component for the drag-and-drop operations?

web/core/components/home/widgets/empty-states/stickies.tsx (1)

4-13: LGTM! Consider adding aria-label for better accessibility.

The empty state implementation is clean and follows React best practices. The styling is consistent with Tailwind conventions.

Consider adding an aria-label to improve accessibility:

-  <div className="min-h-[110px] flex w-full justify-center py-6 bg-custom-border-100 rounded">
+  <div className="min-h-[110px] flex w-full justify-center py-6 bg-custom-border-100 rounded" aria-label="No stickies available">
web/core/components/editor/sticky-editor/color-palette.tsx (2)

50-52: Consider adding validation for color keys.

The type definition looks good, but consider adding validation to ensure background_color matches the available color keys.

type TColorKey = typeof STICKY_COLORS_LIST[number]['key'];

type TProps = {
  handleUpdate: (data: Partial<Omit<TSticky, 'background_color'> & { background_color: TColorKey }>) => Promise<void>;
};

54-78: Consider keyboard navigation improvements.

The color palette implementation is good, but could benefit from keyboard navigation enhancements.

Consider these improvements:

  1. Add aria-label to buttons with color names
  2. Implement arrow key navigation between colors
  3. Add role="toolbar" to the color palette container
-    <div className="flex flex-wrap gap-2">
+    <div className="flex flex-wrap gap-2" role="toolbar">
       {STICKY_COLORS_LIST.map((color) => (
         <button
           key={color.key}
           type="button"
+          aria-label={`${color.label} background color`}
           onClick={() => {
web/core/components/home/home-dashboard-widgets.tsx (1)

68-93: Consider extracting widget rendering logic to a separate component.

The conditional rendering logic for widgets could be extracted to improve readability and maintainability.

+const WidgetRenderer: React.FC<{
+  widgetKey: THomeWidgetKeys;
+  widgetComponent: React.FC<THomeWidgetProps> | null;
+  isEnabled: boolean;
+  workspaceSlug: string;
+}> = ({ widgetKey, widgetComponent: WidgetComponent, isEnabled, workspaceSlug }) => {
+  if (!WidgetComponent || !isEnabled) return null;
+  return (
+    <div key={widgetKey} className="py-4">
+      <WidgetComponent workspaceSlug={workspaceSlug.toString()} />
+    </div>
+  );
+};

 {isAnyWidgetEnabled ? (
   <div className="flex flex-col divide-y-[1px] divide-custom-border-100">
     {orderedWidgets.map((key) => {
-      const WidgetComponent = HOME_WIDGETS_LIST[key]?.component;
-      const isEnabled = widgetsMap[key]?.is_enabled;
-      if (!WidgetComponent || !isEnabled) return null;
-      return (
-        <div key={key} className="py-4">
-          <WidgetComponent workspaceSlug={workspaceSlug.toString()} />
-        </div>
-      );
+      return (
+        <WidgetRenderer
+          key={key}
+          widgetKey={key}
+          widgetComponent={HOME_WIDGETS_LIST[key]?.component}
+          isEnabled={widgetsMap[key]?.is_enabled}
+          workspaceSlug={workspaceSlug.toString()}
+        />
+      );
     })}
   </div>
 ) : (
   // ... empty state remains the same
 )}
web/core/components/home/widgets/empty-states/root.tsx (1)

Line range hint 20-24: Consider memoizing the canCreateProject value.

Since this value depends on permissions that may not change frequently, memoizing it could prevent unnecessary recalculations.

-const canCreateProject = allowPermissions(
+const canCreateProject = React.useMemo(() => allowPermissions(
   [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
   EUserPermissionsLevel.WORKSPACE
-);
+), [allowPermissions]);
web/core/components/empty-state/empty-state.tsx (1)

78-79: Consider destructuring primaryButtonConfig for better readability.

The repeated access to primaryButtonConfig properties could be simplified.

+const { size, variant } = primaryButtonConfig;
 const commonProps = {
-  size: primaryButtonConfig.size,
-  variant: primaryButtonConfig.variant,
+  size,
+  variant,
   prependIcon: primaryButton.icon,
   onClick: primaryButtonOnClick ? primaryButtonOnClick : undefined,
   disabled: !isEditingAllowed,
 };
web/core/constants/empty-state.tsx (1)

946-957: Consider adding a secondary action for widget management.

The HOME_WIDGETS empty state could benefit from a secondary action to help users understand what widgets are available.

 [EmptyStateType.HOME_WIDGETS]: {
   key: EmptyStateType.HOME_WIDGETS,
   title: "It's Quiet Without Widgets, Turn Them On",
   description: "It looks like all your widgets are turned off. Enable them\nnow to enhance your experience!",
   path: "/empty-state/dashboard/widgets",
   primaryButton: {
     icon: <Shapes className="size-4" />,
     text: "Manage widgets",
   },
+  secondaryButton: {
+    text: "Learn about widgets",
+    comicBox: {
+      title: "Widgets enhance your dashboard",
+      description: "Widgets show you quick links, recent activity, and your stickies at a glance.",
+    },
+  },
   access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
   accessType: "workspace",
 },
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6336a8c and 1ce00d8.

📒 Files selected for processing (29)
  • packages/constants/src/index.ts (1 hunks)
  • packages/editor/src/styles/editor.css (1 hunks)
  • packages/editor/src/styles/variables.css (0 hunks)
  • packages/types/src/stickies.d.ts (1 hunks)
  • web/app/[workspaceSlug]/(projects)/stickies/page.tsx (1 hunks)
  • web/core/components/editor/sticky-editor/color-palette.tsx (1 hunks)
  • web/core/components/editor/sticky-editor/color-pallete.tsx (0 hunks)
  • web/core/components/editor/sticky-editor/toolbar.tsx (1 hunks)
  • web/core/components/empty-state/empty-state.tsx (4 hunks)
  • web/core/components/home/home-dashboard-widgets.tsx (1 hunks)
  • web/core/components/home/root.tsx (1 hunks)
  • web/core/components/home/user-greetings.tsx (2 hunks)
  • web/core/components/home/widgets/empty-states/root.tsx (2 hunks)
  • web/core/components/home/widgets/empty-states/stickies.tsx (1 hunks)
  • web/core/components/home/widgets/manage/widget-item.tsx (3 hunks)
  • web/core/components/stickies/action-bar.tsx (3 hunks)
  • web/core/components/stickies/empty.tsx (0 hunks)
  • web/core/components/stickies/layout/stickies-infinite.tsx (1 hunks)
  • web/core/components/stickies/layout/stickies-list.tsx (1 hunks)
  • web/core/components/stickies/layout/stickies-truncated.tsx (1 hunks)
  • web/core/components/stickies/modal/stickies.tsx (3 hunks)
  • web/core/components/stickies/sticky/inputs.tsx (2 hunks)
  • web/core/components/stickies/sticky/root.tsx (3 hunks)
  • web/core/components/stickies/sticky/use-operations.tsx (4 hunks)
  • web/core/components/stickies/widget.tsx (1 hunks)
  • web/core/constants/empty-state.tsx (3 hunks)
  • web/core/store/sticky/sticky.store.ts (5 hunks)
  • web/core/store/workspace/home.ts (3 hunks)
  • web/styles/globals.css (1 hunks)
💤 Files with no reviewable changes (3)
  • web/core/components/editor/sticky-editor/color-pallete.tsx
  • web/core/components/stickies/empty.tsx
  • packages/editor/src/styles/variables.css
✅ Files skipped from review due to trivial changes (1)
  • web/core/components/editor/sticky-editor/toolbar.tsx
🚧 Files skipped from review as they are similar to previous changes (7)
  • packages/constants/src/index.ts
  • web/core/components/stickies/widget.tsx
  • web/core/components/stickies/modal/stickies.tsx
  • web/core/components/stickies/sticky/root.tsx
  • web/core/components/stickies/layout/stickies-truncated.tsx
  • web/app/[workspaceSlug]/(projects)/stickies/page.tsx
  • web/core/components/stickies/layout/stickies-infinite.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
web/core/components/empty-state/empty-state.tsx

[error] 187-192: Avoid using unnecessary Fragment.

A fragment is redundant if it contains only one child, or if it is the child of a html element, and is not a keyed fragment.
Unsafe fix: Remove the Fragment

(lint/complexity/noUselessFragments)

🔇 Additional comments (23)
web/core/store/workspace/home.ts (2)

14-14: LGTM! Well-typed interface addition.

The new computed property is properly typed and well-placed in the interface definition.


57-59: LGTM! Clean and efficient implementation.

The computed property implementation:

  • Uses efficient methods (Object.values + Array.some)
  • Follows MobX best practices with no side effects
  • Will properly cache results and only recompute when widgetsMap changes
web/core/components/stickies/action-bar.tsx (2)

6-11: Well-organized imports with clear sectioning!

The imports are properly organized with descriptive comments, making the code more maintainable.


21-32: Clean state management organization!

Good separation of state variables, navigation, and store hooks with clear comments improves code readability.

web/core/components/stickies/sticky/inputs.tsx (1)

4-21: LGTM! Clean type definitions and well-organized imports.

The addition of the optional showToolbar prop and the organization of imports with clear grouping comments enhance code readability and maintainability.

Also applies to: 24-24

web/core/components/home/widgets/manage/widget-item.tsx (2)

17-25: Well-organized import categorization!

The clear categorization of imports improves code organization and maintainability.


131-131: Clean widget title rendering!

The use of the widgetTitle variable improves code maintainability and centralizes title management.

packages/editor/src/styles/editor.css (1)

148-148: Verify checkbox visibility across themes.

The change to transparent background might affect checkbox visibility, especially in dark mode where contrast could be an issue.

Run the following script to check for any related accessibility or contrast issues in the codebase:

✅ Verification successful

Checkbox visibility is properly maintained

The transparent background is safe as the checkbox remains visible through:

  • Themed border colors using CSS custom properties
  • Hover and checked states with appropriate background colors
  • Consistent styling across themes through CSS variables
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any related accessibility or contrast issues with checkboxes

# Search for any existing accessibility-related comments or issues
rg -i "checkbox.*visibility|checkbox.*contrast|checkbox.*accessibility" --type css --type ts --type tsx

# Search for any theme-specific checkbox styles
rg "checkbox.*dark|checkbox.*light" --type css

Length of output: 182


Script:

#!/bin/bash
# Search for checkbox-related styles
rg -A 3 "input\[type=.?checkbox" --type css

# Search for theme-related styles and CSS custom properties
rg "(:root|\.theme--(dark|light))" -A 5 --type css

# Search for accessibility-related code in all files
rg -i "(aria-|role=)" . | rg -i "checkbox"

Length of output: 3962

web/styles/globals.css (3)

384-406: LGTM! Well-organized color variables.

The color variables are well-structured with clear separation between text and background colors. The naming convention is consistent and semantic.


382-428: Excellent color system implementation!

The color system is well-implemented with:

  • Clear separation of concerns between text and background colors
  • Consistent naming conventions
  • Proper theme support including contrast modes
  • Semantic organization of color variables

408-427: Verify color contrast ratios for accessibility.

The dark theme colors look good, but it's important to verify that they meet WCAG accessibility standards for contrast ratios.

Run the following script to extract and analyze the color combinations:

✅ Verification successful

Color contrast ratios meet accessibility standards

The editor color combinations in both light and dark themes meet WCAG AA standards for large text (3:1 minimum contrast ratio). The dark theme's contrast ratios range from 3.5:1 to 4.8:1, while the light theme shows even better contrast due to lighter backgrounds.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Extract and analyze color combinations for contrast verification

# Search for any existing color contrast tests
rg -i "contrast.*test|wcag|a11y" --type test

# Search for any color utility functions that might handle contrast
rg "contrast|color.*util" --type ts --type tsx

Length of output: 152


Script:

#!/bin/bash
# Search for color-related CSS variables and their usage
rg "color|--editor" web/styles/globals.css

# Search for any test files or utilities related to colors/contrast
rg -l "contrast|wcag|a11y|color.*test"

# Find usage of the background color variables
rg "var\(--editor-colors-.*-background\)"

Length of output: 20753

web/core/components/home/user-greetings.tsx (1)

56-59: LGTM! Good migration to the UI component library.

The replacement of the HTML button with the Button component from @plane/ui improves consistency across the application while maintaining the same functionality.

web/core/components/editor/sticky-editor/color-palette.tsx (1)

3-48: LGTM! Well-structured color constants.

The color constants are well-organized with clear keys, labels, and use of CSS variables for theme support.

web/core/components/home/root.tsx (1)

62-64: LGTM! Minor styling adjustment.

The gap reduction from 7 to 6 units maintains visual hierarchy while likely improving alignment with other components.

web/core/components/home/home-dashboard-widgets.tsx (1)

18-49: LGTM! Well-structured widget configuration.

The HOME_WIDGETS_LIST is well-typed and provides a clear structure for widget configuration with title, component, and fullWidth properties.

web/core/constants/empty-state.tsx (1)

921-933: LGTM! Well-defined empty state for stickies.

The empty state configuration for stickies is comprehensive and includes all necessary properties.

packages/types/src/stickies.d.ts (1)

4-15: LGTM! Type definitions align with API response structure.

The changes to the TSticky type definition look good:

  • Added metadata fields (created_at, updated_at, etc.)
  • Added sort_order for position management
  • Added logo_props for UI customization
  • Proper typing of optional fields
web/core/components/stickies/sticky/use-operations.tsx (2)

141-141: ⚠️ Potential issue

Add missing dependencies to useMemo.

The dependency array is missing several dependencies that are used within the memoized operations.

Add the missing dependencies:

-[createSticky, deleteSticky, getWorkspaceStickyIds, stickies, updateSticky, updateStickyPosition, workspaceSlug]
+[
+  createSticky,
+  deleteSticky,
+  getWorkspaceStickyIds,
+  stickies,
+  updateSticky,
+  updateStickyPosition,
+  workspaceSlug,
+  isValid
+]

Likely invalid or redundant comment.


60-72: ⚠️ Potential issue

Fix empty sticky check logic.

The condition workspaceStickIds && workspaceStickIds.length >= 0 is always true. This could lead to unexpected behavior.

Apply this diff to fix the logic:

-if (workspaceStickIds && workspaceStickIds.length >= 0) {
+if (workspaceStickIds && workspaceStickIds.length > 0) {

Likely invalid or redundant comment.

web/core/components/stickies/layout/stickies-list.tsx (2)

91-94: 🛠️ Refactor suggestion

Add error handling for sticky creation.

The sticky creation operation should be wrapped in try-catch to handle potential failures gracefully.

-primaryButtonOnClick={() => {
-  toggleShowNewSticky(true);
-  stickyOperations.create();
-}}
+primaryButtonOnClick={async () => {
+  try {
+    toggleShowNewSticky(true);
+    await stickyOperations.create();
+  } catch (error) {
+    console.error('Failed to create sticky:', error);
+    toggleShowNewSticky(false);
+    setToast({
+      type: TOAST_TYPE.ERROR,
+      title: "Error",
+      message: "Failed to create sticky"
+    });
+  }
+}}

Likely invalid or redundant comment.


55-72: 🛠️ Refactor suggestion

Simplify handleDrop logic and improve error handling.

The nested conditionals make the code harder to follow. Consider this refactoring:

-const handleDrop = (self: DropTargetRecord, source: ElementDragPayload, location: DragLocationHistory) => {
-  const dropTargets = location?.current?.dropTargets ?? [];
-  if (!dropTargets || dropTargets.length <= 0) return;
-
-  const dropTarget = dropTargets[0];
-  if (!dropTarget?.data?.id || !source.data?.id) return;
-
-  const instruction = getInstructionFromPayload(dropTarget, source, location);
-  const droppedId = dropTarget.data.id;
-  const sourceId = source.data.id;
-
-  try {
-    if (!instruction || !droppedId || !sourceId) return;
-    stickyOperations.updatePosition(workspaceSlug, sourceId as string, droppedId as string, instruction);
-  } catch (error) {
-    console.error("Error reordering sticky:", error);
-  }
+const handleDrop = async (self: DropTargetRecord, source: ElementDragPayload, location: DragLocationHistory) => {
+  try {
+    const dropTarget = location?.current?.dropTargets?.[0];
+    const sourceId = source.data?.id;
+    const droppedId = dropTarget?.data?.id;
+    
+    if (!dropTarget || !sourceId || !droppedId) return;
+    
+    const instruction = getInstructionFromPayload(dropTarget, source, location);
+    if (!instruction) return;
+    
+    await stickyOperations.updatePosition(
+      workspaceSlug,
+      sourceId,
+      droppedId,
+      instruction
+    );
+  } catch (error) {
+    console.error("Error reordering sticky:", error);
+    setToast({
+      type: TOAST_TYPE.ERROR,
+      title: "Error",
+      message: "Failed to reorder sticky"
+    });
+  }
};

Likely invalid or redundant comment.

web/core/store/sticky/sticky.store.ts (2)

223-270: LGTM! Good error handling with state rollback.

The updateStickyPosition method has proper error handling:

  • Saves previous state before update
  • Rolls back to previous state on error
  • Uses runInAction for state updates

78-84: 🛠️ Refactor suggestion

Add search filtering to getWorkspaceStickyIds.

The computed function only sorts by sort_order but doesn't filter based on searchQuery.

getWorkspaceStickyIds = computedFn((workspaceSlug: string) =>
  orderBy(
-    (this.workspaceStickies[workspaceSlug] || []).map((stickyId) => this.stickies[stickyId]),
+    (this.workspaceStickies[workspaceSlug] || [])
+      .map((stickyId) => this.stickies[stickyId])
+      .filter((sticky) => 
+        !this.searchQuery || 
+        sticky.name?.toLowerCase().includes(this.searchQuery.toLowerCase()) ||
+        sticky.description_html?.toLowerCase().includes(this.searchQuery.toLowerCase())
+      ),
    ["sort_order"],
    ["desc"]
  ).map((sticky) => sticky.id)
);

Likely invalid or redundant comment.

web/core/components/empty-state/empty-state.tsx Outdated Show resolved Hide resolved
web/core/store/sticky/sticky.store.ts Show resolved Hide resolved
@pushya22 pushya22 changed the title feat: stickies page [WEB-3096] feat: stickies page Jan 16, 2025
Copy link

makeplane bot commented Jan 16, 2025

Pull Request Linked with Plane Issues

Comment Automatically Generated by Plane

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
web/app/[workspaceSlug]/(projects)/stickies/page.tsx (2)

7-16: Add loading state and error handling.

While the implementation is clean, consider these improvements for better user experience and reliability:

  1. Add a loading state while StickiesInfinite is initializing
  2. Wrap StickiesInfinite with an error boundary
 export default function WorkspaceStickiesPage() {
   return (
     <>
       <PageHead title="Your stickies" />
-      <div className="relative h-full w-full overflow-hidden overflow-y-auto">
-        <StickiesInfinite />
+      <div className="relative h-full w-full overflow-hidden overflow-y-auto" role="main">
+        <ErrorBoundary fallback={<StickyErrorState />}>
+          <Suspense fallback={<StickiesLoader />}>
+            <StickiesInfinite />
+          </Suspense>
+        </ErrorBoundary>
       </div>
     </>
   );
 }

11-11: Add ARIA landmark for better accessibility.

The main content div should have an ARIA landmark role for better screen reader navigation.

-<div className="relative h-full w-full overflow-hidden overflow-y-auto">
+<div className="relative h-full w-full overflow-hidden overflow-y-auto" role="main">
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ce00d8 and 6164f21.

📒 Files selected for processing (4)
  • web/app/[workspaceSlug]/(projects)/stickies/header.tsx (1 hunks)
  • web/app/[workspaceSlug]/(projects)/stickies/page.tsx (1 hunks)
  • web/core/components/empty-state/empty-state.tsx (5 hunks)
  • web/core/components/stickies/sticky/use-operations.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • web/app/[workspaceSlug]/(projects)/stickies/header.tsx
  • web/core/components/empty-state/empty-state.tsx
  • web/core/components/stickies/sticky/use-operations.tsx
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (2)
web/app/[workspaceSlug]/(projects)/stickies/page.tsx (2)

1-6: LGTM! Clean imports and proper client directive.

The imports are well-organized and the "use client" directive is correctly placed.


7-16: Validate workspaceSlug parameter.

The previous review comment about validating workspaceSlug is still applicable.

@sriramveeraghanta sriramveeraghanta merged commit fd7eedc into preview Jan 16, 2025
9 of 14 checks passed
@sriramveeraghanta sriramveeraghanta deleted the feat-stickies-page branch January 16, 2025 14:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants