Skip to content

Conversation

@im-vipin
Copy link
Member

@im-vipin im-vipin commented Dec 29, 2025

Description

This PR adds backend-driven pagination, filtering, and ordering for project pages to ensure consistent and scalable page listing.

  • Moves filtering and order_by logic fully to the backend
  • Introduces cursor-based pagination for project pages
  • Adds reusable page filter utilities (search, favorites, created_by, created_at)
  • Updates frontend store, services, and components to support paginated fetching and infinite scroll
  • Ensures stable ordering across paginated results

Type of Change

  • Feature (non-breaking change which adds functionality)

Screenshots and Media (if applicable)

Screen.Recording.2025-12-29.at.5.14.02.PM.mov

Summary by CodeRabbit

  • New Features

    • Infinite-scroll pagination with cursor-based loading, per-tab pagination state, and a skeleton loader; paginated responses (default 20).
    • Advanced filters: name search, creator, favorites, and flexible creation-date ranges; filter changes trigger refetches.
  • Bug Fixes / Stability

    • Safer ordering with validated sort fields and defaults; more reliable multi-page fetch handling and clearer loading/error states.

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

Copilot AI review requested due to automatic review settings December 29, 2025 11:42
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 29, 2025

Note

Other AI code review bot(s) detected

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

📝 Walkthrough

Walkthrough

Adds backend page_filters and moves PageViewSet.list to paginated, ordered responses; introduces frontend cursor-based, per-tab pagination with infinite-scroll, filter-aware SWR fetching, updated service/store signatures and a list skeleton loader.

Changes

Cohort / File(s) Summary
Backend filtering & view
apps/api/plane/utils/page_filters.py, apps/api/plane/app/views/page/base.py
New page_filters.py builds Django ORM filter dicts (UUID validation, date ranges, created_by, favorites, search). PageViewSet.list applies type filters, page_filters, validated order_by and returns paginated responses.
Frontend service & types
apps/web/core/services/page/project-page.service.ts
fetchAll now accepts queries/config and returns TPagePaginatedResponse (adds TPaginationInfo import and exported TPagePaginatedResponse type).
Frontend store & pagination logic
apps/web/core/store/pages/project-page.store.ts
Adds pagination types/constants, per-tab filtered*PageIds, paginationInfo, paginationLoader, hasActiveFilters, accessors, and updates fetchPagesList signature to accept pageType and optional cursor. Implements cursor-aware requests, parses paginated responses, updates per-tab state, and changes default sortKey to created_at.
Frontend components & UI
apps/web/core/components/pages/list/loader.tsx, apps/web/core/components/pages/list/root.tsx, apps/web/core/components/pages/pages-list-main-content.tsx, apps/web/core/components/pages/pages-list-view.tsx
Adds PageListLoader skeleton. PagesListRoot adds intersection-observer infinite-scroll, guarded fetchNextPage, and renders pagination loader. PagesListMainContent switches to getFilteredPageIds() and shows mutation-loader early. PagesListView incorporates filters into SWR key and sets revalidation options.
Route usage update
apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/layout.tsx
Reads pageType from search params and passes it to fetchPagesList on bootstrap.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User
    participant UI as Frontend UI
    participant IO as IntersectionObserver
    participant Store as ProjectPageStore
    participant Service as ProjectPageService
    participant API as Backend API

    User->>UI: Open pages list (workspace, project, pageType)
    UI->>Store: fetchPagesList(workspace, project, pageType)
    Store->>Service: fetchAll(workspace, project, queries?, config?)
    Service->>API: GET /pages?filters&order_by&cursor
    API->>API: page_filters(query_params) -> queryset
    API->>API: paginate(queryset)
    API-->>Service: TPagePaginatedResponse { results, paginationInfo }
    Service-->>Store: paginated response
    Store->>Store: update paginationInfo[pageType], append filteredPageIds
    Store-->>UI: updated items, hasNextPage
    UI->>IO: render intersection trigger

    Note over User,IO: user scrolls
    IO->>UI: intersection triggered
    UI->>Store: fetchPagesList(..., cursor)
    Store->>Service: fetchAll(..., { cursor })
    Service->>API: GET /pages?cursor&filters
    API-->>Service: next page response
    Store->>Store: append results, update paginationInfo
    Store-->>UI: new items
    UI->>UI: render appended items / loader
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

🌐frontend, 🌟improvement

Suggested reviewers

  • prateekshourya29
  • aaryan610

Poem

🐇 I hopped through queries, tails of date and id,
I parsed each filter, skipped the ones I hid,
Cursor in paw — a gentle nibble for more,
I fetched and appended, scrolled down the floor,
Loader aglow as new pages pour.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding pagination, filters, and ordering for project pages, which aligns with all file modifications across backend and frontend.
Description check ✅ Passed The description covers all required template sections: detailed description of changes, type of change (Feature), and includes media reference. All key implementation aspects are documented.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9b3f0e and 4041be6.

📒 Files selected for processing (4)
  • apps/api/plane/app/views/page/base.py
  • apps/api/plane/utils/page_filters.py
  • apps/web/core/components/pages/list/root.tsx
  • apps/web/core/store/pages/project-page.store.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,mts,cts}

📄 CodeRabbit inference engine (.github/instructions/typescript.instructions.md)

