Skip to content

Conversation

@anton-v-a
Copy link

@anton-v-a anton-v-a commented Jan 27, 2026

Pull Request: Calendar and Kanban Layouts for Workspace Views

Summary

This PR adds Calendar and Kanban layout support to workspace-level views in Plane. Previously, workspace views (All Issues, Assigned, Created, Subscribed) only supported the Spreadsheet layout. Users can now visualize their work across all projects in time-based (Calendar) and workflow-based (Kanban) formats.

Key Features

  • Calendar Layout: View issues grouped by target date with month/week navigation
  • Kanban Layout: View issues grouped by state group, priority, project, or labels
  • "No Date" Section: Collapsible section in Calendar showing issues without a target date
  • Drag-and-Drop: Move issues between dates (Calendar) or columns (Kanban)
  • Quick Add: Create issues directly from Calendar/Kanban with project selection
  • Cross-Project State Mapping: Intelligent state resolution when moving issues between state groups

Changes Overview

Frontend (apps/web)

New Components

File Description
calendar/roots/workspace-root.tsx Workspace Calendar root component with "No Date" section support
kanban/roots/workspace-root.tsx Workspace Kanban root component with state group mapping
quick-add/workspace-root.tsx Quick add component with project dropdown for workspace views

Modified Components

File Changes
calendar/calendar.tsx Added "No Date" collapsible section, updated props for workspace support
roots/all-issue-layout-root.tsx Integrated Calendar and Kanban layout rendering
utils.tsx Added findStateByGroup() utility for cross-project state mapping, enhanced drag-drop handling for state_detail.group
issue-layout-HOC.tsx Updated layout type handling

Store Changes

File Changes
workspace/filter.store.ts Auto-set state_detail.group as default group_by for Kanban, layout switch handling
workspace/issue.store.ts Added grouped pagination support, undefined response handling for aborted requests
helpers/base-issues.store.ts Added addIssue() method exposure for issue map updates

Hooks & Services

File Changes
use-issues.ts Exposed addIssuesToMap function for adding fetched issues to the store
workspace.service.ts Updated return type to handle undefined (aborted requests)

Backend (apps/api)

API Changes

File Changes
views/view/base.py Added grouped pagination to WorkspaceViewIssuesViewSet, backward-compatible with existing clients
utils/filters/filterset.py Added target_date__isnull and start_date__isnull filters for "No Date" queries

Packages

Constants (packages/constants)

File Changes
issue/filter.ts Added WORKSPACE_KANBAN_GROUP_BY_OPTIONS, updated layout configs for my_issues filter type
issue/common.ts Added "calendar" to WORKSPACE_ACTIVE_LAYOUTS

Technical Implementation Details

1. State Group to State ID Mapping

Workspace views group issues by state_detail.group (e.g., "backlog", "started", "completed") instead of specific state_id because states are project-specific. When an issue is dragged to a new state group column:

  1. The findStateByGroup() utility finds a matching state in the issue's project
  2. Prefers the default state for that group, falls back to first match
  3. Updates the issue's state_id to the resolved state
export const findStateByGroup = (
  projectStates: IState[] | undefined,
  targetStateGroup: string
): IState | undefined => {
  if (!projectStates) return undefined;
  return (
    projectStates.find((s) => s.group === targetStateGroup && s.default) ||
    projectStates.find((s) => s.group === targetStateGroup)
  );
};

2. "No Date" Section Architecture

The Calendar view fetches issues without target_date separately from date-range issues:

  • Separate API call: Uses target_date__isnull=true filter
  • Independent state: Managed via noDateIssueIds and noDateTotalCount
  • Cached results: Only re-fetches when appliedFiltersKey changes
  • Collapsible UI: Users can show/hide the section

3. Grouped Pagination for Kanban

The backend WorkspaceViewIssuesViewSet now supports grouped pagination:

  • Without group_by: Returns flat list (backward compatible)
  • With group_by: Returns grouped structure with per-group pagination
  • Uses existing GroupedOffsetPaginator from project issue views

4. Quick Add with Project Selection

Workspace views require project selection before creating an issue:

  • Project dropdown appears in quick add form
  • State is auto-resolved based on the column's state group
  • Pre-populated data (target_date, priority, etc.) is applied

API Changes

WorkspaceViewIssuesViewSet

Endpoint: GET /api/v1/workspaces/{workspace_slug}/views/issues/

