refactor: migrate web icons to @makeplane/propel - #9731
Conversation
Replace confirmed icon symbols across apps/web with their audited @makeplane/propel/icons targets. Unresolved symbols and the State, Priority and Brand icons stay on their existing imports. Lucide size props become explicit width/height, including the two relation icons and the inbox status icon that forward a size variable. Two aliased imports resolve to targets the file already imported, so the aliases are dropped rather than importing the same symbol twice. Widen the member dropdown chain and the AI menu from LucideIcon to ComponentType<SVGProps<SVGSVGElement>> so both Lucide and Propel icons satisfy them; both only ever render the icon with a className. CycleGroupIcon is deliberately not migrated: it dispatches a different glyph and color per cycle status, which CyclesOutline cannot express. apps/web already declared @makeplane/propel, so no dependency change was needed.
|
Important Review skippedToo many files! This PR contains 470 files, which is 170 over the limit of 300. To get a review, reduce the PR to 300 files or fewer by splitting it into smaller PRs or changing its base branch. Usage-priced reviews support at most 300 files. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (470)
You can disable this status message by setting the 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. Comment |
|
React Doctor found 40 new issues in 40 files · 40 warnings · score 70 / 100 (Needs work) · 121 fixed · vs 40 warnings
Reviewed by React Doctor for commit |
|
|
||
| export function LockedComponent(props: { toolTipContent?: string }) { | ||
| const { toolTipContent } = props; | ||
| const lockedComponent = ( |
There was a problem hiding this comment.
React Doctor · react-doctor/rendering-hoist-jsx (warning)
This rebuilds on every render because static JSX "lockedComponent" is built inside the component, so move it to the top of the file to make it just once
Fix → Move the static JSX out to the top of the file: const ICON = <svg>...</svg>, so it isn't rebuilt on every render
| import { ArchiveIcon } from "@plane/propel/icons"; | ||
| import { useIssueDetail } from "@/hooks/store/use-issue-detail"; | ||
| // components | ||
| import { IssueActivityBlockComponent } from "./"; |
There was a problem hiding this comment.
React Doctor · react-doctor/no-barrel-import (warning)
This ships extra code to your users & slows page load. Import directly from "./helpers/activity-block".
Fix → Import from the direct path: import { Button } from './components/Button' instead of ./components
| import { CyclesOutline } from "@makeplane/propel/icons"; | ||
| import { useIssueDetail } from "@/hooks/store/use-issue-detail"; | ||
| // components | ||
| import { IssueActivityBlockComponent } from "./"; |
There was a problem hiding this comment.
React Doctor · react-doctor/no-barrel-import (warning)
This ships extra code to your users & slows page load. Import directly from "./helpers/activity-block".
Fix → Import from the direct path: import { Button } from './components/Button' instead of ./components
| import { IntakeOutline } from "@makeplane/propel/icons"; | ||
| import { useIssueDetail } from "@/hooks/store/use-issue-detail"; | ||
| // components | ||
| import { IssueActivityBlockComponent } from "./"; |
There was a problem hiding this comment.
React Doctor · react-doctor/no-barrel-import (warning)
This ships extra code to your users & slows page load. Import directly from "./helpers/activity-block".
Fix → Import from the direct path: import { Button } from './components/Button' instead of ./components
| import { ModuleOutline } from "@makeplane/propel/icons"; | ||
| import { useIssueDetail } from "@/hooks/store/use-issue-detail"; | ||
| // components | ||
| import { IssueActivityBlockComponent } from "./"; |
There was a problem hiding this comment.
React Doctor · react-doctor/no-barrel-import (warning)
This ships extra code to your users & slows page load. Import directly from "./helpers/activity-block".
Fix → Import from the direct path: import { Button } from './components/Button' instead of ./components
| import { StateOutline } from "@makeplane/propel/icons"; | ||
| import { useIssueDetail } from "@/hooks/store/use-issue-detail"; | ||
| // components | ||
| import { IssueActivityBlockComponent, IssueLink } from "./"; |
There was a problem hiding this comment.
React Doctor · react-doctor/no-barrel-import (warning)
This ships extra code to your users & slows page load. Import directly from: "./helpers/activity-block", "./helpers/issue-link".
Fix → Import from the direct path: import { Button } from './components/Button' instead of ./components
| @@ -22,9 +22,9 @@ export function ModuleLayoutIcon(props: ILayoutIcon) { | |||
|
|
|||
| // get Layout icon | |||
| const icons = { | |||
There was a problem hiding this comment.
React Doctor · react-doctor/prefer-module-scope-static-value (warning)
icons inside ModuleLayoutIcon uses no local state but is rebuilt every render, so it looks new each time & breaks memoized children. Move it to the top of the file, outside the component.
Fix → Move the value above the component, at the top of the file. It doesn't use local state, so rebuilding it each update is wasted and makes it look new every time.
There was a problem hiding this comment.
Pull request overview
Refactors apps/web to migrate audit-confirmed icon usages from the existing Lucide / @plane/propel/icons imports to @makeplane/propel/icons, aligning the web app with the ongoing community icon migration while leaving unresolved symbol groups (State/Priority/Brand, CycleGroupIcon) untouched.
Changes:
- Updated icon imports/usages across
apps/webto use@makeplane/propel/iconsoutline icons where mappings are known. - Replaced Lucide
sizeusages with explicitwidth/heightin select call sites to preserve sizing behavior. - Broadened a few icon-accepting types (e.g., member dropdown chain) to allow
ComponentType<SVGProps<SVGSVGElement>>where needed.
Reviewed changes
Copilot reviewed 300 out of 470 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/web/core/components/exporter/column.tsx | Migrates download icon to DownloadOutline (note: currently also changes visible text/key to “DownloadOutline”). |
| apps/web/core/components/home/widgets/links/action.tsx | Migrates “add link” icon to AddOutline (note: contains text-2 class likely meant to be stroke-2). |
| apps/web/core/components/issues/issue-layouts/quick-add/root.tsx | Migrates quick-add icon to AddOutline (note: contains text-2 class likely meant to be stroke-2). |
| apps/web/core/components/issues/issue-layouts/quick-add/button/list.tsx | Migrates list quick-add icon to AddOutline (note: contains text-2 class likely meant to be stroke-2). |
| apps/web/core/components/issues/issue-layouts/quick-add/button/spreadsheet.tsx | Migrates spreadsheet quick-add icon to AddOutline (note: contains text-2 class likely meant to be stroke-2). |
| apps/web/core/components/issues/issue-layouts/quick-add/button/kanban.tsx | Migrates kanban quick-add icon to AddOutline (note: contains text-2 class likely meant to be stroke-2). |
| apps/web/core/components/issues/issue-layouts/quick-add/button/gantt.tsx | Migrates gantt quick-add icon to AddOutline (note: contains text-2 class likely meant to be stroke-2). |
| apps/web/core/components/issues/issue-layouts/calendar/quick-add-issue-actions.tsx | Migrates calendar quick-add icon to AddOutline (note: contains text-2 class likely meant to be stroke-2). |
| apps/web/core/components/cycles/list/cycles-list-item.tsx | Migrates completed check icon to TickOutline (note: contains text-2 class likely meant to be stroke-2). |
| apps/web/core/components/cycles/analytics-sidebar/sidebar-header.tsx | Migrates close chevron to ChevronRightOutline (note: contains text-2 class likely meant to be stroke-2). |
| apps/web/core/components/modules/analytics-sidebar/root.tsx | Migrates close chevron to ChevronRightOutline (note: contains text-2 class likely meant to be stroke-2). |
| apps/web/helpers/views.helper.ts | Migrates view-access icons to GlobeOutline / LockOutline. |
| apps/web/core/layouts/auth-layout/workspace-wrapper.tsx | Replaces Lucide logout icon with LogOutOutline and uses explicit width/height. |
| apps/web/core/components/workspace/views/header.tsx | Replaces PlusIcon with AddOutline in global views header. |
| apps/web/core/components/workspace/sidebar/quick-actions.tsx | Replaces AddWorkItemIcon with AddWorkItemOutline in sidebar quick action. |
| apps/web/core/components/pages/navigation-pane/tab-panels/assets.tsx | Replaces Lucide download icon with DownloadOutline. |
| apps/web/core/components/issues/issue-update-status.tsx | Replaces Lucide refresh icon with RefreshOutline for saving indicator. |
| apps/web/core/components/common/activity/activity-block.tsx | Replaces Lucide network icon with HierarchyOutline fallback. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| onClick={() => handleClose()} | ||
| > | ||
| <ChevronRightIcon className="size-4 stroke-2 text-secondary" /> | ||
| <ChevronRightOutline className="text-2 size-4 text-secondary" /> |
| <div className="pointer-events-none absolute inset-0 flex items-center justify-center"> | ||
| {progress === 100 ? ( | ||
| <CheckIcon className="h-3 w-3 stroke-2" /> | ||
| <TickOutline className="text-2 h-3 w-3" /> |
| key: "DownloadOutline", | ||
| content: "DownloadOutline", | ||
| tdRender: (rowData: RowData) => |
| > | ||
| <div className="my-auto h-8 w-8 rounded-sm bg-layer-1/40 p-2"> | ||
| <PlusIcon className="h-4 w-4 stroke-2 text-tertiary" /> | ||
| <AddOutline className="text-2 h-4 w-4 text-tertiary" /> |
| customButton={ | ||
| <div className="flex w-full items-center gap-x-[6px] rounded-md px-2 py-1.5 text-tertiary hover:text-tertiary"> | ||
| <PlusIcon className="h-3.5 w-3.5 flex-shrink-0 stroke-2" /> | ||
| <AddOutline className="text-2 h-3.5 w-3.5 flex-shrink-0" /> |
| onClick={onClick} | ||
| > | ||
| <PlusIcon className="h-3.5 w-3.5 stroke-2" /> | ||
| <AddOutline className="text-2 h-3.5 w-3.5" /> |
| onClick={onClick} | ||
| > | ||
| <PlusIcon className="h-3.5 w-3.5 stroke-2" /> | ||
| <AddOutline className="text-2 h-3.5 w-3.5" /> |
| onClick={onClick} | ||
| > | ||
| <PlusIcon className="h-3.5 w-3.5 stroke-2" /> | ||
| <AddOutline className="text-2 h-3.5 w-3.5" /> |
| onClick={() => handleIsOpen(true)} | ||
| > | ||
| <PlusIcon className="h-3.5 w-3.5 stroke-2" /> | ||
| <AddOutline className="text-2 h-3.5 w-3.5" /> |
| onClick={() => handleClose()} | ||
| > | ||
| <ChevronRightIcon className="h-3 w-3 stroke-2 text-on-color" /> | ||
| <ChevronRightOutline className="text-2 h-3 w-3 text-on-color" /> |
codingwolf-at
left a comment
There was a problem hiding this comment.
Identifier replace leaked into user-facing copy (DownloadOutline / SettingsOutline / PinOutline / StarOutline us on GitHub), stroke-2 became text-2, favorited stars went hollow, and strokeWidth is a no-op on fill-based Propel glyphs.
| key: "DownloadOutline", | ||
| content: "DownloadOutline", | ||
| tdRender: (rowData: RowData) => | ||
| checkExpiry(rowData.created_at) ? ( | ||
| <> | ||
| {rowData.status == "completed" ? ( | ||
| <a target="_blank" href={rowData?.url} rel="noopener noreferrer"> | ||
| <button className="flex w-full items-center gap-1 font-medium text-accent-primary"> | ||
| <Download className="h-4 w-4" /> | ||
| <div>Download</div> | ||
| <DownloadOutline className="h-4 w-4" /> | ||
| <div>DownloadOutline</div> |
There was a problem hiding this comment.
Identifier replace leaked into user-facing copy: key, content, and the button label are now "DownloadOutline". Keep icon={DownloadOutline}; restore "Download" on key / content / the <div>.
| @@ -128,43 +136,43 @@ export const ProjectCard = observer(function ProjectCard(props: Props) { | |||
| { | |||
| key: "settings", | |||
| action: () => router.push(`/${workspaceSlug}/settings/projects/${project.id}`), | |||
| title: "Settings", | |||
| icon: Settings, | |||
| title: "SettingsOutline", | |||
There was a problem hiding this comment.
title is the action/tooltip label, not the icon name. Restore title: "Settings"; keep icon: SettingsOutline.
| className="size-3.5 flex-shrink-0 text-placeholder outline-none hover:text-tertiary" | ||
| onClick={() => unPinNavigationItem(item.key)} | ||
| /> | ||
| </Tooltip> | ||
| ) : ( | ||
| <Tooltip label="Pin"> | ||
| <Pin | ||
| <Tooltip label="PinOutline"> |
There was a problem hiding this comment.
"Pin" got renamed with the lucide identifier. Restore label="Pin". (Unpin survived because it is a different string.)
| <EmptySpaceItem Icon={BoxesOutline} title="Continue to home" href="/" /> | ||
| )} | ||
| <EmptySpaceItem Icon={Star} title="Star us on GitHub" href="https://github.com/makeplane" /> | ||
| <EmptySpaceItem Icon={StarOutline} title="StarOutline us on GitHub" href="https://github.com/makeplane" /> |
There was a problem hiding this comment.
Visible empty-state copy became "StarOutline us on GitHub" because Star matched the lucide export. Restore title="Star us on GitHub"; keep Icon={StarOutline}.
Worth a sweep: grep title/label/content/key values ending in Outline/Filled.
| > | ||
| <div className="my-auto h-8 w-8 rounded-sm bg-layer-1/40 p-2"> | ||
| <PlusIcon className="h-4 w-4 stroke-2 text-tertiary" /> | ||
| <AddOutline className="text-2 h-4 w-4 text-tertiary" /> |
There was a problem hiding this comment.
stroke-2 (lucide stroke weight) was rewritten to text-2. That is not a stroke token here (text-11 / text-20 are font sizes), and Propel glyphs are fill paths so stroke-* would be a no-op anyway. Drop text-2.
Same pattern: quick-add list/kanban/gantt/spreadsheet/root + calendar quick-add, cycles-list-item, cycle sidebar header, module sidebar.
| onClick={() => handleClose()} | ||
| > | ||
| <ChevronRightIcon className="h-3 w-3 stroke-2 text-on-color" /> | ||
| <ChevronRightOutline className="text-2 h-3 w-3 text-on-color" /> |
There was a problem hiding this comment.
Drop text-2 (was stroke-2).
| @@ -227,13 +233,13 @@ export function FavoriteFolder(props: Props) { | |||
| > | |||
| <CustomMenu.MenuItem onClick={() => handleRemoveFromFavorites(favorite)}> | |||
| <span className="flex items-center justify-start gap-2"> | |||
| <Star className="fill-yellow-500 stroke-yellow-500 h-3.5 w-3.5" /> | |||
| <StarOutline className="fill-yellow-500 text-yellow-500 h-3.5 w-3.5" /> | |||
There was a problem hiding this comment.
Lucide Star + fill-yellow-500 stroke-yellow-500 was a solid yellow star. StarOutline is a ring path, so fill-yellow-500 paints the outline, not the interior. Use StarFilled for the favorited state.
| @@ -47,7 +47,7 @@ export const FavoriteItemQuickAction = observer(function FavoriteItemQuickAction | |||
| > | |||
| <CustomMenu.MenuItem onClick={() => handleRemoveFromFavorites(favorite)}> | |||
| <span className="flex items-center justify-start gap-2"> | |||
| <Star className="fill-yellow-500 stroke-yellow-500 h-3.5 w-3.5 flex-shrink-0" /> | |||
| <StarOutline className="fill-yellow-500 text-yellow-500 h-3.5 w-3.5 flex-shrink-0" /> | |||
There was a problem hiding this comment.
Same as the folder row: StarOutline + fill-yellow-500 will not look like the old solid Lucide star. Use StarFilled.
| {subIssueHelpers.preview_loader.includes(issue.id) ? ( | ||
| <div className="flex h-full w-full cursor-not-allowed items-center justify-center rounded-xs bg-layer-1 transition-all"> | ||
| <Loader width={14} strokeWidth={2} className="animate-spin" /> | ||
| <LinkOutline width={14} height={14} strokeWidth={2} className="animate-spin" /> |
There was a problem hiding this comment.
Old import was Link as Loader, so the spinner was already a link glyph. Mapping Link → LinkOutline kept that. Elsewhere in this PR loaders correctly became LoadingOutline. Use LoadingOutline with animate-spin here too.
| > | ||
| <div className="flex items-center gap-1.5 rounded-sm border border-subtle bg-surface-2 px-2"> | ||
| <SearchIcon className="h-3.5 w-3.5 text-placeholder" strokeWidth={1.5} /> | ||
| <SearchOutline className="h-3.5 w-3.5 text-placeholder" strokeWidth={1.5} /> |
There was a problem hiding this comment.
Propel generated icons paint with fill="currentColor", not stroke. strokeWidth={1.5} on the <svg> does not thicken the path (Lucide API leftover). Safe to drop.
This shows up ~117 times in the PR — same cleanup everywhere you still pass strokeWidth to a *Outline/*Filled icon.
Description
Migrates the audit-confirmed icon symbols in
apps/web(470 files) to@makeplane/propel/icons— the largest scope of this migration.apps/webalready declared the dependency.Unresolved symbols, including all State, Priority and Brand icons, stay on their existing imports.
Lucide
sizebecame explicitwidth/height, including three sites that forward a size variable; the member dropdown chain and pages AI menu widen fromLucideIcontoComponentType<SVGProps<SVGSVGElement>>so generated Propel icons satisfy them.CycleGroupIcon(6 files) is not migrated: it dispatches a different glyph and colour per cycle status, whichCyclesOutlinecannot express.Icon mapping (150 rows)
UserActivityIconActivityOutlineListFilterPlusAddFilterOutlineCreateIcon,PlusIconAddOutlineAddWorkItemIconAddWorkItemOutlineSparkle,SparklesAiStar1OutlineAlertOctagon,OctagonAlertAlertOctagonOutlineAlignLeftAlignLeftOutlineAnalyticsIconAnalyticsOutlineArchiveIconArchiveOutlineMinimize2,ShrinkArrowCollapseOutlineArrowDownArrowDownOutlineArrowLeft,MoveLeftArrowNarrowLeftOutlineArrowRight,MoveRightArrowNarrowRightOutlineAtSignAtOutlinePaperclip,PaperclipIconAttachOutlineArchiveXAutoCloseOutlineBarChart2,BarChart4,ChartNoAxesColumnBarOutlineCreditCardBillingsOutlineCircleDotBlockingOutlineBoardLayoutIconBoardOutlineBoxesBoxesOutlineBuildingBuildingOutlineCalendar,CalendarDays,CalendarLayoutIconCalendarOutlineMessageCircle,MessageSquare,MessageSquareIconChatOutlineCheckCheckCheckDoneOutlineChevronDownIconChevronDownOutlineChevronLeftIconChevronLeftOutlineChevronRightIconChevronRightOutlineChevronUpIconChevronUpOutlineClipboardClipboardOutlineClockClockOutlineCloseCircleFilledIconCloseCircleFilledCircleX,XCircleCloseCircleOutlineCloseIcon,XCloseOutlineCalendarCheckCompletedAtOutlineCopyIconCopyOutlineCornerDownRightCornerRightDownOutlineFolderPlusCreateFolderOutlineBoxCubeOutlineContrastIcon,CycleIconCyclesOutlineDashboardIconDashboardsOutlineSuspendedUserIconDeactivatedUserOutlineSetAsDefaultIconDefaultTabOutlineTrash2,TrashIconDeleteOutlineFileTextDocumentationOutlineDownloadDownloadOutlineDraftIconDraftsOutlineGripVertical,MoveDiagonalDragDropOutlineDueDatePropertyIconDueDateOutlineCopyPlus,DuplicatePropertyIcon,FileStackDuplicateOfOutlineEditIcon,PenSquare,PencilEditOutlineEpicIconEpicOutlineEstimatePropertyIconEstimateOutlineArrowUpToLine,FileOutputExportOutlineFilterIcon,ListFilterFilterOutlineFolderFolderOutlineExpand,Maximize2FullScreenOutlineFullScreenPanelIconFullScreenPeekOutlineGitBranchGitBranchOutlineGithubIconGithubEarth,GlobeIconGlobeOutlineGridLayoutIcon,LayoutGrid,LayoutGridIconGridOutlineComponentGroupOutlineHashHashOutlineHelpCircleHelpOutlineEyeOffHideOutlineNetworkHierarchyOutlineHistoryHistoryOutlineHome,HomeIconHomeOutlineImageIconImageOutlineInbox,InboxIconInboxOutlineInfoFillIconInfoFilledInfo,InfoIconInfoOutlineIntake,IntakeIconIntakeOutlineKeyRoundKeyOutlineLabelFilledIconLabelsFilledLabelPropertyIcon,TagIconLabelsOutlinePanelLeftLeftSidePaneOutlineCopyLinkIcon,Link,Link2Icon,LinkIconLinkOutlineListLayoutIconListOutlineLoaderLoadingOutlineLockKeyholeLockedOutlineLockIconLockOutlineLogOutLogOutOutlineMail,MailsMailOutlineMembersPropertyIcon,Users,Users2Icon,UsersIconMembersOutlineMinusMinusOutlineCenterPanelIconModalPeekOutlineDiceIcon,ModuleIconModuleOutlineMonitorMonitorOutlineEllipsis,MoreHorizontalMoreHorizontalOutlineMoreVertical,MoreVerticalIconMoreVerticalOutlineMultipleStickyIcon,RecentStickyIconMultipleStickyOutlineExternalLink,NewTabIconNewTabOutlinePageIconPagesOutlinePalettePaletteOutlineParentPropertyIconParentOutlinePinPinOutlineCirclePlusPlusCircleOutlinePreferencesIcon,SlidersHorizontalPreferencesOutlinePriorityPropertyIconPriorityOutlineBriefcase,Hotel,ProjectIconProjectsOutlineSmilePlusReactionOutlineRefreshCcw,RefreshCw,RotateCcwRefreshOutlineRelatedIconRelatesToOutlineRelationPropertyIconRelationsOutlineCommentReplyIconReplyOutlineArchiveRestore,ArchiveRestoreIconRestoreOutlinePanelRightRightSidePaneOutlineRocketRocketOutlineSearchIconSearchOutlineFilterAppliedIconSelectedFilterOutlineSettings,Settings2,SettingsIconSettingsOutlineShare2ShareAltOutlineEye,EyeIconShowOutlineSidePanelIconSidePeekOutlineArrowUpNarrowWide,ArrowUpWideNarrowSortAscendingOutlineArrowDownWideNarrowSortDescendingOutlineStarStarOutlineStartDatePropertyIconStartDateOutlineDoubleCircleIcon,StatePropertyIconStateOutlineStickyNote,StickyNoteIconStickyNoteOutlineBellSubscribeOutlineSheetLayoutIconTableOutlineALargeSmall,TypeTextOutlineLightbulbThoughtsOutlineCheckCircleFilledIconTickCircleFilledCheckCircle,CheckCircle2,CircleCheckTickCircleOutlineCheckIconTickOutlineTimelineLayoutIconTimelineOutlineTimerTimeTrackingOutlineListTodoToDoOutlineArrowUpTopArrowOutlineTransferIconTransferWorkItemOutlineZapTriggerOutlineLockKeyholeOpenUnlockedOutlinePinOffUnpinOutlineBellOffUnsubscribeOutlineUploadCloudUploadOutlineSquareUserUserAltOutlineUserMinus2UserMinusOutlineCircleUser,CircleUserRound,User,User2,UserCirclePropertyIcon,UserRoundUserOutlineUserPlus,UserPlus2UserPlusOutlineViewsIconViewsOutlineAlertCircle,CircleAlertWarningCircleOutlineAlertTriangle,AlertTriangleIcon,TriangleAlertWarningTriangleOutlineWebhookWebhooksOutlineShapesWidgetOutlineLayers,LayersIcon,WorkItemsIconWorkItemsOutlineYourWorkIconYourWorkOutlineType of Change
Screenshots and Media (if applicable)
Test Scenarios
References
Part of the community icon migration to
@makeplane/propel/icons. Mappings come from the icon audit manifest; none were inferred by name or appearance.🤖 Generated with Claude Code