[FIX] Fix work items outer box#55
Conversation
…on the scroll region so height comes from the dropdown panel, filteredIssues uses the same rules as WorkspaceViewsPage, header count uses filteredIssues.length and empty state 'No work items match your filters.' when the list is non-empty but filtered to zero
…al ID, state, priority, start/due, assignee, labels, sub-work count, attachments, estimate, module, cycle, link, plus visibility and overflow menu
There was a problem hiding this comment.
Pull request overview
This PR refactors the project “work items” (issues) UI by adding a new filters + display dropdown experience in the header, wiring those settings into the issues list via custom events, and cleaning up legacy “Plane-style” references in comments.
Changes:
- Add project issues Filters and Display dropdown panels in
PageHeader.tsx, including local persistence of display settings and custom date-range modal integration. - Update
IssueListPage.tsxto consume filter/display events, fetch cycles/modules, and support grouping/sorting + conditional columns. - Rename the project views icon and remove “Plane-style” references across UI and API comments.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| ui/src/types/workspaceViewDisplay.ts | Comment cleanup removing “Plane-style” terminology. |
| ui/src/pages/WorkspaceViewsPage.tsx | Comment cleanup removing “Plane-style” terminology. |
| ui/src/pages/IssueListPage.tsx | Implements filtering, grouping, conditional columns, and event-driven state updates for project issues. |
| ui/src/pages/CyclesPage.tsx | Comment cleanup. |
| ui/src/lib/viewFilterCount.ts | Comment cleanup. |
| ui/src/lib/utils.ts | Exposes normalizeUuidKey for consistent UUID comparisons; updates member lookup to use it. |
| ui/src/lib/projectIssuesEvents.ts | Introduces shared event names and filter/display state shapes for project issues. |
| ui/src/lib/projectIssuesDisplay.ts | Adds project-issues display state model + localStorage persistence helpers. |
| ui/src/lib/issueListGroupAndSort.ts | New grouping/sorting logic for issues list based on display settings. |
| ui/src/components/project-issues/ProjectIssuesFiltersPanel.tsx | New filters dropdown panel UI (priority/state/assignee/date presets). |
| ui/src/components/project-issues/ProjectIssuesDisplayPanel.tsx | New display dropdown panel UI (columns, group-by, order-by, toggles). |
| ui/src/components/layout/Sidebar.tsx | Comment cleanup removing “Plane-style” terminology. |
| ui/src/components/layout/PageHeader.tsx | Adds project issues filter/display dropdowns, member loading for filters, icon update. |
| api/internal/service/cycle.go | Comment cleanup removing “Plane-style” terminology. |
| api/internal/redis/cache.go | Comment cleanup removing “Plane-style” terminology. |
| api/internal/queue/queue.go | Comment cleanup removing “Plane-style” terminology. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const memberName = (uid: string) => { | ||
| const m = members.find((x) => x.member_id === uid); | ||
| return m?.member_display_name?.trim() || m?.member_email?.split('@')[0]?.trim() || 'Member'; | ||
| }; |
There was a problem hiding this comment.
buildGroupedIssues’s assignee/creator group title lookup uses members.find((x) => x.member_id === uid). Elsewhere (e.g., IssueListPage) the code uses findWorkspaceMemberByUserId / normalizeUuidKey to handle UUID formatting differences and older payloads that may use a different key. For consistency (and to avoid showing “Member” for valid users), consider using the shared helper or normalizing IDs before lookup.
| <a | ||
| href={issueUrl} | ||
| target="_blank" | ||
| rel="noreferrer" | ||
| className="flex size-6 items-center justify-center rounded text-(--txt-icon-tertiary) hover:bg-(--bg-layer-1-hover) hover:text-(--txt-icon-secondary)" | ||
| title="Open in new tab" | ||
| onClick={(e) => e.stopPropagation()} | ||
| > | ||
| <IconLinkOut /> | ||
| </a> |
There was a problem hiding this comment.
renderIssueRow wraps the entire row in a react-router Link (renders an ) and then conditionally renders another <a href=...> inside it for the “open in new tab” action. Nested anchors are invalid HTML and can cause broken navigation / click handling (stopPropagation won’t reliably prevent the outer link). Consider making the row container non-anchor (e.g.,
window.open.
| <a | |
| href={issueUrl} | |
| target="_blank" | |
| rel="noreferrer" | |
| className="flex size-6 items-center justify-center rounded text-(--txt-icon-tertiary) hover:bg-(--bg-layer-1-hover) hover:text-(--txt-icon-secondary)" | |
| title="Open in new tab" | |
| onClick={(e) => e.stopPropagation()} | |
| > | |
| <IconLinkOut /> | |
| </a> | |
| <button | |
| type="button" | |
| className="flex size-6 items-center justify-center rounded text-(--txt-icon-tertiary) hover:bg-(--bg-layer-1-hover) hover:text-(--txt-icon-secondary)" | |
| title="Open in new tab" | |
| onClick={(e) => { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| window.open(issueUrl, '_blank', 'noopener,noreferrer'); | |
| }} | |
| > | |
| <IconLinkOut /> | |
| </button> |
| const now = new Date(); | ||
| const addDays = (d: number) => new Date(now.getTime() + d * 24 * 60 * 60 * 1000); | ||
| const startDateEffective = | ||
| listFilters.startDate.length && | ||
| !( | ||
| listFilters.startDate.includes('custom') && | ||
| (!listFilters.startAfter || !listFilters.startBefore) | ||
| ); | ||
| if (startDateEffective) { | ||
| list = list.filter((i) => { | ||
| const sd = i.start_date ? new Date(i.start_date) : null; | ||
| if (!sd) return false; | ||
| return listFilters.startDate.some((preset) => { | ||
| if (preset === 'custom' && listFilters.startAfter && listFilters.startBefore) { | ||
| const after = new Date(listFilters.startAfter); | ||
| const before = new Date(listFilters.startBefore); | ||
| return sd >= after && sd <= before; | ||
| } |
There was a problem hiding this comment.
Date filtering uses new Date(i.start_date) / new Date(i.target_date) and also parses the custom range with new Date(listFilters.startAfter|startBefore). In this codebase there’s a dedicated parseISODateLocal() helper for YYYY-MM-DD values to avoid UTC off-by-one shifts; using the built-in Date parser for date-only strings will produce timezone-dependent results. Also, the preset comparisons use sd >= now, which will exclude items dated “today” (midnight) for most of the day; consider comparing against start-of-today instead.
| export interface ProjectIssuesFiltersPanelProps { | ||
| search: string; | ||
| onSearchChange: (v: string) => void; | ||
| filters: ProjectIssuesFiltersState; | ||
| setFilters: React.Dispatch<React.SetStateAction<ProjectIssuesFiltersState>>; | ||
| members: WorkspaceMemberApiResponse[]; |
There was a problem hiding this comment.
This file references React.Dispatch / React.SetStateAction in the props, but React isn’t imported as a type. With the repo’s TS config (jsx: react-jsx, verbatimModuleSyntax, strict), React won’t be available as a global namespace, so this will fail type-checking. Import the needed types from react (e.g., Dispatch, SetStateAction) or import type * as React from 'react'.
| export interface ProjectIssuesDisplayPanelProps { | ||
| display: ProjectIssuesDisplayState; | ||
| setDisplay: React.Dispatch<React.SetStateAction<ProjectIssuesDisplayState>>; | ||
| } |
There was a problem hiding this comment.
This file uses React.Dispatch / React.SetStateAction but doesn’t import React as a type. In this repo’s TS setup, the React namespace won’t be in scope automatically, so the file won’t compile. Import the types directly from react (recommended) or add a import type * as React from 'react'.
| {section === 'issues' && ( | ||
| <DateRangeModal | ||
| open={issuesDateRangeModal !== null} | ||
| onClose={() => setIssuesDateRangeModal(null)} | ||
| title={issuesDateRangeModal === 'start' ? 'Start date range' : 'Due date range'} | ||
| after={ | ||
| issuesDateRangeModal === 'start' ? issuesFilters.startAfter : issuesFilters.dueAfter | ||
| } | ||
| before={ | ||
| issuesDateRangeModal === 'start' ? issuesFilters.startBefore : issuesFilters.dueBefore | ||
| } |
There was a problem hiding this comment.
When the user toggles the “custom” start/due date filter, the modal can be closed via Cancel/escape, but startDate/dueDate will still include 'custom' with startAfter/startBefore (or dueAfter/dueBefore) left null. This makes the header “filters active” indicator light up even though IssueListPage intentionally treats that state as non-effective filtering. Consider clearing the 'custom' preset (and related fields) when the modal closes without applying, or only counting custom as active when both bounds are set.
This pull request primarily refactors the UI for the project issues section by introducing a new filter dropdown panel, improves filter state management, and removes legacy "Plane-style" references from comments across the codebase. The changes enhance usability for filtering issues and clarify code documentation.
UI/UX Improvements for Project Issues Filters:
Dropdown+ProjectIssuesFiltersPanel) to the project issues section inPageHeader.tsx, allowing users to filter issues by priority, state, assignee, and date range. The filter state is managed locally and dispatched as a custom event for other components to consume. A badge indicator shows when filters are active. ([[1]](https://github.com/Devlaner/devlane/pull/55/files#diff-145447ff85e6b8c42a58d2551342e376c1cf3f2e62152aeb3f9b514ba47e065cR1021),[[2]](https://github.com/Devlaner/devlane/pull/55/files#diff-145447ff85e6b8c42a58d2551342e376c1cf3f2e62152aeb3f9b514ba47e065cR1064-R1070),[[3]](https://github.com/Devlaner/devlane/pull/55/files#diff-145447ff85e6b8c42a58d2551342e376c1cf3f2e62152aeb3f9b514ba47e065cR1132-R1156),[[4]](https://github.com/Devlaner/devlane/pull/55/files#diff-145447ff85e6b8c42a58d2551342e376c1cf3f2e62152aeb3f9b514ba47e065cL1244-R1338),[[5]](https://github.com/Devlaner/devlane/pull/55/files#diff-145447ff85e6b8c42a58d2551342e376c1cf3f2e62152aeb3f9b514ba47e065cR2338-R2372))DateRangeModalfor custom date filtering within the issues filter panel, updating the filter state accordingly. ([ui/src/components/layout/PageHeader.tsxR2338-R2372](https://github.com/Devlaner/devlane/pull/55/files#diff-145447ff85e6b8c42a58d2551342e376c1cf3f2e62152aeb3f9b514ba47e065cR2338-R2372))Icon and Visual Consistency Updates:
IconViewsPlaneicon with the newIconProjectViewsfor project views navigation and dropdowns, ensuring consistent branding and iconography. ([[1]](https://github.com/Devlaner/devlane/pull/55/files#diff-145447ff85e6b8c42a58d2551342e376c1cf3f2e62152aeb3f9b514ba47e065cL349-R356),[[2]](https://github.com/Devlaner/devlane/pull/55/files#diff-145447ff85e6b8c42a58d2551342e376c1cf3f2e62152aeb3f9b514ba47e065cL490-R497),[[3]](https://github.com/Devlaner/devlane/pull/55/files#diff-145447ff85e6b8c42a58d2551342e376c1cf3f2e62152aeb3f9b514ba47e065cL2358-R2482),[[4]](https://github.com/Devlaner/devlane/pull/55/files#diff-145447ff85e6b8c42a58d2551342e376c1cf3f2e62152aeb3f9b514ba47e065cL2370-R2494),[[5]](https://github.com/Devlaner/devlane/pull/55/files#diff-145447ff85e6b8c42a58d2551342e376c1cf3f2e62152aeb3f9b514ba47e065cL2714-R2838),[[6]](https://github.com/Devlaner/devlane/pull/55/files#diff-145447ff85e6b8c42a58d2551342e376c1cf3f2e62152aeb3f9b514ba47e065cL2723-R2847))Codebase Cleanup and Documentation:
queue.go,cache.go,cycle.go, and various frontend files. ([[1]](https://github.com/Devlaner/devlane/pull/55/files#diff-da1b8a210f84d9350910e5404ce93674397344666d2f6d0e62954e08101735baL13-R13),[[2]](https://github.com/Devlaner/devlane/pull/55/files#diff-304559d08e7a4339f114e954a01c73e99058ae3ca4c37bf6001daf93101a31f1L11-R11),[[3]](https://github.com/Devlaner/devlane/pull/55/files#diff-304559d08e7a4339f114e954a01c73e99058ae3ca4c37bf6001daf93101a31f1L71-R71),[[4]](https://github.com/Devlaner/devlane/pull/55/files#diff-304559d08e7a4339f114e954a01c73e99058ae3ca4c37bf6001daf93101a31f1L88-R88),[[5]](https://github.com/Devlaner/devlane/pull/55/files#diff-304559d08e7a4339f114e954a01c73e99058ae3ca4c37bf6001daf93101a31f1L131-R131),[[6]](https://github.com/Devlaner/devlane/pull/55/files#diff-8648cdc1ec7f1219e25f99893762e33a3e46bf4f61e0c10642c25c8f65df79b4L13-R13),[[7]](https://github.com/Devlaner/devlane/pull/55/files#diff-145447ff85e6b8c42a58d2551342e376c1cf3f2e62152aeb3f9b514ba47e065cL2291-R2415),[[8]](https://github.com/Devlaner/devlane/pull/55/files#diff-669d16bb6beef8d1df0b0496917e06eaf9b4f639ef67b353cd8f6b63a3a20226L269-R269),[[9]](https://github.com/Devlaner/devlane/pull/55/files#diff-669d16bb6beef8d1df0b0496917e06eaf9b4f639ef67b353cd8f6b63a3a20226L792-R792),[[10]](https://github.com/Devlaner/devlane/pull/55/files#diff-145447ff85e6b8c42a58d2551342e376c1cf3f2e62152aeb3f9b514ba47e065cL2542-R2666))Supporting Changes for New Filters:
[ui/src/components/layout/PageHeader.tsxR1132-R1156](https://github.com/Devlaner/devlane/pull/55/files#diff-145447ff85e6b8c42a58d2551342e376c1cf3f2e62152aeb3f9b514ba47e065cR1132-R1156))DEFAULT_PROJECT_ISSUES_FILTERSand ensured filter changes are broadcast via a custom event for integration with other components. ([[1]](https://github.com/Devlaner/devlane/pull/55/files#diff-145447ff85e6b8c42a58d2551342e376c1cf3f2e62152aeb3f9b514ba47e065cR38-R42),[[2]](https://github.com/Devlaner/devlane/pull/55/files#diff-145447ff85e6b8c42a58d2551342e376c1cf3f2e62152aeb3f9b514ba47e065cR1064-R1070),[[3]](https://github.com/Devlaner/devlane/pull/55/files#diff-145447ff85e6b8c42a58d2551342e376c1cf3f2e62152aeb3f9b514ba47e065cR1132-R1156))These updates improve the user experience for filtering project issues and clean up the codebase for better maintainability.
closes #54