New Query Parameters:

Parameter Type Description
group_by string Group results by field (e.g., state_detail.group, priority)
sub_group_by string Sub-group results (optional)
target_date__isnull boolean Filter issues without target date

Backward Compatibility: ✅ Existing clients receive flat list when group_by is not provided.


Testing Checklist

Calendar Layout

  • Month view displays issues on correct dates
  • Week view navigation works correctly
  • "No Date" section shows only issues without target_date
  • "No Date" section collapses/expands
  • Drag-drop changes issue target_date
  • Filters apply correctly across all projects

Kanban Layout

  • Groups by state_detail.group correctly
  • Groups by priority correctly
  • Groups by project correctly
  • Groups by labels correctly
  • Drag-drop between state groups updates issue state
  • Empty groups show/hide based on display filter

Cross-cutting

  • Layout persistence works (switching between layouts remembers choice)
  • Loading states display correctly during layout switch
  • Aborted requests don't cause errors

Related Issues


Migration Notes

No database migrations required. The feature uses existing issue fields and adds optional query parameters to existing endpoints.


Rollback Plan

  1. Revert this PR
  2. No database changes to rollback
  3. Users will see only Spreadsheet layout in workspace views (previous behavior)

Summary by CodeRabbit

  • New Features

    • Workspace Calendar view with a "No Date" collapsible section
    • Workspace Kanban view with state-based grouping and workspace-level quick-add
  • Bug Fixes

    • Fixed null-date filtering for target and start dates
  • Improvements

    • Multi-level grouping with improved pagination and group-aware fetching
    • Streamlined layout selection and enhanced drag-and-drop across views

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

@CLAassistant
Copy link

CLAassistant commented Jan 27, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 27, 2026

📝 Walkthrough

Walkthrough

Adds workspace-level calendar and kanban UIs and supporting APIs: grouped pagination for workspace issue lists, workspace roots for calendar/kanban/quick-add, filter/type consolidations, drag-drop state-group mapping, store/hook extensions, and new constants for workspace kanban grouping and DRAG_ALLOWED_GROUPS.

Changes