**/*.{ts,tsx,mts,cts}: Use const type parameters for more precise literal inference in TypeScript 5.0+
Use the satisfies operator to validate types without widening them
Leverage inferred type predicates to reduce the need for explicit is return types in filter/check functions
Use NoInfer<T> utility to block inference for specific type arguments when they should be determined by other arguments
Utilize narrowing in switch(true) blocks for control flow analysis (TypeScript 5.3+)
Rely on narrowing from direct boolean comparisons for type guards
Trust preserved narrowing in closures when variables aren't modified after the check (TypeScript 5.4+)
Use constant indices to narrow object/array properties (TypeScript 5.5+)
Use standard ECMAScript decorators (Stage 3) instead of legacy experimentalDecorators
Use using declarations for explicit resource management with Disposable pattern instead of manual cleanup (TypeScript 5.2+)
Use with { type: "json" } for import attributes; avoid deprecated assert syntax (TypeScript 5.3/5.8+)
Use import type explicitly when importing types to ensure they are erased during compilation, respecting verbatimModuleSyntax flag
Use .ts, .mts, .cts extensions in import type statements (TypeScript 5.2+)
Use import type { Type } from "mod" with { "resolution-mode": "import" } for specific module resolution contexts (TypeScript 5.3+)
Use new iterator methods (map, filter, etc.) if targeting modern environments (TypeScript 5.6+)
Utilize new Set methods like union, intersection, etc., when available (TypeScript 5.5+)
Use Object.groupBy / Map.groupBy standard methods for grouping instead of external libraries (TypeScript 5.4+)
Use Promise.withResolvers() for creating promises with exposed resolve/reject functions (TypeScript 5.7+)
Use copying array methods (toSorted, toSpliced, with) for immutable array operations (TypeScript 5.2+)
Avoid accessing instance fields via super in classes (TypeScript 5....

Files:

  • apps/web/core/components/pages/list/root.tsx
  • apps/web/core/store/pages/project-page.store.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Enable TypeScript strict mode and ensure all files are fully typed

Files:

  • apps/web/core/components/pages/list/root.tsx
  • apps/web/core/store/pages/project-page.store.ts
**/*.{js,jsx,ts,tsx,json,css}

📄 CodeRabbit inference engine (AGENTS.md)

Use Prettier with Tailwind plugin for code formatting, run pnpm fix:format

Files:

  • apps/web/core/components/pages/list/root.tsx
  • apps/web/core/store/pages/project-page.store.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Use ESLint with shared config across packages, adhering to max warnings limits per package
Use camelCase for variable and function names, PascalCase for components and types
Use try-catch with proper error types and log errors appropriately

Files:

  • apps/web/core/components/pages/list/root.tsx
  • apps/web/core/store/pages/project-page.store.ts
🧠 Learnings (2)
📚 Learning: 2025-10-21T17:22:05.204Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7989
File: apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/[pageId]/page.tsx:45-46
Timestamp: 2025-10-21T17:22:05.204Z
Learning: In the makeplane/plane repository, the refactor from useParams() to params prop is specifically scoped to page.tsx and layout.tsx files in apps/web/app (Next.js App Router pattern). Other components (hooks, regular client components, utilities) should continue using the useParams() hook as that is the correct pattern for non-route components.

Applied to files:

  • apps/web/core/components/pages/list/root.tsx
📚 Learning: 2025-12-23T14:18:32.899Z
Learnt from: dheeru0198
Repo: makeplane/plane PR: 8339
File: apps/api/plane/db/models/api.py:35-35
Timestamp: 2025-12-23T14:18:32.899Z
Learning: Django REST Framework rate limit strings are flexible: only the first character of the time unit matters. Acceptable formats include: "60/s", "60/sec", "60/second" (all equivalent), "60/m", "60/min", "60/minute" (all equivalent), "60/h", "60/hr", "60/hour" (all equivalent), and "60/d", "60/day" (all equivalent). Abbreviations like "min" are valid and do not need to be changed to "minute". Apply this guidance to any Python files in the project that configure DRF throttling rules.

Applied to files:

  • apps/api/plane/utils/page_filters.py
  • apps/api/plane/app/views/page/base.py
🧬 Code graph analysis (1)
apps/api/plane/app/views/page/base.py (2)
apps/api/plane/utils/page_filters.py (1)
  • page_filters (140-155)
apps/api/plane/app/serializers/page.py (1)
  • PageSerializer (21-122)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Build packages
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (31)
apps/web/core/store/pages/project-page.store.ts (12)

49-91: LGTM!

The interface updates correctly capture the new pagination infrastructure with per-tab filtered arrays, pagination state, and accessor methods. The fetchPagesList signature properly adds pageType and optional cursor parameters.


122-148: LGTM!

MobX observables and computed properties are correctly configured for the new pagination infrastructure.


182-207: LGTM!

The hasActiveFilters computed property correctly detects active filters by checking search query and filter values with appropriate handling for arrays, booleans, and other types.


239-254: LGTM!

The refactor correctly returns pre-computed filtered page IDs from per-tab arrays instead of filtering on-demand, improving performance with pagination.


280-290: LGTM!

The pagination accessor methods correctly use computedFn and provide appropriate fallback to DEFAULT_PAGINATION_INFO.


304-319: LGTM!

The pagination state validation correctly prevents redundant fetches when there's no next page or a fetch is already in progress.


321-357: LGTM!

Query parameter building is comprehensive and correctly handles search, sorting, cursor, and filter values. The order_by format conversion to Django style (-field for desc) is appropriate.


359-385: LGTM!

Loader management is correct: uses init-loader or mutation-loader for first page based on existing data, pagination loader for subsequent pages, and resets pagination info and filtered arrays when fetching the first page.


397-407: Backward compatibility handling for array responses is correct.

The logic appropriately handles both array responses (legacy) and paginated object responses. For arrays, setting totalResults to response.length and hasNextPage to false is correct since array responses represent complete data.

Past review comment suggested this may not be sufficient if backend returns arrays with more data available, but given the context (migration to pagination), array responses are expected to be complete.


429-445: Duplicate prevention is correctly implemented.

Using Array.from(new Set([...existing, ...new])) appropriately prevents duplicate page IDs when appending paginated results. Past review comment flagged potential duplicates, but this implementation handles them correctly.


409-456: LGTM!

The store update logic correctly:

  • Extracts page IDs from response
  • Updates or creates page instances
  • Appends to per-tab filtered arrays with deduplication
  • Updates pagination info
  • Clears appropriate loaders

459-479: LGTM!

Error handling appropriately distinguishes between first-page and subsequent-page failures with distinct error messages and loader state cleanup.

apps/web/core/components/pages/list/root.tsx (6)

1-15: LGTM!

Imports are appropriate for the infinite scroll implementation. Using useParams hook is correct for this client component per the repository's patterns.

Based on learnings, useParams() is the correct pattern for non-route components.


25-42: LGTM!

Hook usage and pagination state derivation are correct. The intersection observer setup with refs and state is appropriate for infinite scroll.


44-56: LGTM!

The fetchNextPage callback correctly guards against invalid states and uses appropriate dependencies. The void keyword for the promise is correct for fire-and-forget async calls in callbacks.


58-64: LGTM!

Intersection observer setup is correct. Passing null for the element when isFetchingNextPage is true prevents multiple simultaneous fetch triggers.


74-74: Intersection observer target structure is acceptable.

Past review suggested separating the ref div and loader as siblings, but the current structure (loader as child of ref div) works correctly for infinite scroll. The ref div is rendered whenever hasNextPage is true, maintaining the intersection observer target regardless of loader visibility.

The suggested sibling structure would be slightly cleaner but not functionally different.


68-77: LGTM!

The rendering structure correctly wraps the list in a container with ref for intersection observation and conditionally renders the trigger element and loader based on pagination state.

apps/api/plane/app/views/page/base.py (5)

45-45: LGTM!

Import of page_filters is necessary for the new filtering logic in the list method.


302-309: LGTM!

Page type filtering correctly handles public (access=0, not archived), private (access=1, not archived), and archived (archived_at not null) cases.


311-313: LGTM!

Integration of page_filters utility correctly applies additional query parameter filters to the queryset.


315-320: LGTM!

The order_by parameter validation with ALLOWED_ORDER_FIELDS allowlist correctly prevents invalid field errors and potential abuse. This addresses the past review concern (commit 520a48c).


322-328: LGTM!

The pagination implementation correctly uses self.paginate() with appropriate parameters: queryset, serialization callback, and per-page limits (default 20, max 100).

apps/api/plane/utils/page_filters.py (8)

1-11: LGTM!

Imports are appropriate and the regex pattern correctly matches duration formats like "2_weeks" or "3_months".


15-24: LGTM!

The UUID validation function correctly filters out invalid UUID strings by catching ValueError exceptions. Silently skipping invalid UUIDs is appropriate for filter parameters.


28-51: LGTM!

The relative date filter logic correctly handles various combinations of duration (weeks/months), direction (after/before), and offset (fromnow/past). Note that months are approximated as 30 days, which is acceptable for this use case.


58-80: LGTM!

The date filter parsing logic correctly handles both relative (pattern-matched durations) and absolute date queries with appropriate lookup operators (__gte, __lte, __contains).


97-113: LGTM with caveat.

The function correctly delegates to date_filter for parsing date queries. However, it has the AttributeError issue flagged above.


116-125: LGTM!

The favorites filter correctly handles both GET (string parameter converted to lowercase) and non-GET (boolean parameter) cases.


128-137: LGTM!

The search filter correctly uses name__icontains for case-insensitive partial matching, appropriate for search functionality.


140-155: LGTM!

The orchestrator function uses a clean mapping pattern to delegate to specific filter functions based on query parameter presence. This is maintainable and extensible.


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

❤️ Share

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

@makeplane
Copy link

makeplane bot commented Dec 29, 2025

Linked to Plane Work Item(s)

This comment was auto-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: 4

Caution

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

⚠️ Outside diff range comments (2)
apps/web/core/store/pages/project-page.store.ts (2)

560-580: Deleted pages not removed from filtered ID arrays.

Similar to createPage, removePage only removes from this.data but doesn't clean up filteredPublicPageIds, filteredPrivatePageIds, or filteredArchivedPageIds. This could cause rendering issues when the component tries to render a deleted page.

🔎 Proposed fix
       await this.service.remove(workspaceSlug, projectId, pageId);
       runInAction(() => {
         unset(this.data, [pageId]);
+        // Remove from filtered arrays
+        this.filteredPublicPageIds = this.filteredPublicPageIds.filter((id) => id !== pageId);
+        this.filteredPrivatePageIds = this.filteredPrivatePageIds.filter((id) => id !== pageId);
+        this.filteredArchivedPageIds = this.filteredArchivedPageIds.filter((id) => id !== pageId);
         if (this.rootStore.favorite.entityMap[pageId]) this.rootStore.favorite.removeFavoriteFromStore(pageId);
       });

527-554: Newly created pages do not appear in filtered lists until refetch.

The createPage method adds the page to this.data but doesn't update filteredPublicPageIds or filteredPrivatePageIds. These filtered arrays are populated only by fetchPagesList, so users won't see newly created pages in the filtered list until they refresh or filters are changed.

Consider either adding the new page ID to the appropriate filtered array based on its visibility type, or triggering a refetch after creation.

🧹 Nitpick comments (3)
apps/api/plane/utils/page_filters.py (1)

28-51: Month calculation uses 30-day approximation.

The month calculation uses timedelta(days=duration * 30) which is an approximation. This may lead to slight inaccuracies for date filters (e.g., 3 months = 90 days instead of actual calendar months). This is acceptable for most use cases but worth noting for precision-sensitive scenarios.

apps/web/core/components/pages/pages-list-view.tsx (1)

24-36: SWR key construction correctly triggers refetch on filter changes.

The key includes all relevant filter state to ensure data refreshes when filters change. However, revalidateOnFocus: true combined with complex filter state may cause frequent refetches when users switch tabs/windows.

Consider setting revalidateOnFocus: false if users report excessive loading states when returning to the page.

apps/web/core/components/pages/list/root.tsx (1)

59-65: Intersection observer rootMargin may trigger loading too early.