Cohort / File(s) Summary
API pagination & filters
apps/api/plane/app/views/view/base.py, apps/api/plane/utils/filters/filterset.py
Adds grouped pagination (group_by/sub_group_by) handling and validation in WorkspaceViewIssuesViewSet.list; introduces target_date__isnull and start_date__isnull Boolean filters.
Workspace calendar components
apps/web/core/components/issues/issue-layouts/calendar/..., .../roots/workspace-root.tsx
New WorkspaceCalendarRoot component; calendar components consolidated to use IBaseIssueFilterStore; added no-date section, props for no-date handling, and calendar-specific fetch/pagination logic.
Workspace kanban components
apps/web/core/components/issues/issue-layouts/kanban/roots/workspace-root.tsx
New WorkspaceKanBanRoot component implementing workspace Kanban view, grouping, drag-drop, delete-drop area, and per-project permission checks.
Workspace quick-add
apps/web/core/components/issues/issue-layouts/quick-add/*
New WorkspaceQuickAddIssueRoot and re-export; quick-add flow for workspace with project selector, state-group resolution, form handling, and toast feedback.
Layout helpers & UI selection
apps/web/ce/components/views/helper.tsx, apps/web/core/components/issues/issue-layouts/all-issue-layout-root.tsx, issue-layout-HOC.tsx
Adds GlobalViewLayoutSelection and WorkspaceAdditionalLayouts; layout-aware fetch control and minor HOC refactor.
Drag-drop & state utilities
apps/web/core/components/issues/issue-layouts/utils.tsx, packages/constants/src/issue/common.ts
Adds findStateByGroup utility and maps state_detail.group into DRAG_ALLOWED_GROUPS; updates drag-drop handling to resolve state groups to state IDs.
Stores & hooks
apps/web/core/store/issue/helpers/base-issues.store.ts, workspace/filter.store.ts, workspace/issue.store.ts, apps/web/core/hooks/*
Adds clearIssueIds and setLoader to IBaseIssuesStore; workspace filter methods become sync and layout-aware; quickAddIssue/archiveIssue added to global actions; addIssuesToMap added; DND types extended to include GLOBAL.
Services & constants
apps/web/core/services/workspace.service.ts, apps/live/src/services/page/core.service.ts, packages/constants/src/issue/filter.ts
getViewIssues may return undefined on aborts; renamed descriptiondescription_json in page payload type; adds WORKSPACE_KANBAN_GROUP_BY_OPTIONS and expands ISSUE_DISPLAY_FILTERS_BY_PAGE for workspace calendar/kanban/spreadsheet/gantt.
Minor UI prop changes
apps/web/app/(all)/[workspaceSlug]/(projects)/workspace-views/header.tsx
Removed workspaceSlug prop usage when rendering GlobalViewLayoutSelection.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant WorkspaceCalendarRoot
    participant IssueStore
    participant WorkspaceService
    participant CalendarChart

    User->>WorkspaceCalendarRoot: open workspace calendar
    WorkspaceCalendarRoot->>IssueStore: fetch grouped issues (by date)
    IssueStore->>WorkspaceService: GET /view/issues (group_by=target_date)
    WorkspaceService-->>IssueStore: grouped issues response
    WorkspaceCalendarRoot->>IssueStore: fetch no-date issues (target_date__isnull=true)
    WorkspaceService-->>IssueStore: no-date issues
    WorkspaceCalendarRoot->>CalendarChart: render grouped + no-date sections
    User->>CalendarChart: drag issue to date
    CalendarChart->>WorkspaceCalendarRoot: handleDragDrop(issue, date)
    WorkspaceCalendarRoot->>IssueStore: update issue.target_date
    IssueStore->>WorkspaceService: PATCH issue
    WorkspaceService-->>IssueStore: confirmation
    IssueStore-->>CalendarChart: UI updates
Loading
sequenceDiagram
    participant User
    participant WorkspaceKanBanRoot
    participant IssueStore
    participant WorkspaceService
    participant KanBanComponent
    participant DeleteModal

    User->>WorkspaceKanBanRoot: open workspace kanban
    WorkspaceKanBanRoot->>IssueStore: fetch grouped issues (group_by=state_detail.group)
    IssueStore->>WorkspaceService: GET /view/issues (group_by=state_detail.group)
    WorkspaceService-->>IssueStore: grouped issues
    WorkspaceKanBanRoot->>KanBanComponent: render groups
    User->>KanBanComponent: drag issue to delete zone
    KanBanComponent->>WorkspaceKanBanRoot: onDrop(issue)
    WorkspaceKanBanRoot->>DeleteModal: show confirm
    User->>DeleteModal: confirm
    WorkspaceKanBanRoot->>IssueStore: remove issue
    IssueStore->>WorkspaceService: DELETE issue
    WorkspaceService-->>IssueStore: confirmation
    IssueStore-->>KanBanComponent: UI updates
Loading
sequenceDiagram
    participant User
    participant WorkspaceQuickAddIssueRoot
    participant WorkspaceService
    participant IssueStore
    participant Toast

    User->>WorkspaceQuickAddIssueRoot: open quick-add
    WorkspaceQuickAddIssueRoot->>WorkspaceService: getProjectStates(projectId)
    WorkspaceService-->>WorkspaceQuickAddIssueRoot: states
    WorkspaceQuickAddIssueRoot->>WorkspaceQuickAddIssueRoot: resolve state via findStateByGroup
    User->>WorkspaceQuickAddIssueRoot: submit form
    WorkspaceQuickAddIssueRoot->>WorkspaceService: createIssue(payload)
    WorkspaceService-->>WorkspaceQuickAddIssueRoot: created issue
    WorkspaceQuickAddIssueRoot->>IssueStore: addIssuesToMap(issue)
    WorkspaceQuickAddIssueRoot->>Toast: show success
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 I hopped through workspaces, maps in paw,
Grouped dates and states now sing with awe,
Kanban lanes and calendars in tune,
Quick-add sprouts beneath the moon,
I nibbled bugs — now features hop along!

🚥 Pre-merge checks | ✅ 3 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title is partially truncated and vague, stating 'Implementation of the feature #6642 Calendar/Kanban View for Individu…' which does not clearly summarize the main change and lacks clarity. Clarify and complete the PR title to explicitly state the main change, e.g., 'Add Calendar and Kanban layouts to workspace-level views' or similar.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The PR description is comprehensive, well-structured, and covers all major aspects including summary, key features, detailed changes, technical implementation, API changes, testing checklist, and related issues.
Linked Issues check ✅ Passed The code changes fully address the requirements from issue #6642 by implementing Calendar and Kanban layouts for workspace-level views with drag-and-drop, quick-add, state mapping, and collapsible No Date section.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing Calendar/Kanban views for workspace and supporting infrastructure (grouped pagination, state mapping, filters) with no unrelated modifications detected.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

❤️ Share

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

Copy link
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: 6

Caution

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

⚠️ Outside diff range comments (4)
apps/web/core/hooks/use-issues-actions.tsx (1)

687-699: Keep pagination viewId consistent with fetchIssues override.

fetchIssues now accepts an explicit viewId, but fetchNextIssues still uses the router globalViewId. If callers pass a viewId (workspace-level views), pagination can no-op or page the wrong list. Consider mirroring the same effectiveViewId logic in fetchNextIssues.

💡 Suggested fix
-  const fetchNextIssues = useCallback(
-    async (groupId?: string, subGroupId?: string) => {
-      if (!workspaceSlug || !globalViewId) return;
-      return issues.fetchNextIssues(workspaceSlug.toString(), globalViewId.toString(), groupId, subGroupId);
-    },
-    [issues.fetchIssues, workspaceSlug, globalViewId]
-  );
+  const fetchNextIssues = useCallback(
+    async (groupId?: string, subGroupId?: string, viewId?: string) => {
+      const effectiveViewId = viewId ?? globalViewId;
+      if (!workspaceSlug || !effectiveViewId) return;
+      return issues.fetchNextIssues(workspaceSlug.toString(), effectiveViewId.toString(), groupId, subGroupId);
+    },
+    [issues.fetchIssues, workspaceSlug, globalViewId]
+  );
-  fetchNextIssues: (groupId?: string, subGroupId?: string) => Promise<TIssuesResponse | undefined>;
+  fetchNextIssues: (groupId?: string, subGroupId?: string, viewId?: string) => Promise<TIssuesResponse | undefined>;
apps/web/core/services/workspace.service.ts (1)

266-283: Fix type annotation in workspace-root.tsx for getViewIssues response.

The getViewIssues method now returns Promise<TIssuesResponse | undefined> for canceled requests, but workspace-root.tsx line 133 declares the response as const response: TIssuesResponse (missing | undefined). This will cause type errors in strict mode. Update the type to TIssuesResponse | undefined or remove the explicit type annotation to infer it correctly. The runtime guard if (response && response.results) is present but the type declaration is incorrect.

apps/web/core/store/issue/workspace/filter.store.ts (1)

248-265: Re-check sub_group_by after normalizing group_by.
If group_by is forced to "state_detail.group", sub_group_by can still equal it, bypassing the earlier guard. Consider validating again after the normalization.

🔧 Suggested fix
          if (_filters.displayFilters.layout === "kanban") {
            if (
              !_filters.displayFilters.group_by ||
              !WORKSPACE_KANBAN_GROUP_BY_OPTIONS.includes(
                _filters.displayFilters.group_by as typeof WORKSPACE_KANBAN_GROUP_BY_OPTIONS[number]
              )
            ) {
              _filters.displayFilters.group_by = "state_detail.group";
              updatedDisplayFilters.group_by = "state_detail.group";
            }
+           if (_filters.displayFilters.group_by === _filters.displayFilters.sub_group_by) {
+             _filters.displayFilters.sub_group_by = null;
+             updatedDisplayFilters.sub_group_by = null;
+           }
          }
apps/web/core/components/issues/issue-layouts/calendar/day-tile.tsx (1)

77-123: Avoid using ref .current in useEffect dependency array.

dayTileRef?.current in the dependency array won't trigger re-renders when the ref changes since refs are mutable and don't cause component updates. The ref object itself (dayTileRef) is stable across renders.

Proposed fix
-  }, [dayTileRef?.current, formattedDatePayload]);
+  }, [formattedDatePayload, handleDragAndDrop, issues]);

Note: You may also want to include handleDragAndDrop and issues in the dependency array since they're used inside the effect, or wrap them in useCallback/memoize appropriately to prevent stale closures.

🤖 Fix all issues with AI agents
In `@apps/api/plane/app/views/view/base.py`:
- Around line 246-247: Replace the unsafe deepcopy of the Django QuerySet:
instead of using copy.deepcopy(issue_queryset) to create
filtered_issue_queryset, call issue_queryset.all() to produce a new, independent
QuerySet (and remove the now-unused import copy from the top of the file if it's
no longer referenced). Ensure this change targets the filtered_issue_queryset
assignment where issue_queryset is referenced.

In
`@apps/web/core/components/issues/issue-layouts/calendar/roots/workspace-root.tsx`:
- Around line 103-165: The no-date fetch (fetchNoDateIssues) currently uses
perPageCount: 50 and sets setNoDateTotalCount(issueIds.length), which caps and
misreports totals; change it to read TIssuesResponse.total_count for total count
and implement pagination/load-more using the same pattern as the main calendar
(use workspaceService.getViewIssues response.next_page_results / cursors and a
loadMoreNoDateIssues handler or reuse loadMoreIssues) to request additional
pages instead of relying on a single 50-item request; accumulate results by
appending new issues to the existing no-date IDs (setNoDateIssueIds) and calling
addIssuesToMap for each page, and only fall back to client-side filtering of
issue.target_date while preserving the API pagination cursors rather than
truncating by array.length.

In
`@apps/web/core/components/issues/issue-layouts/kanban/roots/workspace-root.tsx`:
- Around line 205-218: handleCollapsedGroups currently mutates the store-backed
collapsedGroups array with push(), which can cause non-atomic updates; instead
create a new array before calling updateFilters: read the existing array from
issuesFilter?.issueFilters?.kanbanFilters?.[toggle] into collapsedGroups, then
if value is included produce a new array using filter to remove it, otherwise
produce a new array by concatenating the value (e.g., [...collapsedGroups,
value]); pass that new array to updateFilters (function updateFilters) so the
original store array is never mutated in place.
- Around line 190-203: The current handleDeleteIssue swallows errors by catching
all exceptions and always resolving, preventing DeleteIssueModal from receiving
rejections and showing error toasts; update handleDeleteIssue so that you await
removeIssue(draggedIssue.project_id, draggedIssueId) and only call
setDeleteIssueModal(false) and setDraggedIssueId(undefined) on success, but do
not swallow failures—either remove the try/catch entirely or rethrow the caught
error in the catch block (keep references to handleDeleteIssue, removeIssue,
setDeleteIssueModal, setDraggedIssueId, and DeleteIssueModal to locate the
change).

In `@apps/web/core/components/issues/issue-layouts/quick-add/workspace-root.tsx`:
- Around line 69-87: The logic in resolvedPrePopulatedData (useMemo) currently
retains "state_detail.group" when no matching state is found; update it so you
always remove the "state_detail.group" key from prePopulatedData and only add
state_id when findStateByGroup(projectStates, stateGroup) returns a targetState;
locate the resolution in resolvedPrePopulatedData (references:
selectedProjectId, prePopulatedData, getProjectStates, findStateByGroup, TIssue)
and return the spread rest without the "state_detail.group" field in both
branches, conditionally merging state_id = targetState.id only when targetState
exists.

In `@apps/web/core/components/issues/issue-layouts/utils.tsx`:
- Around line 563-597: When handling groupBy === "state_detail.group", guard
against a missing project state list by checking the result of
getProjectStates(sourceIssue.project_id) and if projectStates is undefined (i.e.
sourceIssue.project_id is falsy or no states returned) throw an explicit Error
(or otherwise block the drop) before calling findStateByGroup; update the logic
around getProjectStates, findStateByGroup, updatedIssue and issueUpdates to
ensure you only set state_id and issueUpdates when a valid targetState is found
and otherwise reject the operation with a clear error message.
🧹 Nitpick comments (5)
apps/web/ce/components/views/helper.tsx (1)

8-12: Unused workspaceSlug prop in GlobalViewLayoutSelection.

The workspaceSlug property is defined in TLayoutSelectionProps but is not destructured or used in the component implementation. If this is intentional for API consistency, consider adding a comment. Otherwise, remove it from the type definition to keep the interface clean.

♻️ Suggested fix if the prop is not needed
 export type TLayoutSelectionProps = {
   onChange: (layout: EIssueLayoutTypes) => void;
   selectedLayout: EIssueLayoutTypes;
-  workspaceSlug: string;
 };

Also applies to: 21-22

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

266-339: Add validation for allowed group_by and sub_group_by field values.

The code correctly prevents group_by and sub_group_by from being equal, but does not validate that these values are from the set of supported grouping fields (state_id, priority, state__group, cycle_id, project_id, labels__id, assignees__id, issue_module__module_id, target_date, start_date, created_by). Invalid field names are silently ignored—issue_group_values returns an empty list and results fail to group properly—leaving users without feedback that their grouping parameter was unsupported. Adding validation to reject invalid field names with a 400 error would improve clarity and user experience.

apps/web/core/components/issues/issue-layouts/calendar/calendar.tsx (1)

241-276: Add ARIA state for the “No Date” toggle.

This makes the collapsible section discoverable to screen readers.

♿ Proposed accessibility tweak
-                <button
+                <button
                   type="button"
+                  aria-expanded={!isNoDateCollapsed}
+                  aria-controls="no-date-section"
                   className="flex w-full items-center gap-2 px-4 py-2 bg-layer-1 cursor-pointer hover:bg-layer-2 text-left"
                   onClick={() => setIsNoDateCollapsed(!isNoDateCollapsed)}
                 >
                   <ChevronRight
                     className={cn("size-4 text-tertiary transition-transform", {
                       "rotate-90": !isNoDateCollapsed,
                     })}
                   />
                   <span className="text-13 font-medium text-secondary">No Date</span>
                   <span className="text-11 text-tertiary">({noDateIssueCount ?? noDateIssueIds.length})</span>
                 </button>
                 {!isNoDateCollapsed && (
-                  <div className="px-4 py-2 bg-surface-1">
+                  <div id="no-date-section" className="px-4 py-2 bg-surface-1">
                     <CalendarIssueBlocks
                       date={new Date()}
                       issueIdList={noDateIssueIds}
                       loadMoreIssues={() => {}}
apps/web/core/components/issues/issue-layouts/kanban/roots/workspace-root.tsx (1)

81-92: Avoid duplicate permission scans per render.

Compute once and reuse for the two prop expressions.

♻️ Proposed small refactor
   const canCreateIssues = useCallback(() => {
     if (!joinedProjectIds || joinedProjectIds.length === 0) return false;
     return joinedProjectIds.some((projectId) =>
       allowPermissions(
         [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
         EUserPermissionsLevel.PROJECT,
         workspaceSlug?.toString(),
         projectId
       )
     );
   }, [joinedProjectIds, allowPermissions, workspaceSlug]);

+  const canCreateIssuesValue = canCreateIssues();
...
-                enableQuickIssueCreate={enableQuickAdd && canCreateIssues()}
+                enableQuickIssueCreate={enableQuickAdd && canCreateIssuesValue}
...
-                disableIssueCreation={!enableIssueCreation || !canCreateIssues()}
+                disableIssueCreation={!enableIssueCreation || !canCreateIssuesValue}

Also applies to: 223-223, 270-273

apps/web/core/components/issues/issue-layouts/calendar/roots/workspace-root.tsx (1)

51-62: Compute canCreateIssues once per render.

Avoid repeating the permission scan for both creation props.

♻️ Small reuse improvement
   const canCreateIssues = useCallback(() => {
     if (!joinedProjectIds || joinedProjectIds.length === 0) return false;
     return joinedProjectIds.some((projectId) =>
       allowPermissions(
         [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
         EUserPermissionsLevel.PROJECT,
         workspaceSlug?.toString(),
         projectId
       )
     );
   }, [joinedProjectIds, allowPermissions, workspaceSlug]);

+  const canCreateIssuesValue = canCreateIssues();
...
-        enableQuickIssueCreate={enableQuickAdd && canCreateIssues()}
-        disableIssueCreation={!enableIssueCreation || !canCreateIssues()}
+        enableQuickIssueCreate={enableQuickAdd && canCreateIssuesValue}
+        disableIssueCreation={!enableIssueCreation || !canCreateIssuesValue}

Also applies to: 274-275

…ent pagination

Mirror the effectiveViewId logic from fetchIssues in fetchNextIssues so
that workspace-level views using an explicit viewId paginate correctly.
Also fix dependency array referencing fetchIssues instead of fetchNextIssues.
…Issues response

Let TypeScript infer the return type (TIssuesResponse | undefined) instead
of explicitly annotating as TIssuesResponse, which is incorrect since
getViewIssues can return undefined for canceled requests.
…nban

When group_by is forced to state_detail.group for workspace kanban views,
sub_group_by could still equal it, bypassing the earlier duplicate guard.
Added a post-normalization check to nullify sub_group_by in that case.
Remove dayTileRef.current from dependency array (ref mutations don't
trigger re-renders) and add handleDragAndDrop and issues to prevent
stale closures in the drop handler.
The prop was defined in the type and passed by the caller but never
used in GlobalViewLayoutSelection. Removed from both the type and
the call site.
Reject invalid field names with a 400 error instead of silently
returning empty groups. Validates against the set of fields supported
by issue_group_values.
Replace useCallback returning a function with useMemo returning a
boolean to avoid duplicate permission scans across joinedProjectIds
on every render. Applied to both kanban and calendar workspace roots.
Use response.total_count instead of issueIds.length for accurate count
when there are more no-date issues than the page size. Increased
perPageCount from 50 to 500 to cover most real-world cases without
needing full cursor-based pagination.
Use .finally() for cleanup instead of try/catch that swallowed errors.
This allows DeleteIssueModal to surface error toasts on failed deletes.
Use spread to create a new array instead of push() which mutates
the MobX store array before updateFilters runs.
Remove the synthetic state_detail.group key regardless of whether a
matching state is found, preventing the API from receiving an
unrecognized field.
Throw early when sourceIssue.project_id is falsy instead of silently
skipping the state_id update. Also simplify the else branch to always
throw when no matching state is found, since projectStates is now
guaranteed to be defined.
@anton-v-a anton-v-a changed the title Implementation of the feature #6642 Calendar/Kanban View for Individu… Implementation of the feature #6642 Calendar/Kanban View for Individual User across the Projects Jan 31, 2026
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: 1

🤖 Fix all issues with AI agents
In `@apps/api/plane/app/views/view/base.py`:
- Around line 278-281: The code always calls issue_queryset_grouper even when
grouping isn't requested, causing expensive subquery annotations (e.g.,
assignee_ids, label_ids, module_ids) to be added unnecessarily; modify
issue_queryset_grouper to return the original queryset immediately when both
group_by and sub_group_by are falsy (add an early return at the top of
issue_queryset_grouper) so no annotations are applied when grouping is not used,
and keep the existing behavior for the rest of the function.
🧹 Nitpick comments (1)
apps/web/core/store/issue/workspace/filter.store.ts (1)

210-220: Consider logging typed errors with console.error.
Normalizing unknown error values improves log quality and consistency.

♻️ Suggested change
   } catch (error) {
-    console.log("error while updating rich filters", error);
-    throw error;
+    const err = error instanceof Error ? error : new Error(String(error));
+    console.error("error while updating rich filters", err);
+    throw err;
   }

As per coding guidelines: Use try-catch with proper error types and log errors appropriately.

Comment on lines +278 to 281
# Apply grouper to issue queryset
issue_queryset = issue_queryset_grouper(
queryset=issue_queryset, group_by=group_by, sub_group_by=sub_group_by
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

rg -n "def issue_queryset_grouper" -A 30 apps/api/plane/app/views/view/base.py

Repository: makeplane/plane

Length of output: 41


🏁 Script executed:

rg -n "def issue_queryset_grouper" -A 30

Repository: makeplane/plane

Length of output: 4567


🏁 Script executed:

rg -n "def issue_queryset_grouper" -A 100 apps/api/plane/utils/grouper.py | head -80

Repository: makeplane/plane

Length of output: 2965


🏁 Script executed:

sed -n '270,290p' apps/api/plane/app/views/view/base.py

Repository: makeplane/plane

Length of output: 879


Unconditional invocation of grouper applies unnecessary annotations when grouping is not used.

The issue_queryset_grouper function is called even when group_by and sub_group_by are both falsy. While the function safely skips filters in those cases (lines 41-43 in grouper.py), it still unconditionally applies all subquery annotations to the queryset (lines 81-86). When grouping is not needed, these annotations—particularly the assignee_ids, label_ids, and module_ids subqueries—are computed unnecessarily, adding overhead to the query. Consider adding an early return when neither grouping parameter is provided.

🤖 Prompt for AI Agents
In `@apps/api/plane/app/views/view/base.py` around lines 278 - 281, The code
always calls issue_queryset_grouper even when grouping isn't requested, causing
expensive subquery annotations (e.g., assignee_ids, label_ids, module_ids) to be
added unnecessarily; modify issue_queryset_grouper to return the original
queryset immediately when both group_by and sub_group_by are falsy (add an early
return at the top of issue_queryset_grouper) so no annotations are applied when
grouping is not used, and keep the existing behavior for the rest of the
function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feature]: Calendar/Kabana View for Individual User across the Projects

2 participants