The rootMargin 100% 0% 100% 0% expands the root bounds by 100% of its height both top and bottom. This means the intersection callback could fire when the element is still a full viewport away. Consider using a smaller pixel-based margin like 100px 0px 100px 0px for more predictable loading behavior.

🔎 Proposed fix
   useIntersectionObserver(
     containerRef,
     isFetchingNextPage ? null : intersectionElement,
     fetchNextPage,
-    `100% 0% 100% 0%`
+    `100px 0% 100px 0%`
   );
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 20492ff and 5093e03.

📒 Files selected for processing (8)
  • apps/api/plane/app/views/page/base.py
  • apps/api/plane/utils/page_filters.py
  • apps/web/core/components/pages/list/loader.tsx
  • apps/web/core/components/pages/list/root.tsx
  • apps/web/core/components/pages/pages-list-main-content.tsx
  • apps/web/core/components/pages/pages-list-view.tsx
  • apps/web/core/services/page/project-page.service.ts
  • apps/web/core/store/pages/project-page.store.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,mts,cts}

📄 CodeRabbit inference engine (.github/instructions/typescript.instructions.md)

**/*.{ts,tsx,mts,cts}: Use const type parameters for more precise literal inference in TypeScript 5.0+
Use the satisfies operator to validate types without widening them
Leverage inferred type predicates to reduce the need for explicit is return types in filter/check functions
Use NoInfer<T> utility to block inference for specific type arguments when they should be determined by other arguments
Utilize narrowing in switch(true) blocks for control flow analysis (TypeScript 5.3+)
Rely on narrowing from direct boolean comparisons for type guards
Trust preserved narrowing in closures when variables aren't modified after the check (TypeScript 5.4+)
Use constant indices to narrow object/array properties (TypeScript 5.5+)
Use standard ECMAScript decorators (Stage 3) instead of legacy experimentalDecorators
Use using declarations for explicit resource management with Disposable pattern instead of manual cleanup (TypeScript 5.2+)
Use with { type: "json" } for import attributes; avoid deprecated assert syntax (TypeScript 5.3/5.8+)
Use import type explicitly when importing types to ensure they are erased during compilation, respecting verbatimModuleSyntax flag
Use .ts, .mts, .cts extensions in import type statements (TypeScript 5.2+)
Use import type { Type } from "mod" with { "resolution-mode": "import" } for specific module resolution contexts (TypeScript 5.3+)
Use new iterator methods (map, filter, etc.) if targeting modern environments (TypeScript 5.6+)
Utilize new Set methods like union, intersection, etc., when available (TypeScript 5.5+)
Use Object.groupBy / Map.groupBy standard methods for grouping instead of external libraries (TypeScript 5.4+)
Use Promise.withResolvers() for creating promises with exposed resolve/reject functions (TypeScript 5.7+)
Use copying array methods (toSorted, toSpliced, with) for immutable array operations (TypeScript 5.2+)
Avoid accessing instance fields via super in classes (TypeScript 5....

Files:

  • apps/web/core/components/pages/pages-list-main-content.tsx
  • apps/web/core/components/pages/list/loader.tsx
  • apps/web/core/components/pages/list/root.tsx
  • apps/web/core/components/pages/pages-list-view.tsx
  • apps/web/core/services/page/project-page.service.ts
  • apps/web/core/store/pages/project-page.store.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Enable TypeScript strict mode and ensure all files are fully typed

Files:

  • apps/web/core/components/pages/pages-list-main-content.tsx
  • apps/web/core/components/pages/list/loader.tsx
  • apps/web/core/components/pages/list/root.tsx
  • apps/web/core/components/pages/pages-list-view.tsx
  • apps/web/core/services/page/project-page.service.ts
  • apps/web/core/store/pages/project-page.store.ts
**/*.{js,jsx,ts,tsx,json,css}

📄 CodeRabbit inference engine (AGENTS.md)

Use Prettier with Tailwind plugin for code formatting, run pnpm fix:format

Files:

  • apps/web/core/components/pages/pages-list-main-content.tsx
  • apps/web/core/components/pages/list/loader.tsx
  • apps/web/core/components/pages/list/root.tsx
  • apps/web/core/components/pages/pages-list-view.tsx
  • apps/web/core/services/page/project-page.service.ts
  • apps/web/core/store/pages/project-page.store.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Use ESLint with shared config across packages, adhering to max warnings limits per package
Use camelCase for variable and function names, PascalCase for components and types
Use try-catch with proper error types and log errors appropriately

Files:

  • apps/web/core/components/pages/pages-list-main-content.tsx
  • apps/web/core/components/pages/list/loader.tsx
  • apps/web/core/components/pages/list/root.tsx
  • apps/web/core/components/pages/pages-list-view.tsx
  • apps/web/core/services/page/project-page.service.ts
  • apps/web/core/store/pages/project-page.store.ts
🧠 Learnings (2)
📚 Learning: 2025-10-21T17:22:05.204Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7989
File: apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/[pageId]/page.tsx:45-46
Timestamp: 2025-10-21T17:22:05.204Z
Learning: In the makeplane/plane repository, the refactor from useParams() to params prop is specifically scoped to page.tsx and layout.tsx files in apps/web/app (Next.js App Router pattern). Other components (hooks, regular client components, utilities) should continue using the useParams() hook as that is the correct pattern for non-route components.

Applied to files:

  • apps/web/core/components/pages/list/root.tsx
📚 Learning: 2025-12-23T14:18:32.899Z
Learnt from: dheeru0198
Repo: makeplane/plane PR: 8339
File: apps/api/plane/db/models/api.py:35-35
Timestamp: 2025-12-23T14:18:32.899Z
Learning: Django REST Framework rate limit strings are flexible: only the first character of the time unit matters. Acceptable formats include: "60/s", "60/sec", "60/second" (all equivalent), "60/m", "60/min", "60/minute" (all equivalent), "60/h", "60/hr", "60/hour" (all equivalent), and "60/d", "60/day" (all equivalent). Abbreviations like "min" are valid and do not need to be changed to "minute". Apply this guidance to any Python files in the project that configure DRF throttling rules.

Applied to files:

  • apps/api/plane/utils/page_filters.py
  • apps/api/plane/app/views/page/base.py
🧬 Code graph analysis (5)
apps/web/core/components/pages/pages-list-main-content.tsx (1)
apps/web/core/components/pages/loaders/page-loader.tsx (1)
  • PageLoader (4-31)
apps/web/core/components/pages/list/loader.tsx (1)
apps/web/core/components/pages/loaders/page-loader.tsx (1)
  • PageLoader (4-31)
apps/web/core/components/pages/list/root.tsx (4)
apps/space/app/compat/next/navigation.ts (1)
  • useParams (34-36)
apps/space/core/store/publish/publish.store.ts (1)
  • workspaceSlug (93-95)
apps/web/core/components/pages/list/block.tsx (1)
  • PageListBlock (21-54)
apps/web/core/components/pages/list/loader.tsx (1)
  • PageListLoader (3-15)
apps/web/core/components/pages/pages-list-view.tsx (1)
apps/space/core/store/publish/publish.store.ts (1)
  • workspaceSlug (93-95)
apps/web/core/store/pages/project-page.store.ts (1)
packages/types/src/page/core.ts (3)
  • TPageNavigationTabs (28-28)
  • TPageFilters (41-46)
  • TPage (5-25)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Lint API
  • GitHub Check: Build packages
  • GitHub Check: CodeQL analysis (javascript-typescript)
  • GitHub Check: Agent
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (7)
apps/web/core/services/page/project-page.service.ts (1)

23-40: LGTM on the paginated fetch implementation.

The updated fetchAll method correctly supports query parameters for pagination and filtering. The type definition TPagePaginatedResponse properly combines pagination info with results.

One minor note: the config parameter has an implicit any type. Consider adding an explicit type annotation for stricter typing, though this follows the existing pattern in the codebase.

apps/api/plane/app/views/page/base.py (1)

286-314: List method pagination implementation looks well-structured.

The filtering, ordering, and pagination flow is logically organized. The paginate helper with default_per_page=20 and max_per_page=100 provides sensible defaults and limits.

apps/web/core/components/pages/pages-list-main-content.tsx (1)

159-162: Good addition for handling mutation loading state.

This correctly shows a loading indicator when filters are being applied and no results are available yet, preventing a flash of the empty state before results arrive.

apps/web/core/components/pages/list/root.tsx (1)

46-57: Well-implemented pagination fetch with proper guard conditions.

The fetchNextPage callback correctly guards against missing params, absence of next page, and concurrent fetches. The dependency array is complete.

apps/web/core/store/pages/project-page.store.ts (3)

26-40: Well-designed pagination state types.

The TPagePaginationInfo type and DEFAULT_PAGINATION_INFO constant provide a clean structure for managing cursor-based pagination state. The per-page count of 20 aligns with the backend's default_per_page.


182-207: Comprehensive filter detection logic.

The hasActiveFilters computed property thoroughly checks search query and various filter types (arrays, booleans, etc.). This enables proper UI state management for filter indicators.


299-384: Robust pagination implementation with proper state guards.

The fetchPagesList method correctly:

  • Guards against concurrent fetches and invalid pagination states
  • Builds query parameters from filter state
  • Differentiates between first-page and subsequent-page fetches
  • Resets pagination state on first page load

The backward-compatible response parsing (lines 396-406) is a good defensive pattern.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements backend-driven pagination, filtering, and ordering for project pages, moving the logic from client-side to server-side for improved scalability and consistency. The implementation introduces cursor-based pagination with infinite scroll support and centralizes filtering logic in reusable backend utilities.

Key Changes:

  • Implemented cursor-based pagination with backend filtering and ordering for page lists
  • Added comprehensive page filter utilities (search, favorites, created_by, created_at) on the backend
  • Updated frontend store and components to support paginated fetching with infinite scroll
  • Changed default sort order from "updated_at" to "created_at"

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
apps/web/core/store/pages/project-page.store.ts Adds pagination state management, filtered page ID arrays per tab, and refactors fetchPagesList to support cursor-based pagination with backend filtering
apps/web/core/services/page/project-page.service.ts Updates fetchAll method signature to accept query parameters and return paginated response type
apps/web/core/components/pages/pages-list-view.tsx Modifies SWR key to include filter/sort parameters for proper cache invalidation
apps/web/core/components/pages/pages-list-main-content.tsx Adds additional loader check for mutation-loader state with empty results
apps/web/core/components/pages/list/root.tsx Implements infinite scroll with intersection observer for loading paginated results
apps/web/core/components/pages/list/loader.tsx New loader component for pagination loading states
apps/api/plane/utils/page_filters.py New utility module providing reusable filter functions for pages (search, favorites, created_by, created_at)
apps/api/plane/app/views/page/base.py Integrates page filters and pagination in the list endpoint, removes duplicate order_by calls from queryset

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

else:
page_filter[f"{date_term}__lte"] = date_query[0]
else:
page_filter[f"{date_term}__contains"] = date_query[0]
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

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

Missing return statement in the date filter logic. The function modifies page_filter in place but doesn't explicitly return it, though Python returns None implicitly. For consistency with other filter functions (filter_created_by, filter_created_at, filter_favorites, filter_search) which all return page_filter, this function should also return page_filter.

Suggested change
page_filter[f"{date_term}__contains"] = date_query[0]
page_filter[f"{date_term}__contains"] = date_query[0]
return page_filter
Copilot uses AI. Check for mistakes.
Comment on lines 401 to 405
paginationData = {
nextCursor: response.next_cursor || null,
hasNextPage: response.next_page_results ?? false,
totalResults: response.total_results ?? 0,
};
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

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

Type mismatch with TPaginationInfo. According to the TPaginationInfo type definition, next_cursor is a non-nullable string, but the code treats it as potentially falsy (using || null). The local TPagePaginationInfo type defines nextCursor as "string | null" which conflicts with the backend type. Either the backend type definition needs to be updated to allow null, or the frontend should handle empty strings differently.

Copilot uses AI. Check for mistakes.
import { useParams } from "next/navigation";
// types
import type { TPageNavigationTabs } from "@plane/types";
import { Loader } from "@plane/ui";
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

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

Unused import Loader.

Suggested change
import { Loader } from "@plane/ui";
Copilot uses AI. Check for mistakes.
@im-vipin im-vipin changed the title [WIKI-855] feat: backend-driven pagination, filters, and ordering for project pages Dec 29, 2025
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 (3)
apps/web/core/components/pages/list/root.tsx (1)

6-6: Unused import: Loader

The Loader component is imported but never used in this file. PageListLoader is used instead.

Suggested fix
-import { Loader } from "@plane/ui";
apps/web/core/components/pages/pages-list-main-content.tsx (1)

159-161: Mutation loader guard is reasonable, but verify error handling.

This condition shows a loader during filter/sort mutations when no filtered results exist yet. The store implementation clears loader on error (line 392 in store), so this shouldn't show a loader indefinitely on error. However, consider adding defensive error state handling if the store gains error states that could coexist with mutation-loader.

apps/web/core/store/pages/project-page.store.ts (1)

96-96: Default sort key changed from updated_at to created_at.

This changes the default ordering for all users. Existing users accustomed to seeing recently updated pages first will now see pages ordered by creation date. Consider whether this is intentional or if backward compatibility should be preserved.

🧹 Nitpick comments (2)
apps/web/core/store/pages/project-page.store.ts (2)

371-376: Consider deduplicating page IDs when appending paginated results.

If the backend returns overlapping results (e.g., due to data changes between fetches or cursor edge cases), duplicate page IDs could appear in filteredPageIds, causing duplicate React keys and redundant renders.

Suggested fix using Set for deduplication
         // Update filtered page IDs
-        this.filteredPageIds = isFirstPage ? responsePageIds : [...this.filteredPageIds, ...responsePageIds];
+        this.filteredPageIds = isFirstPage
+          ? responsePageIds
+          : Array.from(new Set([...this.filteredPageIds, ...responsePageIds]));

298-303: Redundant null check for nextCursor.

At line 266, you already verified this.paginationInfo?.nextCursor exists before reaching this point (since isFirstPage is false). The inner if (nextCursor) check at line 300 is redundant.

Simplified version
       // Add pagination cursor
       if (!isFirstPage) {
-        const nextCursor = this.paginationInfo.nextCursor;
-        if (nextCursor) {
-          queries.cursor = nextCursor;
-        }
+        queries.cursor = this.paginationInfo.nextCursor!;
       }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5093e03 and 35e6d9c.

📒 Files selected for processing (3)
  • apps/web/core/components/pages/list/root.tsx
  • apps/web/core/components/pages/pages-list-main-content.tsx
  • apps/web/core/store/pages/project-page.store.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,mts,cts}

📄 CodeRabbit inference engine (.github/instructions/typescript.instructions.md)

**/*.{ts,tsx,mts,cts}: Use const type parameters for more precise literal inference in TypeScript 5.0+
Use the satisfies operator to validate types without widening them
Leverage inferred type predicates to reduce the need for explicit is return types in filter/check functions
Use NoInfer<T> utility to block inference for specific type arguments when they should be determined by other arguments
Utilize narrowing in switch(true) blocks for control flow analysis (TypeScript 5.3+)
Rely on narrowing from direct boolean comparisons for type guards
Trust preserved narrowing in closures when variables aren't modified after the check (TypeScript 5.4+)
Use constant indices to narrow object/array properties (TypeScript 5.5+)
Use standard ECMAScript decorators (Stage 3) instead of legacy experimentalDecorators
Use using declarations for explicit resource management with Disposable pattern instead of manual cleanup (TypeScript 5.2+)
Use with { type: "json" } for import attributes; avoid deprecated assert syntax (TypeScript 5.3/5.8+)
Use import type explicitly when importing types to ensure they are erased during compilation, respecting verbatimModuleSyntax flag
Use .ts, .mts, .cts extensions in import type statements (TypeScript 5.2+)
Use import type { Type } from "mod" with { "resolution-mode": "import" } for specific module resolution contexts (TypeScript 5.3+)
Use new iterator methods (map, filter, etc.) if targeting modern environments (TypeScript 5.6+)
Utilize new Set methods like union, intersection, etc., when available (TypeScript 5.5+)
Use Object.groupBy / Map.groupBy standard methods for grouping instead of external libraries (TypeScript 5.4+)
Use Promise.withResolvers() for creating promises with exposed resolve/reject functions (TypeScript 5.7+)
Use copying array methods (toSorted, toSpliced, with) for immutable array operations (TypeScript 5.2+)
Avoid accessing instance fields via super in classes (TypeScript 5....

Files:

  • apps/web/core/store/pages/project-page.store.ts
  • apps/web/core/components/pages/pages-list-main-content.tsx
  • apps/web/core/components/pages/list/root.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Enable TypeScript strict mode and ensure all files are fully typed

Files:

  • apps/web/core/store/pages/project-page.store.ts
  • apps/web/core/components/pages/pages-list-main-content.tsx
  • apps/web/core/components/pages/list/root.tsx
**/*.{js,jsx,ts,tsx,json,css}

📄 CodeRabbit inference engine (AGENTS.md)

Use Prettier with Tailwind plugin for code formatting, run pnpm fix:format

Files:

  • apps/web/core/store/pages/project-page.store.ts
  • apps/web/core/components/pages/pages-list-main-content.tsx
  • apps/web/core/components/pages/list/root.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Use ESLint with shared config across packages, adhering to max warnings limits per package
Use camelCase for variable and function names, PascalCase for components and types
Use try-catch with proper error types and log errors appropriately

Files:

  • apps/web/core/store/pages/project-page.store.ts
  • apps/web/core/components/pages/pages-list-main-content.tsx
  • apps/web/core/components/pages/list/root.tsx
🧠 Learnings (2)
📚 Learning: 2025-06-18T09:46:08.566Z
Learnt from: prateekshourya29
Repo: makeplane/plane PR: 7188
File: web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(list)/header.tsx:40-45
Timestamp: 2025-06-18T09:46:08.566Z
Learning: When reviewing breadcrumb components that accept projectId or similar props, check if empty strings are being passed during loading states, which can result in invalid URLs. The preferred approach is to handle these loading states internally within the component rather than requiring each consumer to manage the loading logic.

Applied to files:

  • apps/web/core/components/pages/pages-list-main-content.tsx
📚 Learning: 2025-10-21T17:22:05.204Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7989
File: apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/[pageId]/page.tsx:45-46
Timestamp: 2025-10-21T17:22:05.204Z
Learning: In the makeplane/plane repository, the refactor from useParams() to params prop is specifically scoped to page.tsx and layout.tsx files in apps/web/app (Next.js App Router pattern). Other components (hooks, regular client components, utilities) should continue using the useParams() hook as that is the correct pattern for non-route components.

Applied to files:

  • apps/web/core/components/pages/list/root.tsx
🧬 Code graph analysis (3)
apps/web/core/store/pages/project-page.store.ts (1)
packages/types/src/page/core.ts (3)
  • TPageNavigationTabs (28-28)
  • TPageFilters (41-46)
  • TPage (5-25)
apps/web/core/components/pages/pages-list-main-content.tsx (1)
apps/web/core/components/pages/loaders/page-loader.tsx (1)
  • PageLoader (4-31)
apps/web/core/components/pages/list/root.tsx (4)
apps/space/app/compat/next/navigation.ts (1)
  • useParams (34-36)
apps/space/core/store/publish/publish.store.ts (1)
  • workspaceSlug (93-95)
apps/web/core/components/pages/list/block.tsx (1)
  • PageListBlock (21-54)
apps/web/core/components/pages/list/loader.tsx (1)
  • PageListLoader (3-15)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: check:types
  • GitHub Check: check:lint
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (6)
apps/web/core/components/pages/list/root.tsx (1)

67-74: Intersection observer pattern looks correct.

The implementation correctly renders the intersection target div whenever hasNextPage is true, ensuring the observer can detect when the user scrolls near the bottom. The PageListLoader is conditionally shown only during fetch. This addresses the infinite scroll trigger concern from the past review since the div with ref is always present when there are more pages.

apps/web/core/components/pages/pages-list-main-content.tsx (1)

36-46: Store hook usage updated correctly.

The destructured values align with the new store interface (isAnyPageAvailable, getCurrentProjectPageIdsByTab, getFilteredPageIds, loader), and getFilteredPageIds() is correctly called without arguments per the new signature.

apps/web/core/store/pages/project-page.store.ts (4)

165-190: hasActiveFilters computed getter is well-implemented.

The logic correctly identifies active filters by checking search query presence and iterating over filter values with appropriate type handling for arrays, booleans, and other values.


261-274: Guard clauses for pagination state are thorough.

The early returns prevent redundant fetches when there's no next page, no cursor, or an ongoing pagination request. This is good defensive programming for infinite scroll scenarios.


344-354: Backward compatibility for non-paginated responses is handled.

The code gracefully handles both array responses (legacy) and paginated response objects, ensuring smooth migration. The next_cursor || null and next_page_results ?? false fallbacks are appropriate for type safety.


218-223: getFilteredPageIds implementation is minimal but effective.

The computed function simply returns the stored filteredPageIds, which is populated by fetchPagesList. This aligns with the backend-driven filtering approach where the store trusts the API response ordering.

@im-vipin im-vipin force-pushed the feat-pagination_filter_orderby_project_pages branch from 35e6d9c to 5093e03 Compare December 29, 2025 12:39
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)
apps/api/plane/app/views/page/base.py (1)

302-309: Validate page_type parameter against allowed values.

The page_type parameter is taken from the request without validation. While a default of "public" is provided, invalid values (e.g., "foo") will fall through to the "public" case, potentially causing confusion or unexpected behavior.

🔎 Proposed fix
+        ALLOWED_PAGE_TYPES = ["public", "private", "archived"]
+        
         # Filter by page type (public, private, archived)
         page_type = request.GET.get("type", "public")
+        if page_type not in ALLOWED_PAGE_TYPES:
+            page_type = "public"
+        
         if page_type == "private":
             queryset = queryset.filter(access=1, archived_at__isnull=True)
         elif page_type == "archived":
             queryset = queryset.filter(archived_at__isnull=False)
         elif page_type == "public":
             queryset = queryset.filter(access=0, archived_at__isnull=True)
apps/web/core/store/pages/project-page.store.ts (1)

333-337: Consider edge cases in sorting parameter handling.

The code checks if (this.filters.sortKey && this.filters.sortBy) but doesn't handle cases where only one is set. While the filters are initialized with both values, a partial update could leave them inconsistent.

🔎 Suggested enhancement
       // Add sorting parameters
       // Convert sortKey and sortBy to Django order_by format
-      if (this.filters.sortKey && this.filters.sortBy) {
+      const sortKey = this.filters.sortKey || "created_at";
+      const sortBy = this.filters.sortBy || "desc";
+      if (sortKey && sortBy) {
-        const orderByField = this.filters.sortBy === "desc" ? `-${this.filters.sortKey}` : this.filters.sortKey;
+        const orderByField = sortBy === "desc" ? `-${sortKey}` : sortKey;
         queries.order_by = orderByField;
       }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 35e6d9c and c2cf2ab.

📒 Files selected for processing (3)
  • apps/api/plane/app/views/page/base.py
  • apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/layout.tsx
  • apps/web/core/store/pages/project-page.store.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,mts,cts}

📄 CodeRabbit inference engine (.github/instructions/typescript.instructions.md)

**/*.{ts,tsx,mts,cts}: Use const type parameters for more precise literal inference in TypeScript 5.0+
Use the satisfies operator to validate types without widening them
Leverage inferred type predicates to reduce the need for explicit is return types in filter/check functions
Use NoInfer<T> utility to block inference for specific type arguments when they should be determined by other arguments
Utilize narrowing in switch(true) blocks for control flow analysis (TypeScript 5.3+)
Rely on narrowing from direct boolean comparisons for type guards
Trust preserved narrowing in closures when variables aren't modified after the check (TypeScript 5.4+)
Use constant indices to narrow object/array properties (TypeScript 5.5+)
Use standard ECMAScript decorators (Stage 3) instead of legacy experimentalDecorators
Use using declarations for explicit resource management with Disposable pattern instead of manual cleanup (TypeScript 5.2+)
Use with { type: "json" } for import attributes; avoid deprecated assert syntax (TypeScript 5.3/5.8+)
Use import type explicitly when importing types to ensure they are erased during compilation, respecting verbatimModuleSyntax flag
Use .ts, .mts, .cts extensions in import type statements (TypeScript 5.2+)
Use import type { Type } from "mod" with { "resolution-mode": "import" } for specific module resolution contexts (TypeScript 5.3+)
Use new iterator methods (map, filter, etc.) if targeting modern environments (TypeScript 5.6+)
Utilize new Set methods like union, intersection, etc., when available (TypeScript 5.5+)
Use Object.groupBy / Map.groupBy standard methods for grouping instead of external libraries (TypeScript 5.4+)
Use Promise.withResolvers() for creating promises with exposed resolve/reject functions (TypeScript 5.7+)
Use copying array methods (toSorted, toSpliced, with) for immutable array operations (TypeScript 5.2+)
Avoid accessing instance fields via super in classes (TypeScript 5....

Files:

  • apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/layout.tsx
  • apps/web/core/store/pages/project-page.store.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Enable TypeScript strict mode and ensure all files are fully typed

Files:

  • apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/layout.tsx
  • apps/web/core/store/pages/project-page.store.ts
**/*.{js,jsx,ts,tsx,json,css}

📄 CodeRabbit inference engine (AGENTS.md)

Use Prettier with Tailwind plugin for code formatting, run pnpm fix:format

Files:

  • apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/layout.tsx
  • apps/web/core/store/pages/project-page.store.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Use ESLint with shared config across packages, adhering to max warnings limits per package
Use camelCase for variable and function names, PascalCase for components and types
Use try-catch with proper error types and log errors appropriately

Files:

  • apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/layout.tsx
  • apps/web/core/store/pages/project-page.store.ts
🧠 Learnings (3)
📚 Learning: 2025-10-21T17:22:05.204Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7989
File: apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/[pageId]/page.tsx:45-46
Timestamp: 2025-10-21T17:22:05.204Z
Learning: In the makeplane/plane repository, the refactor from useParams() to params prop is specifically scoped to page.tsx and layout.tsx files in apps/web/app (Next.js App Router pattern). Other components (hooks, regular client components, utilities) should continue using the useParams() hook as that is the correct pattern for non-route components.

Applied to files:

  • apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/layout.tsx
📚 Learning: 2025-12-23T14:18:32.899Z
Learnt from: dheeru0198
Repo: makeplane/plane PR: 8339
File: apps/api/plane/db/models/api.py:35-35
Timestamp: 2025-12-23T14:18:32.899Z
Learning: Django REST Framework rate limit strings are flexible: only the first character of the time unit matters. Acceptable formats include: "60/s", "60/sec", "60/second" (all equivalent), "60/m", "60/min", "60/minute" (all equivalent), "60/h", "60/hr", "60/hour" (all equivalent), and "60/d", "60/day" (all equivalent). Abbreviations like "min" are valid and do not need to be changed to "minute". Apply this guidance to any Python files in the project that configure DRF throttling rules.

Applied to files:

  • apps/api/plane/app/views/page/base.py
📚 Learning: 2025-06-16T07:23:39.497Z
Learnt from: vamsikrishnamathala
Repo: makeplane/plane PR: 7214
File: web/core/store/issue/helpers/base-issues.store.ts:117-117
Timestamp: 2025-06-16T07:23:39.497Z
Learning: In the updateIssueDates method of BaseIssuesStore (web/core/store/issue/helpers/base-issues.store.ts), the projectId parameter is intentionally made optional to support override implementations in subclasses. The base implementation requires projectId and includes an early return check, but making it optional allows derived classes to override the method with different parameter requirements.

Applied to files:

  • apps/web/core/store/pages/project-page.store.ts
🧬 Code graph analysis (1)
apps/web/core/store/pages/project-page.store.ts (1)
packages/types/src/page/core.ts (3)
  • TPageNavigationTabs (28-28)
  • TPageFilters (41-46)
  • TPage (5-25)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: check:lint
  • GitHub Check: check:types
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
apps/web/core/store/pages/project-page.store.ts (1)

328-330: No action required—axios automatically handles URL encoding.

Query parameters passed via the params option in axios (as is done here) are automatically URL-encoded by the library. This is the correct and standard approach; explicit encoding is neither needed nor recommended, as it would result in double-encoding and actually create problems. The code pattern is consistent with other query parameter usage throughout the service layer.

Likely an incorrect or invalid review comment.

@im-vipin im-vipin force-pushed the feat-pagination_filter_orderby_project_pages branch from f5413a2 to 622290c Compare December 29, 2025 12:59
@im-vipin im-vipin force-pushed the feat-pagination_filter_orderby_project_pages branch from e9b3f0e to 520a48c Compare December 29, 2025 13:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment