From 342b6ebce7b9fbf7e3f669c8670bb2ea606df5d4 Mon Sep 17 00:00:00 2001 From: tangencialmente Date: Mon, 27 Oct 2025 09:33:28 -0300 Subject: [PATCH] Added end-to-end test cases using testRigor, with CI/CD. Updated tR-ci.yml (changed path to test case files) Updated tR-ci.yml Updated tR-ci.yml Added e2e test cases Updated tR-ci.yml Updated tR-ci.yml Tweaked directory naming and structure Tweaked test cases Tweaked workflow file Fix URL formatting in tR-ci.yml Renamed folders Workflow file changes Renamed test cases Add rules path to testrigor command in CI workflow Updated tR-ci.yml Update tR-ci.yml to remove verbose flag Removed the --verbose flag from the testrigor command. [WEB-5088] feat: Power K `v2` (#7905) * feat: add project shortcut in command palette * feat: global project switcher shortcut * refactor: generalize command palette entity handling * feat: extend command palette navigation * feat: add issue shortcut to command palette * feat: add modular project selection for cycle navigation * chore: add reusable command palette utilities * fix: update key sequence handling to use window methods for timeout management * fix: build errors * chore: minor ux copy improvements * feat: implement a new command registry and renderer for enhanced command palette functionality * feat: introduce new command palette components and enhance search functionality * feat: enhance command palette components with improved initialization and loading indicators * feat: Implement new command palette architecture with multi-step commands, context-aware filtering, and reusable components. Add comprehensive documentation and integration guides. Enhance command execution with a dedicated executor and context provider. Introduce new command types and improve existing command definitions for better usability and maintainability. * refactor: hook arguments * refactor: folder structure * refactor: update import paths * fix: context prop drilling * refactor: update search components * refactor: create actions * chore: add type to pages * chore: init contextual actions * refactor: context based actions code split * chore: module context-based actions * refactor: streamline command execution flow and enhance multi-step handling in command palette * refactor: remove placeholder management from command execution and implement centralized placeholder mapping * chore: cycle context based actions * refactor: simplify command execution by consolidating selection steps and adding page change handling * chore: added more options to work item contextual actions * chore: page context actions * refactor: update step type definitions and enhance page mapping for command execution * feat: implement Command Palette V2 with global shortcuts and enhanced context handling * refactor: power k v2 * refactor: creation commands * feat: add navigation utility for Power K context handling * feat: implement comprehensive navigation commands for Power K * refactor: work item contextual actions * fix: build errors * refactor: remaining contextual actions * refactor: remove old code * chore: update placeholder * refactor: enhance command registry with observable properties and context-aware shortcut handling * refactor: improve command filtering logic in CommandPaletteModal * chore: context indicator * chore: misc actions * style: shortcut badge * feat: add open entity actions and enhance navigation commands for Power K * refactor: rename and reorganize Power K components for improved clarity and structure * refactor: update CommandPalette components and streamline global shortcuts handling * refactor: adjust debounce timing in CommandPaletteModal for improved responsiveness * feat: implement shortcuts modal and enhance command registry for better shortcut management * fix: search implemented * refactor: search results code split * refactor: search results code split * feat: introduce creation and navigation command modules for Power K, enhancing command organization and functionality * chore: update menu logos * refactor: remove unused PowerKOpenEntityActionsExtended component from command palette * refactor: search menu * fix: clear context on backspace and manual clear * refactor: rename creation command keys for consistency and clarity in Power K * chore: added intake in global search * chore: preferences menu * chore: removed the empty serach params * revert: command palette changes * cleanup * refactor: update command IDs to use underscores for consistency across Power K components * refactor: extended context based actions * chore: modal command item status props * refactor: replace CommandPalette with CommandPaletteProvider in settings and profile layouts * refactor: update settings menu to use translated labels instead of i18n labels * refactor: update command titles to use translation keys for creation actions * refactor: update navigation command titles to use translation keys for consistency * chore: minor cleanup * chore: misc commands added * chore: code split for no search results command * chore: state menu items for work item context based commands * chore: add more props to no search results command * chore: add more props to no search results command * refactor: remove shortcut key for create workspace command * Refactor command palette to use PowerK store - Replaced instances of `useCommandPalette` with `usePowerK` across various components, including `AppSearch`, `CommandModal`, and `CommandPalette`. - Introduced `PowerKStore` to manage modal states and commands, enhancing the command palette functionality. - Updated modal handling to toggle `PowerKModal` and `ShortcutsListModal` instead of the previous command palette modals. - Refactored related components to ensure compatibility with the new store structure and maintain functionality. * Refactor PowerK command handling to remove context dependency - Updated `usePowerKCommands` and `usePowerKCreationCommands` to eliminate the need for a context parameter, simplifying their usage. - Adjusted related command records to utilize the new structure, ensuring consistent access to command configurations. - Enhanced permission checks in creation commands to utilize user project roles for better access control. * chore: add context indicator * chore: update type import * chore: migrate toast implementation from @plane/ui to @plane/propel/toast across multiple command files * refactor: power k modal wrapper and provider * fix: type imports * chore: update creation command shortcuts * fix: page context commands * chore: update navigation and open command shortcuts * fix: work item standalone page modals * fix: context indicator visibility * fix: potential error points * fix: build errors * fix: lint errors * fix: import order --------- Co-authored-by: Vihar Kurama Co-authored-by: Prateek Shourya Co-authored-by: NarayanBavisetti [WEB-5191 | WEB-5197] chore: actions and arrows icon revamp (#7984) [WEB-5296]chore: grouping filter refactor (#8034) [SILO-640] fix: add missing title field in IssueLinkCreateSerializer #8036 fix: storybook build error (#8041) Update test cases path in tR-ci.yml Fix test case path in tR-ci workflow Modified test cases Modified test cases --- .github/workflows/tR-ci.yml | 36 ++ .gitignore | 1 - apps/admin/app/(all)/(home)/auth-banner.tsx | 6 +- apps/api/plane/api/serializers/issue.py | 2 +- apps/api/plane/app/views/search/base.py | 211 +++++-- .../account/auth-forms/auth-banner.tsx | 5 +- .../filters/applied-filters/filters-list.tsx | 8 +- .../issues/filters/applied-filters/label.tsx | 4 +- .../filters/applied-filters/priority.tsx | 5 +- .../issues/filters/applied-filters/state.tsx | 5 +- .../issues/filters/helpers/filter-header.tsx | 7 +- .../components/issues/filters/selection.tsx | 5 +- .../kanban/headers/sub-group-by-card.tsx | 5 +- .../comment/comment-detail-card.tsx | 5 +- .../[workspaceSlug]/(projects)/layout.tsx | 33 +- .../(projects)/profile/[userId]/header.tsx | 6 +- .../profile/[userId]/mobile-header.tsx | 8 +- .../cycles/(detail)/mobile-header.tsx | 6 +- .../issues/(list)/mobile-header.tsx | 4 +- .../modules/(detail)/mobile-header.tsx | 6 +- .../modules/(list)/mobile-header.tsx | 4 +- .../views/(list)/mobile-header.tsx | 5 +- .../[workspaceSlug]/(settings)/layout.tsx | 4 +- .../settings/(workspace)/sidebar.tsx | 4 +- apps/web/app/(all)/layout.tsx | 2 +- apps/web/app/(all)/profile/layout.tsx | 5 +- apps/web/app/(all)/profile/sidebar.tsx | 16 +- .../app/(all)/workspace-invitations/page.tsx | 5 +- .../ce/components/command-palette/helpers.tsx | 2 +- .../ce/components/command-palette/index.ts | 1 - .../command-palette/modals/index.ts | 3 - .../{issue-level.tsx => work-item-level.tsx} | 33 +- .../command-palette/power-k/constants.ts | 6 + .../power-k/context-detector.ts | 5 + .../hooks/use-extended-context-indicator.ts | 8 + .../power-k/pages/context-based/index.ts | 1 + .../power-k/pages/context-based/root.tsx | 11 + .../work-item/state-menu-item.tsx | 34 ++ .../power-k/search/no-results-command.tsx | 36 ++ .../power-k/search/search-results-map.tsx | 10 + .../command-palette/power-k/types.ts | 5 + .../issue-details/sidebar.tsx/date-alert.tsx | 9 + .../components/issues/issue-layouts/utils.tsx | 19 +- .../ce/components/pages/editor/ai/menu.tsx | 5 +- .../ce/components/projects/mobile-header.tsx | 5 +- .../workspace/delete-workspace-section.tsx | 4 +- .../workspace/sidebar/app-search.tsx | 9 +- .../ce/store/issue/helpers/filter-utils.ts | 3 + apps/web/ce/store/power-k.store.ts | 13 + .../account/auth-forms/auth-banner.tsx | 7 +- .../auth-forms/forgot-password-popover.tsx | 4 +- .../account/auth-forms/password.tsx | 5 +- .../analytics/insight-table/data-table.tsx | 7 +- .../analytics/work-items/modal/header.tsx | 5 +- .../command-palette/actions/help-actions.tsx | 85 --- .../command-palette/actions/index.ts | 6 - .../actions/issue-actions/actions-list.tsx | 164 ------ .../actions/issue-actions/change-assignee.tsx | 98 ---- .../actions/issue-actions/change-priority.tsx | 57 -- .../actions/issue-actions/change-state.tsx | 46 -- .../actions/issue-actions/index.ts | 4 - .../actions/project-actions.tsx | 94 ---- .../actions/search-results.tsx | 66 --- .../command-palette/actions/theme-actions.tsx | 65 --- .../actions/workspace-settings-actions.tsx | 60 -- .../command-palette/command-modal.tsx | 492 ----------------- .../command-palette/command-palette.tsx | 270 --------- .../core/components/command-palette/index.ts | 4 - .../shortcuts-modal/commands-list.tsx | 99 ---- .../command-palette/shortcuts-modal/index.ts | 2 - .../components/comments/card/edit-form.tsx | 7 +- .../common/applied-filters/date.tsx | 6 +- .../common/applied-filters/members.tsx | 4 +- .../core/description-versions/modal.tsx | 7 +- .../core/filters/date-filter-modal.tsx | 4 +- .../modals/existing-issues-list-modal.tsx | 14 +- .../analytics-sidebar/issue-progress.tsx | 6 +- .../analytics-sidebar/sidebar-header.tsx | 5 +- .../cycles/applied-filters/date.tsx | 4 +- .../cycles/applied-filters/root.tsx | 6 +- .../cycles/applied-filters/status.tsx | 4 +- .../cycles/archived-cycles/header.tsx | 7 +- .../components/cycles/cycles-view-header.tsx | 5 +- .../cycles/dropdowns/filters/root.tsx | 5 +- .../cycles/list/cycle-list-group-header.tsx | 5 +- .../list/cycle-list-project-group-header.tsx | 4 +- .../cycles/transfer-issues-modal.tsx | 6 +- .../core/components/dropdowns/cycle/index.tsx | 5 +- .../core/components/dropdowns/date-range.tsx | 8 +- apps/web/core/components/dropdowns/date.tsx | 5 +- .../core/components/dropdowns/estimate.tsx | 6 +- .../core/components/dropdowns/member/base.tsx | 6 +- .../dropdowns/module/button-content.tsx | 9 +- .../core/components/dropdowns/priority.tsx | 10 +- .../components/dropdowns/project/base.tsx | 6 +- .../core/components/dropdowns/state/base.tsx | 9 +- .../components/estimates/create/modal.tsx | 4 +- .../components/estimates/points/create.tsx | 5 +- .../components/estimates/points/update.tsx | 5 +- .../home/widgets/empty-states/no-projects.tsx | 6 +- .../home/widgets/recents/filters.tsx | 6 +- .../inbox/content/inbox-issue-header.tsx | 19 +- .../content/inbox-issue-mobile-header.tsx | 19 +- .../inbox-filter/applied-filters/date.tsx | 6 +- .../inbox-filter/applied-filters/label.tsx | 6 +- .../inbox-filter/applied-filters/member.tsx | 7 +- .../inbox-filter/applied-filters/priority.tsx | 7 +- .../inbox-filter/applied-filters/state.tsx | 7 +- .../inbox-filter/applied-filters/status.tsx | 4 +- .../inbox-filter/filters/filter-selection.tsx | 5 +- .../components/inbox/inbox-filter/root.tsx | 7 +- .../inbox/inbox-filter/sorting/order-by.tsx | 5 +- .../issues/attachment/attachment-detail.tsx | 5 +- .../sub-issues/filters.tsx | 5 +- .../sub-issues/issues-list/list-group.tsx | 5 +- .../sub-issues/issues-list/list-item.tsx | 9 +- .../issue-detail/label/create-label.tsx | 7 +- .../issue-detail/label/label-list-item.tsx | 4 +- .../issues/issue-detail/parent-select.tsx | 7 +- .../issues/issue-detail/relation-select.tsx | 5 +- .../issues/issue-detail/sidebar.tsx | 48 +- .../calendar/dropdowns/months-dropdown.tsx | 6 +- .../calendar/dropdowns/options-dropdown.tsx | 5 +- .../issues/issue-layouts/calendar/header.tsx | 6 +- .../filters/applied-filters/cycle.tsx | 5 +- .../filters/applied-filters/date.tsx | 6 +- .../filters/applied-filters/label.tsx | 4 +- .../filters/applied-filters/members.tsx | 4 +- .../filters/applied-filters/module.tsx | 5 +- .../filters/applied-filters/priority.tsx | 5 +- .../filters/applied-filters/project.tsx | 4 +- .../filters/applied-filters/state-group.tsx | 5 +- .../filters/applied-filters/state.tsx | 5 +- .../header/display-filters/group-by.tsx | 6 +- .../filters/header/helpers/dropdown.tsx | 10 +- .../filters/header/helpers/filter-header.tsx | 4 +- .../header/mobile-layout-selection.tsx | 6 +- .../kanban/headers/sub-group-by-card.tsx | 7 +- .../issues/issue-layouts/list/block.tsx | 4 +- .../properties/label-dropdown.tsx | 5 +- .../issue-layouts/spreadsheet/issue-row.tsx | 5 +- .../issue-modal/components/parent-tag.tsx | 6 +- .../issues/peek-overview/properties.tsx | 48 +- .../issues/relations/issue-list-item.tsx | 7 +- .../labels/label-block/label-item-block.tsx | 7 +- .../labels/project-setting-label-group.tsx | 7 +- .../labels/project-setting-label-item.tsx | 7 +- .../analytics-sidebar/issue-progress.tsx | 7 +- .../modules/analytics-sidebar/root.tsx | 18 +- .../modules/applied-filters/date.tsx | 4 +- .../modules/applied-filters/members.tsx | 4 +- .../modules/applied-filters/root.tsx | 8 +- .../modules/applied-filters/status.tsx | 5 +- .../modules/archived-modules/header.tsx | 7 +- .../modules/dropdowns/filters/root.tsx | 5 +- .../components/modules/dropdowns/order-by.tsx | 5 +- .../components/modules/module-view-header.tsx | 5 +- .../web/core/components/onboarding/header.tsx | 5 +- .../components/onboarding/invite-members.tsx | 5 +- .../onboarding/steps/profile/set-password.tsx | 5 +- .../components/onboarding/steps/team/root.tsx | 5 +- .../core/components/onboarding/tour/root.tsx | 5 +- .../pages/editor/toolbar/toolbar.tsx | 5 +- .../pages/list/applied-filters/root.tsx | 8 +- .../components/pages/list/filters/root.tsx | 5 +- .../core/components/pages/list/order-by.tsx | 5 +- .../components/pages/list/search-input.tsx | 7 +- .../helper.tsx => power-k/actions/helper.ts} | 0 .../power-k/config/account-commands.ts | 58 ++ .../components/power-k/config/commands.ts | 29 + .../power-k/config/creation/command.ts | 151 +++++ .../power-k/config/creation/root.ts | 18 + .../power-k/config/help-commands.ts | 82 +++ .../power-k/config/miscellaneous-commands.ts | 62 +++ .../power-k/config/navigation/commands.ts | 519 ++++++++++++++++++ .../power-k/config/navigation/root.ts | 45 ++ .../power-k/config/preferences-commands.ts | 153 ++++++ .../power-k/core/context-detector.ts | 18 + .../core/components/power-k/core/registry.ts | 160 ++++++ .../power-k/core/shortcut-handler.ts | 211 +++++++ .../web/core/components/power-k/core/types.ts | 138 +++++ .../components/power-k/global-shortcuts.tsx | 62 +++ .../power-k/hooks/use-context-indicator.ts | 58 ++ .../core/components/power-k/menus/builder.tsx | 51 ++ .../core/components/power-k/menus/cycles.tsx | 28 + .../components/power-k/menus/empty-state.tsx | 9 + .../core/components/power-k/menus/labels.tsx | 31 ++ .../core/components/power-k/menus/members.tsx | 50 ++ .../core/components/power-k/menus/modules.tsx | 28 + .../components/power-k/menus/projects.tsx | 30 + .../components/power-k/menus/settings.tsx | 30 + .../core/components/power-k/menus/views.tsx | 26 + .../components/power-k/menus/workspaces.tsx | 26 + .../power-k/projects-app-provider.tsx | 91 +++ .../ui/modal/command-item-shortcut-badge.tsx | 109 ++++ .../power-k/ui/modal/command-item.tsx | 42 ++ .../power-k/ui/modal/commands-list.tsx | 49 ++ .../components/power-k/ui/modal/constants.ts | 72 +++ .../power-k/ui/modal/context-indicator.tsx | 46 ++ .../components/power-k/ui/modal/footer.tsx | 34 ++ .../components/power-k/ui/modal/header.tsx | 60 ++ .../power-k/ui/modal/search-menu.tsx | 107 ++++ .../power-k/ui/modal/search-results-map.tsx | 113 ++++ .../power-k/ui/modal/search-results.tsx | 74 +++ .../ui/modal/shortcuts-root.tsx} | 23 +- .../components/power-k/ui/modal/wrapper.tsx | 186 +++++++ .../ui/pages/context-based/cycle/commands.ts | 94 ++++ .../power-k/ui/pages/context-based/index.ts | 31 ++ .../pages/context-based/module/commands.tsx | 160 ++++++ .../ui/pages/context-based/module/index.ts | 1 + .../ui/pages/context-based/module/root.tsx | 50 ++ .../context-based/module/status-menu.tsx | 36 ++ .../ui/pages/context-based/page/commands.ts | 183 ++++++ .../power-k/ui/pages/context-based/root.tsx | 46 ++ .../pages/context-based/work-item/commands.ts | 453 +++++++++++++++ .../context-based/work-item/cycles-menu.tsx | 29 + .../work-item/estimates-menu.tsx | 69 +++ .../ui/pages/context-based/work-item/index.ts | 1 + .../context-based/work-item/labels-menu.tsx | 29 + .../context-based/work-item/modules-menu.tsx | 31 ++ .../work-item/priorities-menu.tsx | 33 ++ .../ui/pages/context-based/work-item/root.tsx | 80 +++ .../context-based/work-item/states-menu.tsx | 43 ++ .../components/power-k/ui/pages/default.tsx | 23 + .../core/components/power-k/ui/pages/index.ts | 1 + .../pages/open-entity/project-cycles-menu.tsx | 33 ++ .../open-entity/project-modules-menu.tsx | 33 ++ .../open-entity/project-settings-menu.tsx | 44 ++ .../pages/open-entity/project-views-menu.tsx | 30 + .../ui/pages/open-entity/projects-menu.tsx | 28 + .../power-k/ui/pages/open-entity/root.tsx | 35 ++ .../power-k/ui/pages/open-entity/shared.ts | 8 + .../open-entity/workspace-settings-menu.tsx | 40 ++ .../ui/pages/open-entity/workspaces-menu.tsx | 26 + .../power-k/ui/pages/preferences/index.ts | 1 + .../ui/pages/preferences/languages-menu.tsx | 25 + .../power-k/ui/pages/preferences/root.tsx | 29 + .../pages/preferences/start-of-week-menu.tsx | 25 + .../ui/pages/preferences/themes-menu.tsx | 37 ++ .../ui/pages/preferences/timezone-menu.tsx | 31 ++ .../core/components/power-k/ui/pages/root.tsx | 32 ++ .../ui/pages/work-item-selection-page.tsx | 163 ++++++ .../power-k/ui/renderer/command.tsx | 71 +++ .../components/power-k/ui/renderer/shared.ts | 25 + .../power-k/ui/renderer/shortcut.tsx | 109 ++++ .../components/power-k/utils/navigation.ts | 20 + apps/web/core/components/profile/form.tsx | 5 +- apps/web/core/components/profile/sidebar.tsx | 5 +- .../components/project-states/group-item.tsx | 6 +- .../project-states/options/delete.tsx | 7 +- .../project/applied-filters/access.tsx | 4 +- .../project/applied-filters/date.tsx | 6 +- .../project/applied-filters/members.tsx | 4 +- .../project-display-filters.tsx | 4 +- .../project/applied-filters/root.tsx | 8 +- .../core/components/project/create/header.tsx | 4 +- .../project/dropdowns/filters/member-list.tsx | 4 +- .../project/dropdowns/filters/root.tsx | 5 +- .../components/project/dropdowns/order-by.tsx | 7 +- .../components/project/multi-select-modal.tsx | 5 +- .../components/project/search-projects.tsx | 5 +- .../project/send-project-invitation-modal.tsx | 9 +- .../settings/archive-project/selection.tsx | 4 +- .../settings/delete-project-section.tsx | 4 +- .../rich-filters/filter-item/close-button.tsx | 4 +- .../core/components/settings/mobile/nav.tsx | 5 +- .../core/components/stickies/action-bar.tsx | 6 +- .../core/components/stickies/modal/search.tsx | 5 +- .../components/stickies/modal/stickies.tsx | 6 +- apps/web/core/components/ui/empty-space.tsx | 9 +- .../views/applied-filters/access.tsx | 6 +- .../components/views/applied-filters/root.tsx | 8 +- .../views/filters/filter-selection.tsx | 5 +- .../components/views/filters/order-by.tsx | 5 +- .../components/views/view-list-header.tsx | 7 +- .../web-hooks/form/delete-section.tsx | 4 +- .../sidebar/filters/applied-filter.tsx | 6 +- .../options/snooze/modal.tsx | 4 +- .../workspace/invite-modal/fields.tsx | 4 +- .../settings/invitations-list-item.tsx | 5 +- .../workspace/settings/members-list.tsx | 4 +- .../sidebar/favorites/favorite-folder.tsx | 6 +- .../sidebar/favorites/favorites-menu.tsx | 5 +- .../workspace/sidebar/help-menu.tsx | 6 +- .../workspace/sidebar/help-section.tsx | 6 +- .../workspace/sidebar/help-section/root.tsx | 6 +- .../workspace/sidebar/projects-list-item.tsx | 6 +- .../workspace/sidebar/projects-list.tsx | 5 +- .../workspace/sidebar/sidebar-menu-items.tsx | 5 +- .../sidebar/workspace-menu-header.tsx | 5 +- .../workspace/sidebar/workspace-menu-root.tsx | 5 +- apps/web/core/hooks/store/use-power-k.ts | 11 + .../core/store/base-command-palette.store.ts | 40 +- apps/web/core/store/base-power-k.store.ts | 93 ++++ .../helpers/issue-filter-helper.store.ts | 6 +- apps/web/core/store/project/project.store.ts | 2 +- apps/web/core/store/root.store.ts | 5 + .../command-palette/modals/index.ts | 1 - .../{command-pallette.css => power-k.css} | 22 +- e2etests/README.md | 46 ++ "e2etests/rules/Log \"user\" to website.txt" | 6 + ...ycleName\" to project \"projectName\".txt" | 9 + ...userName\" to project \"projectName\".txt" | 10 + ...viewName\" to project \"projectName\".txt" | 7 + ...pageName\" on project \"projectName\".txt" | 5 + ...ate user \"user\" then join workplace.txt" | 13 + ...workItem\" on project \"projectName\".txt" | 7 + ...age \"pageName\" from \"projectName\".txt" | 10 + ...ewName\" from project \"projectName\".txt" | 7 + ...rkItem\" from project \"projectName\".txt" | 7 + "e2etests/rules/invite user \"userName\".txt" | 6 + e2etests/rules/log out.txt | 2 + ...m \"workItem\" with status \"status\".txt" | 5 + ...leName\" from project \"projectName\".txt" | 7 + ...erName\" from project \"projectName\".txt" | 10 + ...m then validate they see the work item.txt | 21 + ...hat work items get updated accordingly.txt | 20 + ... to other columns updates their status.txt | 10 + ...s receive notifications on their inbox.txt | 18 + ...alidate that other users can modify it.txt | 20 + ... a view, add work items then drag them.txt | 13 + ...ter work items are tagged as completed.txt | 18 + ...u cant have more than one active cycle.txt | 16 + ...cklinks redirect to the specified page.txt | 15 + ...items by labels is filtering correctly.txt | 27 + packages/editor/package.json | 1 + .../menus/bubble-menu/node-selector.tsx | 5 +- .../extensions/callout/color-selector.tsx | 5 +- .../components/toolbar/alignment.tsx | 4 +- .../components/toolbar/full-screen/modal.tsx | 5 +- .../plugins/drag-handles/color-selector.tsx | 5 +- .../plugins/drag-handles/column/dropdown.tsx | 7 +- .../plugins/drag-handles/row/dropdown.tsx | 7 +- packages/i18n/src/locales/en/translations.ts | 181 ++++++ packages/propel/src/calendar/root.tsx | 4 +- .../src/collapsible/collapsible.stories.tsx | 16 +- .../src/context-menu/context-menu.stories.tsx | 9 +- packages/propel/src/dialog/dialog.stories.tsx | 4 +- .../src/emoji-icon-picker/lucide-icons.tsx | 13 +- .../propel/src/icons/actions/close-icon.tsx | 13 + packages/propel/src/icons/actions/index.ts | 1 + .../propel/src/icons/arrows/chevron-down.tsx | 13 + .../propel/src/icons/arrows/chevron-left.tsx | 13 + .../propel/src/icons/arrows/chevron-right.tsx | 13 + .../propel/src/icons/arrows/chevron-up.tsx | 13 + packages/propel/src/icons/arrows/index.ts | 4 + packages/propel/src/icons/constants.tsx | 12 +- packages/propel/src/icons/icons.stories.tsx | 13 + packages/propel/src/icons/index.ts | 1 + packages/propel/src/icons/registry.ts | 12 + packages/propel/src/menu/menu.tsx | 7 +- .../propel/src/popover/popover.stories.tsx | 4 +- packages/propel/src/toast/toast.tsx | 7 +- packages/ui/src/breadcrumbs/breadcrumbs.tsx | 6 +- packages/ui/src/constants/icons.ts | 13 +- .../ui/src/dropdowns/context-menu/item.tsx | 4 +- packages/ui/src/dropdowns/custom-menu.tsx | 11 +- .../ui/src/dropdowns/custom-search-select.tsx | 7 +- packages/ui/src/dropdowns/custom-select.tsx | 7 +- pnpm-lock.yaml | 11 +- 360 files changed, 7114 insertions(+), 2376 deletions(-) create mode 100644 .github/workflows/tR-ci.yml delete mode 100644 apps/web/ce/components/command-palette/modals/index.ts rename apps/web/ce/components/command-palette/modals/{issue-level.tsx => work-item-level.tsx} (75%) create mode 100644 apps/web/ce/components/command-palette/power-k/constants.ts create mode 100644 apps/web/ce/components/command-palette/power-k/context-detector.ts create mode 100644 apps/web/ce/components/command-palette/power-k/hooks/use-extended-context-indicator.ts create mode 100644 apps/web/ce/components/command-palette/power-k/pages/context-based/index.ts create mode 100644 apps/web/ce/components/command-palette/power-k/pages/context-based/root.tsx create mode 100644 apps/web/ce/components/command-palette/power-k/pages/context-based/work-item/state-menu-item.tsx create mode 100644 apps/web/ce/components/command-palette/power-k/search/no-results-command.tsx create mode 100644 apps/web/ce/components/command-palette/power-k/search/search-results-map.tsx create mode 100644 apps/web/ce/components/command-palette/power-k/types.ts create mode 100644 apps/web/ce/components/issues/issue-details/sidebar.tsx/date-alert.tsx create mode 100644 apps/web/ce/store/issue/helpers/filter-utils.ts create mode 100644 apps/web/ce/store/power-k.store.ts delete mode 100644 apps/web/core/components/command-palette/actions/help-actions.tsx delete mode 100644 apps/web/core/components/command-palette/actions/index.ts delete mode 100644 apps/web/core/components/command-palette/actions/issue-actions/actions-list.tsx delete mode 100644 apps/web/core/components/command-palette/actions/issue-actions/change-assignee.tsx delete mode 100644 apps/web/core/components/command-palette/actions/issue-actions/change-priority.tsx delete mode 100644 apps/web/core/components/command-palette/actions/issue-actions/change-state.tsx delete mode 100644 apps/web/core/components/command-palette/actions/issue-actions/index.ts delete mode 100644 apps/web/core/components/command-palette/actions/project-actions.tsx delete mode 100644 apps/web/core/components/command-palette/actions/search-results.tsx delete mode 100644 apps/web/core/components/command-palette/actions/theme-actions.tsx delete mode 100644 apps/web/core/components/command-palette/actions/workspace-settings-actions.tsx delete mode 100644 apps/web/core/components/command-palette/command-modal.tsx delete mode 100644 apps/web/core/components/command-palette/command-palette.tsx delete mode 100644 apps/web/core/components/command-palette/index.ts delete mode 100644 apps/web/core/components/command-palette/shortcuts-modal/commands-list.tsx delete mode 100644 apps/web/core/components/command-palette/shortcuts-modal/index.ts rename apps/web/core/components/{command-palette/actions/helper.tsx => power-k/actions/helper.ts} (100%) create mode 100644 apps/web/core/components/power-k/config/account-commands.ts create mode 100644 apps/web/core/components/power-k/config/commands.ts create mode 100644 apps/web/core/components/power-k/config/creation/command.ts create mode 100644 apps/web/core/components/power-k/config/creation/root.ts create mode 100644 apps/web/core/components/power-k/config/help-commands.ts create mode 100644 apps/web/core/components/power-k/config/miscellaneous-commands.ts create mode 100644 apps/web/core/components/power-k/config/navigation/commands.ts create mode 100644 apps/web/core/components/power-k/config/navigation/root.ts create mode 100644 apps/web/core/components/power-k/config/preferences-commands.ts create mode 100644 apps/web/core/components/power-k/core/context-detector.ts create mode 100644 apps/web/core/components/power-k/core/registry.ts create mode 100644 apps/web/core/components/power-k/core/shortcut-handler.ts create mode 100644 apps/web/core/components/power-k/core/types.ts create mode 100644 apps/web/core/components/power-k/global-shortcuts.tsx create mode 100644 apps/web/core/components/power-k/hooks/use-context-indicator.ts create mode 100644 apps/web/core/components/power-k/menus/builder.tsx create mode 100644 apps/web/core/components/power-k/menus/cycles.tsx create mode 100644 apps/web/core/components/power-k/menus/empty-state.tsx create mode 100644 apps/web/core/components/power-k/menus/labels.tsx create mode 100644 apps/web/core/components/power-k/menus/members.tsx create mode 100644 apps/web/core/components/power-k/menus/modules.tsx create mode 100644 apps/web/core/components/power-k/menus/projects.tsx create mode 100644 apps/web/core/components/power-k/menus/settings.tsx create mode 100644 apps/web/core/components/power-k/menus/views.tsx create mode 100644 apps/web/core/components/power-k/menus/workspaces.tsx create mode 100644 apps/web/core/components/power-k/projects-app-provider.tsx create mode 100644 apps/web/core/components/power-k/ui/modal/command-item-shortcut-badge.tsx create mode 100644 apps/web/core/components/power-k/ui/modal/command-item.tsx create mode 100644 apps/web/core/components/power-k/ui/modal/commands-list.tsx create mode 100644 apps/web/core/components/power-k/ui/modal/constants.ts create mode 100644 apps/web/core/components/power-k/ui/modal/context-indicator.tsx create mode 100644 apps/web/core/components/power-k/ui/modal/footer.tsx create mode 100644 apps/web/core/components/power-k/ui/modal/header.tsx create mode 100644 apps/web/core/components/power-k/ui/modal/search-menu.tsx create mode 100644 apps/web/core/components/power-k/ui/modal/search-results-map.tsx create mode 100644 apps/web/core/components/power-k/ui/modal/search-results.tsx rename apps/web/core/components/{command-palette/shortcuts-modal/modal.tsx => power-k/ui/modal/shortcuts-root.tsx} (81%) create mode 100644 apps/web/core/components/power-k/ui/modal/wrapper.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/context-based/cycle/commands.ts create mode 100644 apps/web/core/components/power-k/ui/pages/context-based/index.ts create mode 100644 apps/web/core/components/power-k/ui/pages/context-based/module/commands.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/context-based/module/index.ts create mode 100644 apps/web/core/components/power-k/ui/pages/context-based/module/root.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/context-based/module/status-menu.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/context-based/page/commands.ts create mode 100644 apps/web/core/components/power-k/ui/pages/context-based/root.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/context-based/work-item/commands.ts create mode 100644 apps/web/core/components/power-k/ui/pages/context-based/work-item/cycles-menu.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/context-based/work-item/estimates-menu.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/context-based/work-item/index.ts create mode 100644 apps/web/core/components/power-k/ui/pages/context-based/work-item/labels-menu.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/context-based/work-item/modules-menu.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/context-based/work-item/priorities-menu.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/context-based/work-item/root.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/context-based/work-item/states-menu.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/default.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/index.ts create mode 100644 apps/web/core/components/power-k/ui/pages/open-entity/project-cycles-menu.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/open-entity/project-modules-menu.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/open-entity/project-settings-menu.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/open-entity/project-views-menu.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/open-entity/projects-menu.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/open-entity/root.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/open-entity/shared.ts create mode 100644 apps/web/core/components/power-k/ui/pages/open-entity/workspace-settings-menu.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/open-entity/workspaces-menu.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/preferences/index.ts create mode 100644 apps/web/core/components/power-k/ui/pages/preferences/languages-menu.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/preferences/root.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/preferences/start-of-week-menu.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/preferences/themes-menu.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/preferences/timezone-menu.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/root.tsx create mode 100644 apps/web/core/components/power-k/ui/pages/work-item-selection-page.tsx create mode 100644 apps/web/core/components/power-k/ui/renderer/command.tsx create mode 100644 apps/web/core/components/power-k/ui/renderer/shared.ts create mode 100644 apps/web/core/components/power-k/ui/renderer/shortcut.tsx create mode 100644 apps/web/core/components/power-k/utils/navigation.ts create mode 100644 apps/web/core/hooks/store/use-power-k.ts create mode 100644 apps/web/core/store/base-power-k.store.ts delete mode 100644 apps/web/ee/components/command-palette/modals/index.ts rename apps/web/styles/{command-pallette.css => power-k.css} (69%) create mode 100644 e2etests/README.md create mode 100644 "e2etests/rules/Log \"user\" to website.txt" create mode 100644 "e2etests/rules/add a cycle \"cycleName\" to project \"projectName\".txt" create mode 100644 "e2etests/rules/add user \"userName\" to project \"projectName\".txt" create mode 100644 "e2etests/rules/add view \"viewName\" to project \"projectName\".txt" create mode 100644 "e2etests/rules/create page \"pageName\" on project \"projectName\".txt" create mode 100644 "e2etests/rules/create user \"user\" then join workplace.txt" create mode 100644 "e2etests/rules/create work item \"workItem\" on project \"projectName\".txt" create mode 100644 "e2etests/rules/delete page \"pageName\" from \"projectName\".txt" create mode 100644 "e2etests/rules/delete view \"viewName\" from project \"projectName\".txt" create mode 100644 "e2etests/rules/delete work item \"workItem\" from project \"projectName\".txt" create mode 100644 "e2etests/rules/invite user \"userName\".txt" create mode 100644 e2etests/rules/log out.txt create mode 100644 "e2etests/rules/quick add work item \"workItem\" with status \"status\".txt" create mode 100644 "e2etests/rules/remove cycle \"cycleName\" from project \"projectName\".txt" create mode 100644 "e2etests/rules/remove user \"userName\" from project \"projectName\".txt" create mode 100644 e2etests/testcases/Add team member to project,assign a work item to them then validate they see the work item.txt create mode 100644 e2etests/testcases/Change Project ID, verify that work items get updated accordingly.txt create mode 100644 e2etests/testcases/Change view to board, verify that dragging items to other columns updates their status.txt create mode 100644 e2etests/testcases/Comment on a work item, validate that other users receive notifications on their inbox.txt create mode 100644 e2etests/testcases/Create a page, validate that other users can modify it.txt create mode 100644 e2etests/testcases/Create a view, add work items then drag them.txt create mode 100644 e2etests/testcases/Create a work cycle, add work items, validate that progress is being tracked after work items are tagged as completed.txt create mode 100644 e2etests/testcases/Create an active cycle, check that you cant have more than one active cycle.txt create mode 100644 e2etests/testcases/Validate that quicklinks redirect to the specified page.txt create mode 100644 e2etests/testcases/Validate that search work items by labels is filtering correctly.txt create mode 100644 packages/propel/src/icons/actions/close-icon.tsx create mode 100644 packages/propel/src/icons/arrows/chevron-down.tsx create mode 100644 packages/propel/src/icons/arrows/chevron-left.tsx create mode 100644 packages/propel/src/icons/arrows/chevron-right.tsx create mode 100644 packages/propel/src/icons/arrows/chevron-up.tsx create mode 100644 packages/propel/src/icons/arrows/index.ts diff --git a/.github/workflows/tR-ci.yml b/.github/workflows/tR-ci.yml new file mode 100644 index 00000000000..e92ba8a541a --- /dev/null +++ b/.github/workflows/tR-ci.yml @@ -0,0 +1,36 @@ +name : End-to-End testing with tR +on: + push: + branches: [tR-e2e-testing, preview] + pull_request: + branches: [tR-e2e-testing, preview] + workflow_dispatch: + +jobs: + e2e-tests-tR: + runs-on: ubuntu-24.04 + steps: + - name: Set up plane + run: | + curl -fsSL https://prime.plane.so/install/ | sh -s -- --silent --domain=127.0.0.1 + - name: Install testRigor command line tool + run: | + set -e + echo "Installing testrigor-cli..." + npm install -g testrigor-cli + + echo "Verifying testrigor-cli installation..." + if command -v testrigor >/dev/null 2>&1; then + echo "SUCCESS: testrigor-cli is installed" + testrigor --version + else + echo "ERROR: testrigor-cli installation failed" + exit 1 + fi + - uses: 'actions/checkout@v4' + name: 'Checkout' + with: + ref: ${{ inputs.refName != '' && inputs.refName || '' }} + - name: Run testRigor tests + run: | + testrigor test-suite run ${{ vars.SUITE_ID }} --token ${{ secrets.CI_TOKEN }} --test-cases-path e2etests/testcases/**/*.txt --localhost --url http://localhost diff --git a/.gitignore b/.gitignore index 4a756d030f7..31c1ae088d3 100644 --- a/.gitignore +++ b/.gitignore @@ -103,5 +103,4 @@ storybook-static CLAUDE.md AGENTS.md - temp/ diff --git a/apps/admin/app/(all)/(home)/auth-banner.tsx b/apps/admin/app/(all)/(home)/auth-banner.tsx index c0a9a0e9263..bb0adaa4d0b 100644 --- a/apps/admin/app/(all)/(home)/auth-banner.tsx +++ b/apps/admin/app/(all)/(home)/auth-banner.tsx @@ -1,7 +1,9 @@ import type { FC } from "react"; -import { Info, X } from "lucide-react"; +import { Info } from "lucide-react"; // plane constants import type { TAdminAuthErrorInfo } from "@plane/constants"; +// icons +import { CloseIcon } from "@plane/propel/icons"; type TAuthBanner = { bannerData: TAdminAuthErrorInfo | undefined; @@ -22,7 +24,7 @@ export const AuthBanner: FC = (props) => { className="relative ml-auto w-6 h-6 rounded-sm flex justify-center items-center transition-all cursor-pointer hover:bg-custom-primary-100/20 text-custom-primary-100/80" onClick={() => handleBannerData && handleBannerData(undefined)} > - + ); diff --git a/apps/api/plane/api/serializers/issue.py b/apps/api/plane/api/serializers/issue.py index d7fc3e911dc..d86dfa6b6ea 100644 --- a/apps/api/plane/api/serializers/issue.py +++ b/apps/api/plane/api/serializers/issue.py @@ -393,7 +393,7 @@ class IssueLinkCreateSerializer(BaseSerializer): class Meta: model = IssueLink - fields = ["url", "issue_id"] + fields = ["title", "url", "issue_id"] read_only_fields = [ "id", "workspace", diff --git a/apps/api/plane/app/views/search/base.py b/apps/api/plane/app/views/search/base.py index 3942b0a44b5..5309bff554e 100644 --- a/apps/api/plane/app/views/search/base.py +++ b/apps/api/plane/app/views/search/base.py @@ -43,22 +43,25 @@ class GlobalSearchEndpoint(BaseAPIView): also show related workspace if found """ - def filter_workspaces(self, query, slug, project_id, workspace_search): + def filter_workspaces(self, query, _slug, _project_id, _workspace_search): fields = ["name"] q = Q() - for field in fields: - q |= Q(**{f"{field}__icontains": query}) + if query: + for field in fields: + q |= Q(**{f"{field}__icontains": query}) return ( Workspace.objects.filter(q, workspace_member__member=self.request.user) + .order_by("-created_at") .distinct() .values("name", "id", "slug") ) - def filter_projects(self, query, slug, project_id, workspace_search): + def filter_projects(self, query, slug, _project_id, _workspace_search): fields = ["name", "identifier"] q = Q() - for field in fields: - q |= Q(**{f"{field}__icontains": query}) + if query: + for field in fields: + q |= Q(**{f"{field}__icontains": query}) return ( Project.objects.filter( q, @@ -67,6 +70,7 @@ def filter_projects(self, query, slug, project_id, workspace_search): archived_at__isnull=True, workspace__slug=slug, ) + .order_by("-created_at") .distinct() .values("name", "id", "identifier", "workspace__slug") ) @@ -74,14 +78,15 @@ def filter_projects(self, query, slug, project_id, workspace_search): def filter_issues(self, query, slug, project_id, workspace_search): fields = ["name", "sequence_id", "project__identifier"] q = Q() - for field in fields: - if field == "sequence_id": - # Match whole integers only (exclude decimal numbers) - sequences = re.findall(r"\b\d+\b", query) - for sequence_id in sequences: - q |= Q(**{"sequence_id": sequence_id}) - else: - q |= Q(**{f"{field}__icontains": query}) + if query: + for field in fields: + if field == "sequence_id": + # Match whole integers only (exclude decimal numbers) + sequences = re.findall(r"\b\d+\b", query) + for sequence_id in sequences: + q |= Q(**{"sequence_id": sequence_id}) + else: + q |= Q(**{f"{field}__icontains": query}) issues = Issue.issue_objects.filter( q, @@ -106,8 +111,9 @@ def filter_issues(self, query, slug, project_id, workspace_search): def filter_cycles(self, query, slug, project_id, workspace_search): fields = ["name"] q = Q() - for field in fields: - q |= Q(**{f"{field}__icontains": query}) + if query: + for field in fields: + q |= Q(**{f"{field}__icontains": query}) cycles = Cycle.objects.filter( q, @@ -120,13 +126,20 @@ def filter_cycles(self, query, slug, project_id, workspace_search): if workspace_search == "false" and project_id: cycles = cycles.filter(project_id=project_id) - return cycles.distinct().values("name", "id", "project_id", "project__identifier", "workspace__slug") + return ( + cycles.order_by("-created_at") + .distinct() + .values( + "name", "id", "project_id", "project__identifier", "workspace__slug" + ) + ) def filter_modules(self, query, slug, project_id, workspace_search): fields = ["name"] q = Q() - for field in fields: - q |= Q(**{f"{field}__icontains": query}) + if query: + for field in fields: + q |= Q(**{f"{field}__icontains": query}) modules = Module.objects.filter( q, @@ -139,13 +152,20 @@ def filter_modules(self, query, slug, project_id, workspace_search): if workspace_search == "false" and project_id: modules = modules.filter(project_id=project_id) - return modules.distinct().values("name", "id", "project_id", "project__identifier", "workspace__slug") + return ( + modules.order_by("-created_at") + .distinct() + .values( + "name", "id", "project_id", "project__identifier", "workspace__slug" + ) + ) def filter_pages(self, query, slug, project_id, workspace_search): fields = ["name"] q = Q() - for field in fields: - q |= Q(**{f"{field}__icontains": query}) + if query: + for field in fields: + q |= Q(**{f"{field}__icontains": query}) pages = ( Page.objects.filter( @@ -157,7 +177,9 @@ def filter_pages(self, query, slug, project_id, workspace_search): ) .annotate( project_ids=Coalesce( - ArrayAgg("projects__id", distinct=True, filter=~Q(projects__id=True)), + ArrayAgg( + "projects__id", distinct=True, filter=~Q(projects__id=True) + ), Value([], output_field=ArrayField(UUIDField())), ) ) @@ -174,19 +196,28 @@ def filter_pages(self, query, slug, project_id, workspace_search): ) if workspace_search == "false" and project_id: - project_subquery = ProjectPage.objects.filter(page_id=OuterRef("id"), project_id=project_id).values_list( - "project_id", flat=True - )[:1] + project_subquery = ProjectPage.objects.filter( + page_id=OuterRef("id"), project_id=project_id + ).values_list("project_id", flat=True)[:1] - pages = pages.annotate(project_id=Subquery(project_subquery)).filter(project_id=project_id) + pages = pages.annotate(project_id=Subquery(project_subquery)).filter( + project_id=project_id + ) - return pages.distinct().values("name", "id", "project_ids", "project_identifiers", "workspace__slug") + return ( + pages.order_by("-created_at") + .distinct() + .values( + "name", "id", "project_ids", "project_identifiers", "workspace__slug" + ) + ) def filter_views(self, query, slug, project_id, workspace_search): fields = ["name"] q = Q() - for field in fields: - q |= Q(**{f"{field}__icontains": query}) + if query: + for field in fields: + q |= Q(**{f"{field}__icontains": query}) issue_views = IssueView.objects.filter( q, @@ -199,29 +230,57 @@ def filter_views(self, query, slug, project_id, workspace_search): if workspace_search == "false" and project_id: issue_views = issue_views.filter(project_id=project_id) - return issue_views.distinct().values("name", "id", "project_id", "project__identifier", "workspace__slug") + return ( + issue_views.order_by("-created_at") + .distinct() + .values( + "name", "id", "project_id", "project__identifier", "workspace__slug" + ) + ) + + def filter_intakes(self, query, slug, project_id, workspace_search): + fields = ["name", "sequence_id", "project__identifier"] + q = Q() + if query: + for field in fields: + if field == "sequence_id": + # Match whole integers only (exclude decimal numbers) + sequences = re.findall(r"\b\d+\b", query) + for sequence_id in sequences: + q |= Q(**{"sequence_id": sequence_id}) + else: + q |= Q(**{f"{field}__icontains": query}) + + issues = Issue.objects.filter( + q, + project__project_projectmember__member=self.request.user, + project__project_projectmember__is_active=True, + project__archived_at__isnull=True, + workspace__slug=slug, + ).filter(models.Q(issue_intake__status=0) | models.Q(issue_intake__status=-2)) + + if workspace_search == "false" and project_id: + issues = issues.filter(project_id=project_id) + + return ( + issues.order_by("-created_at") + .distinct() + .values( + "name", + "id", + "sequence_id", + "project__identifier", + "project_id", + "workspace__slug", + )[:100] + ) def get(self, request, slug): query = request.query_params.get("search", False) + entities_param = request.query_params.get("entities") workspace_search = request.query_params.get("workspace_search", "false") project_id = request.query_params.get("project_id", False) - if not query: - return Response( - { - "results": { - "workspace": [], - "project": [], - "issue": [], - "cycle": [], - "module": [], - "issue_view": [], - "page": [], - } - }, - status=status.HTTP_200_OK, - ) - MODELS_MAPPER = { "workspace": self.filter_workspaces, "project": self.filter_projects, @@ -230,13 +289,27 @@ def get(self, request, slug): "module": self.filter_modules, "issue_view": self.filter_views, "page": self.filter_pages, + "intake": self.filter_intakes, } + # Determine which entities to search + if entities_param: + requested_entities = [ + e.strip() for e in entities_param.split(",") if e.strip() + ] + requested_entities = [e for e in requested_entities if e in MODELS_MAPPER] + else: + requested_entities = list(MODELS_MAPPER.keys()) + results = {} - for model in MODELS_MAPPER.keys(): - func = MODELS_MAPPER.get(model, None) - results[model] = func(query, slug, project_id, workspace_search) + for entity in requested_entities: + func = MODELS_MAPPER.get(entity) + if func: + results[entity] = func( + query or None, slug, project_id, workspace_search + ) + return Response({"results": results}, status=status.HTTP_200_OK) @@ -316,12 +389,15 @@ def get(self, request, slug): projects = ( Project.objects.filter( q, - Q(project_projectmember__member=self.request.user) | Q(network=2), + Q(project_projectmember__member=self.request.user) + | Q(network=2), workspace__slug=slug, ) .order_by("-created_at") .distinct() - .values("name", "id", "identifier", "logo_props", "workspace__slug")[:count] + .values( + "name", "id", "identifier", "logo_props", "workspace__slug" + )[:count] ) response_data["project"] = list(projects) @@ -380,16 +456,20 @@ def get(self, request, slug): .annotate( status=Case( When( - Q(start_date__lte=timezone.now()) & Q(end_date__gte=timezone.now()), + Q(start_date__lte=timezone.now()) + & Q(end_date__gte=timezone.now()), then=Value("CURRENT"), ), When( start_date__gt=timezone.now(), then=Value("UPCOMING"), ), - When(end_date__lt=timezone.now(), then=Value("COMPLETED")), When( - Q(start_date__isnull=True) & Q(end_date__isnull=True), + end_date__lt=timezone.now(), then=Value("COMPLETED") + ), + When( + Q(start_date__isnull=True) + & Q(end_date__isnull=True), then=Value("DRAFT"), ), default=Value("DRAFT"), @@ -507,7 +587,9 @@ def get(self, request, slug): ) ) .order_by("-created_at") - .values("member__avatar_url", "member__display_name", "member__id")[:count] + .values( + "member__avatar_url", "member__display_name", "member__id" + )[:count] ) response_data["user_mention"] = list(users) @@ -521,12 +603,15 @@ def get(self, request, slug): projects = ( Project.objects.filter( q, - Q(project_projectmember__member=self.request.user) | Q(network=2), + Q(project_projectmember__member=self.request.user) + | Q(network=2), workspace__slug=slug, ) .order_by("-created_at") .distinct() - .values("name", "id", "identifier", "logo_props", "workspace__slug")[:count] + .values( + "name", "id", "identifier", "logo_props", "workspace__slug" + )[:count] ) response_data["project"] = list(projects) @@ -583,16 +668,20 @@ def get(self, request, slug): .annotate( status=Case( When( - Q(start_date__lte=timezone.now()) & Q(end_date__gte=timezone.now()), + Q(start_date__lte=timezone.now()) + & Q(end_date__gte=timezone.now()), then=Value("CURRENT"), ), When( start_date__gt=timezone.now(), then=Value("UPCOMING"), ), - When(end_date__lt=timezone.now(), then=Value("COMPLETED")), When( - Q(start_date__isnull=True) & Q(end_date__isnull=True), + end_date__lt=timezone.now(), then=Value("COMPLETED") + ), + When( + Q(start_date__isnull=True) + & Q(end_date__isnull=True), then=Value("DRAFT"), ), default=Value("DRAFT"), diff --git a/apps/space/core/components/account/auth-forms/auth-banner.tsx b/apps/space/core/components/account/auth-forms/auth-banner.tsx index 30cd6e09338..feda960e715 100644 --- a/apps/space/core/components/account/auth-forms/auth-banner.tsx +++ b/apps/space/core/components/account/auth-forms/auth-banner.tsx @@ -1,7 +1,8 @@ "use client"; import type { FC } from "react"; -import { Info, X } from "lucide-react"; +import { Info } from "lucide-react"; +import { CloseIcon } from "@plane/propel/icons"; // helpers import type { TAuthErrorInfo } from "@/helpers/authentication.helper"; @@ -24,7 +25,7 @@ export const AuthBanner: FC = (props) => { className="relative ml-auto w-6 h-6 rounded-sm flex justify-center items-center transition-all cursor-pointer hover:bg-custom-primary-100/20 text-custom-primary-100/80" onClick={() => handleBannerData && handleBannerData(undefined)} > - + ); diff --git a/apps/space/core/components/issues/filters/applied-filters/filters-list.tsx b/apps/space/core/components/issues/filters/applied-filters/filters-list.tsx index cb542facef2..bffc7c553a1 100644 --- a/apps/space/core/components/issues/filters/applied-filters/filters-list.tsx +++ b/apps/space/core/components/issues/filters/applied-filters/filters-list.tsx @@ -1,9 +1,9 @@ "use client"; import { observer } from "mobx-react"; -import { X } from "lucide-react"; -// types import { useTranslation } from "@plane/i18n"; +import { CloseIcon } from "@plane/propel/icons"; +// types import type { TFilters } from "@/types/issue"; // components import { AppliedPriorityFilters } from "./priority"; @@ -55,7 +55,7 @@ export const AppliedFiltersList: React.FC = observer((props) => { className="grid place-items-center text-custom-text-300 hover:text-custom-text-200" onClick={() => handleRemoveFilter(filterKey, null)} > - + @@ -67,7 +67,7 @@ export const AppliedFiltersList: React.FC = observer((props) => { className="flex items-center gap-2 rounded-md border border-custom-border-200 px-2 py-1 text-xs text-custom-text-300 hover:text-custom-text-200" > {t("common.clear_all")} - + ); diff --git a/apps/space/core/components/issues/filters/applied-filters/label.tsx b/apps/space/core/components/issues/filters/applied-filters/label.tsx index 5abbd54bac1..2b842c152e2 100644 --- a/apps/space/core/components/issues/filters/applied-filters/label.tsx +++ b/apps/space/core/components/issues/filters/applied-filters/label.tsx @@ -1,6 +1,6 @@ "use client"; -import { X } from "lucide-react"; +import { CloseIcon } from "@plane/propel/icons"; // types import type { IIssueLabel } from "@/types/issue"; @@ -34,7 +34,7 @@ export const AppliedLabelsFilters: React.FC = (props) => { className="grid place-items-center text-custom-text-300 hover:text-custom-text-200" onClick={() => handleRemove(labelId)} > - + ); diff --git a/apps/space/core/components/issues/filters/applied-filters/priority.tsx b/apps/space/core/components/issues/filters/applied-filters/priority.tsx index a687cb67c5c..da151873f93 100644 --- a/apps/space/core/components/issues/filters/applied-filters/priority.tsx +++ b/apps/space/core/components/issues/filters/applied-filters/priority.tsx @@ -1,7 +1,6 @@ "use client"; -import { X } from "lucide-react"; -import { PriorityIcon } from "@plane/propel/icons"; +import { CloseIcon, PriorityIcon } from "@plane/propel/icons"; import type { TIssuePriorities } from "@plane/propel/icons"; type Props = { @@ -25,7 +24,7 @@ export const AppliedPriorityFilters: React.FC = (props) => { className="grid place-items-center text-custom-text-300 hover:text-custom-text-200" onClick={() => handleRemove(priority)} > - + ))} diff --git a/apps/space/core/components/issues/filters/applied-filters/state.tsx b/apps/space/core/components/issues/filters/applied-filters/state.tsx index c80c8688a58..1836c86a769 100644 --- a/apps/space/core/components/issues/filters/applied-filters/state.tsx +++ b/apps/space/core/components/issues/filters/applied-filters/state.tsx @@ -1,10 +1,9 @@ "use client"; import { observer } from "mobx-react"; -import { X } from "lucide-react"; // plane imports import { EIconSize } from "@plane/constants"; -import { StateGroupIcon } from "@plane/propel/icons"; +import { CloseIcon, StateGroupIcon } from "@plane/propel/icons"; // hooks import { useStates } from "@/hooks/store/use-state"; @@ -34,7 +33,7 @@ export const AppliedStateFilters: React.FC = observer((props) => { className="grid place-items-center text-custom-text-300 hover:text-custom-text-200" onClick={() => handleRemove(stateId)} > - + ); diff --git a/apps/space/core/components/issues/filters/helpers/filter-header.tsx b/apps/space/core/components/issues/filters/helpers/filter-header.tsx index 52d76651608..4505efbdb00 100644 --- a/apps/space/core/components/issues/filters/helpers/filter-header.tsx +++ b/apps/space/core/components/issues/filters/helpers/filter-header.tsx @@ -1,8 +1,7 @@ "use client"; - import React from "react"; -// lucide icons -import { ChevronDown, ChevronUp } from "lucide-react"; +// icons +import { ChevronDownIcon, ChevronUpIcon } from "@plane/propel/icons"; interface IFilterHeader { title: string; @@ -18,7 +17,7 @@ export const FilterHeader = ({ title, isPreviewEnabled, handleIsPreviewEnabled } className="grid h-5 w-5 flex-shrink-0 place-items-center rounded hover:bg-custom-background-80" onClick={handleIsPreviewEnabled} > - {isPreviewEnabled ? : } + {isPreviewEnabled ? : } ); diff --git a/apps/space/core/components/issues/filters/selection.tsx b/apps/space/core/components/issues/filters/selection.tsx index 3042a419d6a..221e9df8cfa 100644 --- a/apps/space/core/components/issues/filters/selection.tsx +++ b/apps/space/core/components/issues/filters/selection.tsx @@ -2,7 +2,8 @@ import React, { useState } from "react"; import { observer } from "mobx-react"; -import { Search, X } from "lucide-react"; +import { Search } from "lucide-react"; +import { CloseIcon } from "@plane/propel/icons"; // types import type { IIssueFilterOptions, TIssueFilterKeys } from "@/types/issue"; // local imports @@ -37,7 +38,7 @@ export const FilterSelection: React.FC = observer((props) => { /> {filtersSearchQuery !== "" && ( )} diff --git a/apps/space/core/components/issues/issue-layouts/kanban/headers/sub-group-by-card.tsx b/apps/space/core/components/issues/issue-layouts/kanban/headers/sub-group-by-card.tsx index fd7ba5f0df0..e656ee2540c 100644 --- a/apps/space/core/components/issues/issue-layouts/kanban/headers/sub-group-by-card.tsx +++ b/apps/space/core/components/issues/issue-layouts/kanban/headers/sub-group-by-card.tsx @@ -1,7 +1,8 @@ import type { FC } from "react"; import React from "react"; import { observer } from "mobx-react"; -import { Circle, ChevronDown, ChevronUp } from "lucide-react"; +import { Circle } from "lucide-react"; +import { ChevronDownIcon, ChevronUpIcon } from "@plane/propel/icons"; // mobx interface IHeaderSubGroupByCard { @@ -20,7 +21,7 @@ export const HeaderSubGroupByCard: FC = observer((props) onClick={() => toggleExpanded()} >
- {isExpanded ? : } + {isExpanded ? : }
diff --git a/apps/space/core/components/issues/peek-overview/comment/comment-detail-card.tsx b/apps/space/core/components/issues/peek-overview/comment/comment-detail-card.tsx index 6e97ed6eaba..c33c1789bd1 100644 --- a/apps/space/core/components/issues/peek-overview/comment/comment-detail-card.tsx +++ b/apps/space/core/components/issues/peek-overview/comment/comment-detail-card.tsx @@ -1,10 +1,11 @@ import React, { useRef, useState } from "react"; import { observer } from "mobx-react"; import { Controller, useForm } from "react-hook-form"; -import { Check, MessageSquare, MoreVertical, X } from "lucide-react"; +import { Check, MessageSquare, MoreVertical } from "lucide-react"; import { Menu, Transition } from "@headlessui/react"; // plane imports import type { EditorRefApi } from "@plane/editor"; +import { CloseIcon } from "@plane/propel/icons"; import type { TIssuePublicComment } from "@plane/types"; import { getFileURL } from "@plane/utils"; // components @@ -136,7 +137,7 @@ export const CommentCard: React.FC = observer((props) => { className="group rounded border border-red-500 bg-red-500/20 p-2 shadow-md duration-300 hover:bg-red-500" onClick={() => setIsEditing(false)} > - +
diff --git a/apps/web/app/(all)/[workspaceSlug]/(projects)/layout.tsx b/apps/web/app/(all)/[workspaceSlug]/(projects)/layout.tsx index 3b486684938..9e381c9030c 100644 --- a/apps/web/app/(all)/[workspaceSlug]/(projects)/layout.tsx +++ b/apps/web/app/(all)/[workspaceSlug]/(projects)/layout.tsx @@ -1,26 +1,33 @@ "use client"; -import { CommandPalette } from "@/components/command-palette"; +import { observer } from "mobx-react"; +import { ProjectsAppPowerKProvider } from "@/components/power-k/projects-app-provider"; import { AuthenticationWrapper } from "@/lib/wrappers/authentication-wrapper"; // plane web components import { WorkspaceAuthWrapper } from "@/plane-web/layouts/workspace-wrapper"; import { ProjectAppSidebar } from "./_sidebar"; +const WorkspaceLayoutContent = observer(({ children }: { children: React.ReactNode }) => ( + <> + + +
+
+
+ +
+ {children} +
+
+
+ + +)); + export default function WorkspaceLayout({ children }: { children: React.ReactNode }) { return ( - - -
-
-
- -
- {children} -
-
-
- + {children} ); } diff --git a/apps/web/app/(all)/[workspaceSlug]/(projects)/profile/[userId]/header.tsx b/apps/web/app/(all)/[workspaceSlug]/(projects)/profile/[userId]/header.tsx index ef49c8ffb0d..cee770d8072 100644 --- a/apps/web/app/(all)/[workspaceSlug]/(projects)/profile/[userId]/header.tsx +++ b/apps/web/app/(all)/[workspaceSlug]/(projects)/profile/[userId]/header.tsx @@ -4,10 +4,10 @@ import type { FC } from "react"; import { observer } from "mobx-react"; import { useParams, useRouter } from "next/navigation"; -import { ChevronDown, PanelRight } from "lucide-react"; +import { PanelRight } from "lucide-react"; import { PROFILE_VIEWER_TAB, PROFILE_ADMINS_TAB, EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; -import { YourWorkIcon } from "@plane/propel/icons"; +import { YourWorkIcon, ChevronDownIcon } from "@plane/propel/icons"; import type { IUserProfileProjectSegregation } from "@plane/types"; import { Breadcrumbs, Header, CustomMenu } from "@plane/ui"; import { cn } from "@plane/utils"; @@ -75,7 +75,7 @@ export const UserProfileHeader: FC = observer((props) => { customButton={
{type} - +
} customButtonClassName="flex flex-grow justify-center text-custom-text-200 text-sm" diff --git a/apps/web/app/(all)/[workspaceSlug]/(projects)/profile/[userId]/mobile-header.tsx b/apps/web/app/(all)/[workspaceSlug]/(projects)/profile/[userId]/mobile-header.tsx index 5b59b39d49b..c6d4e341054 100644 --- a/apps/web/app/(all)/[workspaceSlug]/(projects)/profile/[userId]/mobile-header.tsx +++ b/apps/web/app/(all)/[workspaceSlug]/(projects)/profile/[userId]/mobile-header.tsx @@ -3,12 +3,12 @@ import { useCallback } from "react"; import { observer } from "mobx-react"; import { useParams } from "next/navigation"; -// icons -import { ChevronDown } from "lucide-react"; // plane constants import { EIssueFilterType, ISSUE_LAYOUTS, ISSUE_DISPLAY_FILTERS_BY_PAGE } from "@plane/constants"; // plane i18n import { useTranslation } from "@plane/i18n"; +// icons +import { ChevronDownIcon } from "@plane/propel/icons"; // types import type { IIssueDisplayFilterOptions, @@ -88,7 +88,7 @@ export const ProfileIssuesMobileHeader = observer(() => { customButton={
{t("common.layout")} - +
} customButtonClassName="flex flex-center text-custom-text-200 text-sm" @@ -117,7 +117,7 @@ export const ProfileIssuesMobileHeader = observer(() => { menuButton={
{t("common.display")} - +
} > diff --git a/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(detail)/mobile-header.tsx b/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(detail)/mobile-header.tsx index 747e65c1660..75c0a9114db 100644 --- a/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(detail)/mobile-header.tsx +++ b/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(detail)/mobile-header.tsx @@ -3,12 +3,10 @@ import { useCallback, useState } from "react"; import { observer } from "mobx-react"; import { useParams } from "next/navigation"; -// icons -import { ChevronDown } from "lucide-react"; // plane imports import { EIssueFilterType, ISSUE_LAYOUTS, ISSUE_DISPLAY_FILTERS_BY_PAGE } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; -import { CalendarLayoutIcon, BoardLayoutIcon, ListLayoutIcon } from "@plane/propel/icons"; +import { CalendarLayoutIcon, BoardLayoutIcon, ListLayoutIcon, ChevronDownIcon } from "@plane/propel/icons"; import type { IIssueDisplayFilterOptions, IIssueDisplayProperties, EIssueLayoutTypes } from "@plane/types"; import { EIssuesStoreType } from "@plane/types"; import { CustomMenu } from "@plane/ui"; @@ -125,7 +123,7 @@ export const CycleIssuesMobileHeader = observer(() => { menuButton={ {t("common.display")} - + } > diff --git a/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/issues/(list)/mobile-header.tsx b/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/issues/(list)/mobile-header.tsx index ed827135a7b..c714cfaf1f9 100644 --- a/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/issues/(list)/mobile-header.tsx +++ b/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/issues/(list)/mobile-header.tsx @@ -3,10 +3,10 @@ import { useCallback, useState } from "react"; import { observer } from "mobx-react"; import { useParams } from "next/navigation"; -import { ChevronDown } from "lucide-react"; // plane imports import { EIssueFilterType, ISSUE_DISPLAY_FILTERS_BY_PAGE } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; +import { ChevronDownIcon } from "@plane/propel/icons"; import type { IIssueDisplayFilterOptions, IIssueDisplayProperties } from "@plane/types"; import { EIssuesStoreType, EIssueLayoutTypes } from "@plane/types"; // components @@ -79,7 +79,7 @@ export const ProjectIssuesMobileHeader = observer(() => { menuButton={ {t("common.display")} - + } > diff --git a/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/modules/(detail)/mobile-header.tsx b/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/modules/(detail)/mobile-header.tsx index ae450f5de67..6be0088d633 100644 --- a/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/modules/(detail)/mobile-header.tsx +++ b/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/modules/(detail)/mobile-header.tsx @@ -3,12 +3,10 @@ import { useCallback, useState } from "react"; import { observer } from "mobx-react"; import { useParams } from "next/navigation"; -// icons -import { ChevronDown } from "lucide-react"; // plane imports import { EIssueFilterType, ISSUE_LAYOUTS, ISSUE_DISPLAY_FILTERS_BY_PAGE } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; -import { CalendarLayoutIcon, BoardLayoutIcon, ListLayoutIcon } from "@plane/propel/icons"; +import { CalendarLayoutIcon, BoardLayoutIcon, ListLayoutIcon, ChevronDownIcon } from "@plane/propel/icons"; import type { IIssueDisplayFilterOptions, IIssueDisplayProperties, EIssueLayoutTypes } from "@plane/types"; import { EIssuesStoreType } from "@plane/types"; import { CustomMenu } from "@plane/ui"; @@ -109,7 +107,7 @@ export const ModuleIssuesMobileHeader = observer(() => { menuButton={ Display - + } > diff --git a/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/modules/(list)/mobile-header.tsx b/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/modules/(list)/mobile-header.tsx index 138867e4f05..94b19045748 100644 --- a/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/modules/(list)/mobile-header.tsx +++ b/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/modules/(list)/mobile-header.tsx @@ -1,9 +1,9 @@ "use client"; import { observer } from "mobx-react"; -import { ChevronDown } from "lucide-react"; import { MODULE_VIEW_LAYOUTS } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; +import { ChevronDownIcon } from "@plane/propel/icons"; import { CustomMenu, Row } from "@plane/ui"; import { ModuleLayoutIcon } from "@/components/modules"; import { useModuleFilter } from "@/hooks/store/use-module-filter"; @@ -22,7 +22,7 @@ export const ModulesListMobileHeader = observer(() => { // placement="bottom-start" customButton={ - Layout + Layout } customButtonClassName="flex flex-grow justify-center items-center text-custom-text-200 text-sm" diff --git a/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/views/(list)/mobile-header.tsx b/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/views/(list)/mobile-header.tsx index e9ef19d1f94..3980e977fe7 100644 --- a/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/views/(list)/mobile-header.tsx +++ b/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/views/(list)/mobile-header.tsx @@ -2,7 +2,8 @@ import { observer } from "mobx-react"; // icons -import { ChevronDown, ListFilter } from "lucide-react"; +import { ListFilter } from "lucide-react"; +import { ChevronDownIcon } from "@plane/propel/icons"; // components import { Row } from "@plane/ui"; import { FiltersDropdown } from "@/components/issues/issue-layouts/filters"; @@ -42,7 +43,7 @@ export const ViewMobileHeader = observer(() => { menuButton={ Filters - + } > diff --git a/apps/web/app/(all)/[workspaceSlug]/(settings)/layout.tsx b/apps/web/app/(all)/[workspaceSlug]/(settings)/layout.tsx index a87d4d26767..a42ae9cf890 100644 --- a/apps/web/app/(all)/[workspaceSlug]/(settings)/layout.tsx +++ b/apps/web/app/(all)/[workspaceSlug]/(settings)/layout.tsx @@ -1,7 +1,7 @@ "use client"; -import { CommandPalette } from "@/components/command-palette"; import { ContentWrapper } from "@/components/core/content-wrapper"; +import { ProjectsAppPowerKProvider } from "@/components/power-k/projects-app-provider"; import { SettingsHeader } from "@/components/settings/header"; import { AuthenticationWrapper } from "@/lib/wrappers/authentication-wrapper"; import { WorkspaceAuthWrapper } from "@/plane-web/layouts/workspace-wrapper"; @@ -10,7 +10,7 @@ export default function SettingsLayout({ children }: { children: React.ReactNode return ( - +
{/* Header */} diff --git a/apps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar.tsx b/apps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar.tsx index bda42ccc398..bcf088dd26c 100644 --- a/apps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar.tsx +++ b/apps/web/app/(all)/[workspaceSlug]/(settings)/settings/(workspace)/sidebar.tsx @@ -12,7 +12,7 @@ import { SettingsSidebar } from "@/components/settings/sidebar"; import { useUserPermissions } from "@/hooks/store/user"; import { shouldRenderSettingLink } from "@/plane-web/helpers/workspace.helper"; -const ICONS = { +export const WORKSPACE_SETTINGS_ICONS = { general: Building, members: Users, export: ArrowUpToLine, @@ -30,7 +30,7 @@ export const WorkspaceActionIcons = ({ className?: string; }) => { if (type === undefined) return null; - const Icon = ICONS[type as keyof typeof ICONS]; + const Icon = WORKSPACE_SETTINGS_ICONS[type as keyof typeof WORKSPACE_SETTINGS_ICONS]; if (!Icon) return null; return ; }; diff --git a/apps/web/app/(all)/layout.tsx b/apps/web/app/(all)/layout.tsx index 2775b1b33f7..ee6c5750da9 100644 --- a/apps/web/app/(all)/layout.tsx +++ b/apps/web/app/(all)/layout.tsx @@ -3,7 +3,7 @@ import type { Metadata, Viewport } from "next"; import { PreloadResources } from "./layout.preload"; // styles -import "@/styles/command-pallette.css"; +import "@/styles/power-k.css"; import "@/styles/emoji.css"; import "@plane/propel/styles/react-day-picker"; diff --git a/apps/web/app/(all)/profile/layout.tsx b/apps/web/app/(all)/profile/layout.tsx index fdc0867654c..aee7779a032 100644 --- a/apps/web/app/(all)/profile/layout.tsx +++ b/apps/web/app/(all)/profile/layout.tsx @@ -1,9 +1,8 @@ "use client"; import type { ReactNode } from "react"; -// components -import { CommandPalette } from "@/components/command-palette"; // wrappers +import { ProjectsAppPowerKProvider } from "@/components/power-k/projects-app-provider"; import { AuthenticationWrapper } from "@/lib/wrappers/authentication-wrapper"; // layout import { ProfileLayoutSidebar } from "./sidebar"; @@ -17,7 +16,7 @@ export default function ProfileSettingsLayout(props: Props) { return ( <> - +
diff --git a/apps/web/app/(all)/profile/sidebar.tsx b/apps/web/app/(all)/profile/sidebar.tsx index acf25bb1806..4619b186fa1 100644 --- a/apps/web/app/(all)/profile/sidebar.tsx +++ b/apps/web/app/(all)/profile/sidebar.tsx @@ -5,22 +5,12 @@ import { observer } from "mobx-react"; import Link from "next/link"; import { usePathname } from "next/navigation"; // icons -import { - ChevronLeft, - LogOut, - MoveLeft, - Activity, - Bell, - CircleUser, - KeyRound, - Settings2, - CirclePlus, - Mails, -} from "lucide-react"; +import { LogOut, MoveLeft, Activity, Bell, CircleUser, KeyRound, Settings2, CirclePlus, Mails } from "lucide-react"; // plane imports import { PROFILE_ACTION_LINKS } from "@plane/constants"; import { useOutsideClickDetector } from "@plane/hooks"; import { useTranslation } from "@plane/i18n"; +import { ChevronLeftIcon } from "@plane/propel/icons"; import { TOAST_TYPE, setToast } from "@plane/propel/toast"; import { Tooltip } from "@plane/propel/tooltip"; import { cn, getFileURL } from "@plane/utils"; @@ -141,7 +131,7 @@ export const ProfileLayoutSidebar = observer(() => { }`} > - + {!sidebarCollapsed && (

{t("profile_settings")}

diff --git a/apps/web/app/(all)/workspace-invitations/page.tsx b/apps/web/app/(all)/workspace-invitations/page.tsx index 6f9d78d56f8..7883b08c621 100644 --- a/apps/web/app/(all)/workspace-invitations/page.tsx +++ b/apps/web/app/(all)/workspace-invitations/page.tsx @@ -4,7 +4,8 @@ import React from "react"; import { observer } from "mobx-react"; import { useSearchParams } from "next/navigation"; import useSWR from "swr"; -import { Boxes, Check, Share2, Star, User2, X } from "lucide-react"; +import { Boxes, Check, Share2, Star, User2 } from "lucide-react"; +import { CloseIcon } from "@plane/propel/icons"; // components import { LogoSpinner } from "@/components/common/logo-spinner"; import { EmptySpace, EmptySpaceItem } from "@/components/ui/empty-space"; @@ -85,7 +86,7 @@ const WorkspaceInvitationPage = observer(() => { description="Your workspace is where you'll create projects, collaborate on your work items, and organize different streams of work in your Plane account." > - + ) ) : error || invitationDetail?.responded_at ? ( diff --git a/apps/web/ce/components/command-palette/helpers.tsx b/apps/web/ce/components/command-palette/helpers.tsx index 865aa9e53fe..1ad7e6f4983 100644 --- a/apps/web/ce/components/command-palette/helpers.tsx +++ b/apps/web/ce/components/command-palette/helpers.tsx @@ -93,7 +93,7 @@ export const commandGroups: TCommandGroups = { if (!!projectId && page?.project_ids?.includes(projectId)) redirectProjectId = projectId; return redirectProjectId ? `/${page?.workspace__slug}/projects/${redirectProjectId}/pages/${page?.id}` - : `/${page?.workspace__slug}/pages/${page?.id}`; + : `/${page?.workspace__slug}/wiki/${page?.id}`; }, title: "Pages", }, diff --git a/apps/web/ce/components/command-palette/index.ts b/apps/web/ce/components/command-palette/index.ts index 62404249d75..cb220b2bd92 100644 --- a/apps/web/ce/components/command-palette/index.ts +++ b/apps/web/ce/components/command-palette/index.ts @@ -1,3 +1,2 @@ export * from "./actions"; -export * from "./modals"; export * from "./helpers"; diff --git a/apps/web/ce/components/command-palette/modals/index.ts b/apps/web/ce/components/command-palette/modals/index.ts deleted file mode 100644 index a4fac4b91ef..00000000000 --- a/apps/web/ce/components/command-palette/modals/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./workspace-level"; -export * from "./project-level"; -export * from "./issue-level"; diff --git a/apps/web/ce/components/command-palette/modals/issue-level.tsx b/apps/web/ce/components/command-palette/modals/work-item-level.tsx similarity index 75% rename from apps/web/ce/components/command-palette/modals/issue-level.tsx rename to apps/web/ce/components/command-palette/modals/work-item-level.tsx index f720e38ea35..d06602d7e25 100644 --- a/apps/web/ce/components/command-palette/modals/issue-level.tsx +++ b/apps/web/ce/components/command-palette/modals/work-item-level.tsx @@ -15,21 +15,23 @@ import { useUser } from "@/hooks/store/user"; import { useAppRouter } from "@/hooks/use-app-router"; import { useIssuesActions } from "@/hooks/use-issues-actions"; -export type TIssueLevelModalsProps = { - projectId: string | undefined; - issueId: string | undefined; +export type TWorkItemLevelModalsProps = { + workItemIdentifier: string | undefined; }; -export const IssueLevelModals: FC = observer((props) => { - const { projectId, issueId } = props; +export const WorkItemLevelModals: FC = observer((props) => { + const { workItemIdentifier } = props; // router const { workspaceSlug, cycleId, moduleId } = useParams(); const router = useAppRouter(); // store hooks const { data: currentUser } = useUser(); const { - issue: { getIssueById }, + issue: { getIssueById, getIssueIdByIdentifier }, } = useIssueDetail(); + // derived values + const workItemId = workItemIdentifier ? getIssueIdByIdentifier(workItemIdentifier) : undefined; + const workItemDetails = workItemId ? getIssueById(workItemId) : undefined; const { removeIssue: removeEpic } = useIssuesActions(EIssuesStoreType.EPIC); const { removeIssue: removeWorkItem } = useIssuesActions(EIssuesStoreType.PROJECT); @@ -44,13 +46,12 @@ export const IssueLevelModals: FC = observer((props) => createWorkItemAllowedProjectIds, } = useCommandPalette(); // derived values - const issueDetails = issueId ? getIssueById(issueId) : undefined; const { fetchSubIssues: fetchSubWorkItems } = useIssueDetail(); const { fetchSubIssues: fetchEpicSubWorkItems } = useIssueDetail(EIssueServiceType.EPICS); const handleDeleteIssue = async (workspaceSlug: string, projectId: string, issueId: string) => { try { - const isEpic = issueDetails?.is_epic; + const isEpic = workItemDetails?.is_epic; const deleteAction = isEpic ? removeEpic : removeWorkItem; const redirectPath = `/${workspaceSlug}/projects/${projectId}/${isEpic ? "epics" : "issues"}`; @@ -62,10 +63,10 @@ export const IssueLevelModals: FC = observer((props) => }; const handleCreateIssueSubmit = async (newIssue: TIssue) => { - if (!workspaceSlug || !newIssue.project_id || !newIssue.id || newIssue.parent_id !== issueDetails?.id) return; + if (!workspaceSlug || !newIssue.project_id || !newIssue.id || newIssue.parent_id !== workItemDetails?.id) return; - const fetchAction = issueDetails?.is_epic ? fetchEpicSubWorkItems : fetchSubWorkItems; - await fetchAction(workspaceSlug?.toString(), newIssue.project_id, issueDetails.id); + const fetchAction = workItemDetails?.is_epic ? fetchEpicSubWorkItems : fetchSubWorkItems; + await fetchAction(workspaceSlug?.toString(), newIssue.project_id, workItemDetails.id); }; const getCreateIssueModalData = () => { @@ -83,13 +84,15 @@ export const IssueLevelModals: FC = observer((props) => onSubmit={handleCreateIssueSubmit} allowedProjectIds={createWorkItemAllowedProjectIds} /> - {workspaceSlug && projectId && issueId && issueDetails && ( + {workspaceSlug && workItemId && workItemDetails && workItemDetails.project_id && ( toggleDeleteIssueModal(false)} isOpen={isDeleteIssueModalOpen} - data={issueDetails} - onSubmit={() => handleDeleteIssue(workspaceSlug.toString(), projectId?.toString(), issueId?.toString())} - isEpic={issueDetails?.is_epic} + data={workItemDetails} + onSubmit={() => + handleDeleteIssue(workspaceSlug.toString(), workItemDetails.project_id!, workItemId?.toString()) + } + isEpic={workItemDetails?.is_epic} /> )} = {}; diff --git a/apps/web/ce/components/command-palette/power-k/context-detector.ts b/apps/web/ce/components/command-palette/power-k/context-detector.ts new file mode 100644 index 00000000000..acc803bdc87 --- /dev/null +++ b/apps/web/ce/components/command-palette/power-k/context-detector.ts @@ -0,0 +1,5 @@ +import type { Params } from "next/dist/shared/lib/router/utils/route-matcher"; +// local imports +import type { TPowerKContextTypeExtended } from "./types"; + +export const detectExtendedContextFromURL = (_params: Params): TPowerKContextTypeExtended | null => null; diff --git a/apps/web/ce/components/command-palette/power-k/hooks/use-extended-context-indicator.ts b/apps/web/ce/components/command-palette/power-k/hooks/use-extended-context-indicator.ts new file mode 100644 index 00000000000..ad5f43860b9 --- /dev/null +++ b/apps/web/ce/components/command-palette/power-k/hooks/use-extended-context-indicator.ts @@ -0,0 +1,8 @@ +// local imports +import type { TPowerKContextTypeExtended } from "../types"; + +type TArgs = { + activeContext: TPowerKContextTypeExtended | null; +}; + +export const useExtendedContextIndicator = (_args: TArgs): string | null => null; diff --git a/apps/web/ce/components/command-palette/power-k/pages/context-based/index.ts b/apps/web/ce/components/command-palette/power-k/pages/context-based/index.ts new file mode 100644 index 00000000000..1efe34c51ec --- /dev/null +++ b/apps/web/ce/components/command-palette/power-k/pages/context-based/index.ts @@ -0,0 +1 @@ +export * from "./root"; diff --git a/apps/web/ce/components/command-palette/power-k/pages/context-based/root.tsx b/apps/web/ce/components/command-palette/power-k/pages/context-based/root.tsx new file mode 100644 index 00000000000..2c6c0e8913d --- /dev/null +++ b/apps/web/ce/components/command-palette/power-k/pages/context-based/root.tsx @@ -0,0 +1,11 @@ +// components +import type { TPowerKCommandConfig } from "@/components/power-k/core/types"; +import type { ContextBasedActionsProps, TContextEntityMap } from "@/components/power-k/ui/pages/context-based"; +// local imports +import type { TPowerKContextTypeExtended } from "../../types"; + +export const CONTEXT_ENTITY_MAP_EXTENDED: Record = {}; + +export const PowerKContextBasedActionsExtended: React.FC = () => null; + +export const usePowerKContextBasedExtendedActions = (): TPowerKCommandConfig[] => []; diff --git a/apps/web/ce/components/command-palette/power-k/pages/context-based/work-item/state-menu-item.tsx b/apps/web/ce/components/command-palette/power-k/pages/context-based/work-item/state-menu-item.tsx new file mode 100644 index 00000000000..5fbc91edf8b --- /dev/null +++ b/apps/web/ce/components/command-palette/power-k/pages/context-based/work-item/state-menu-item.tsx @@ -0,0 +1,34 @@ +"use client"; + +import { observer } from "mobx-react"; +// plane types +import { StateGroupIcon } from "@plane/propel/icons"; +import type { IState } from "@plane/types"; +// components +import { PowerKModalCommandItem } from "@/components/power-k/ui/modal/command-item"; + +export type TPowerKProjectStatesMenuItemsProps = { + handleSelect: (stateId: string) => void; + projectId: string | undefined; + selectedStateId: string | undefined; + states: IState[]; + workspaceSlug: string; +}; + +export const PowerKProjectStatesMenuItems: React.FC = observer((props) => { + const { handleSelect, selectedStateId, states } = props; + + return ( + <> + {states.map((state) => ( + } + label={state.name} + isSelected={state.id === selectedStateId} + onSelect={() => handleSelect(state.id)} + /> + ))} + + ); +}); diff --git a/apps/web/ce/components/command-palette/power-k/search/no-results-command.tsx b/apps/web/ce/components/command-palette/power-k/search/no-results-command.tsx new file mode 100644 index 00000000000..cc8ca10d513 --- /dev/null +++ b/apps/web/ce/components/command-palette/power-k/search/no-results-command.tsx @@ -0,0 +1,36 @@ +import { Command } from "cmdk"; +import { Search } from "lucide-react"; +// plane imports +import { useTranslation } from "@plane/i18n"; +// components +import type { TPowerKContext } from "@/components/power-k/core/types"; +// plane web imports +import { PowerKModalCommandItem } from "@/components/power-k/ui/modal/command-item"; + +export type TPowerKModalNoSearchResultsCommandProps = { + context: TPowerKContext; + searchTerm: string; + updateSearchTerm: (value: string) => void; +}; + +export const PowerKModalNoSearchResultsCommand: React.FC = (props) => { + const { updateSearchTerm } = props; + // translation + const { t } = useTranslation(); + + return ( + + + {t("power_k.search_menu.no_results")}{" "} + {t("power_k.search_menu.clear_search")} +

+ } + onSelect={() => updateSearchTerm("")} + /> +
+ ); +}; diff --git a/apps/web/ce/components/command-palette/power-k/search/search-results-map.tsx b/apps/web/ce/components/command-palette/power-k/search/search-results-map.tsx new file mode 100644 index 00000000000..c09dd41a10b --- /dev/null +++ b/apps/web/ce/components/command-palette/power-k/search/search-results-map.tsx @@ -0,0 +1,10 @@ +"use client"; + +// components +import type { TPowerKSearchResultGroupDetails } from "@/components/power-k/ui/modal/search-results-map"; +// local imports +import type { TPowerKSearchResultsKeysExtended } from "../types"; + +type TSearchResultsGroupsMapExtended = Record; + +export const SEARCH_RESULTS_GROUPS_MAP_EXTENDED: TSearchResultsGroupsMapExtended = {}; diff --git a/apps/web/ce/components/command-palette/power-k/types.ts b/apps/web/ce/components/command-palette/power-k/types.ts new file mode 100644 index 00000000000..4e497f8b87a --- /dev/null +++ b/apps/web/ce/components/command-palette/power-k/types.ts @@ -0,0 +1,5 @@ +export type TPowerKContextTypeExtended = never; + +export type TPowerKPageTypeExtended = never; + +export type TPowerKSearchResultsKeysExtended = never; diff --git a/apps/web/ce/components/issues/issue-details/sidebar.tsx/date-alert.tsx b/apps/web/ce/components/issues/issue-details/sidebar.tsx/date-alert.tsx new file mode 100644 index 00000000000..930d4594966 --- /dev/null +++ b/apps/web/ce/components/issues/issue-details/sidebar.tsx/date-alert.tsx @@ -0,0 +1,9 @@ +import type { TIssue } from "@plane/types"; + +export type TDateAlertProps = { + date: string; + workItem: TIssue; + projectId: string; +}; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export const DateAlert = (props: TDateAlertProps) => <>; diff --git a/apps/web/ce/components/issues/issue-layouts/utils.tsx b/apps/web/ce/components/issues/issue-layouts/utils.tsx index 2d2a1deb8c1..82a26a00c69 100644 --- a/apps/web/ce/components/issues/issue-layouts/utils.tsx +++ b/apps/web/ce/components/issues/issue-layouts/utils.tsx @@ -1,6 +1,7 @@ import type { FC } from "react"; import { CalendarDays, LayersIcon, Link2, Paperclip } from "lucide-react"; // types +import { ISSUE_GROUP_BY_OPTIONS } from "@plane/constants"; import type { ISvgIcons } from "@plane/propel/icons"; import { CycleIcon, @@ -13,7 +14,13 @@ import { PriorityPropertyIcon, StartDatePropertyIcon, } from "@plane/propel/icons"; -import type { IGroupByColumn, IIssueDisplayProperties, TGetColumns, TSpreadsheetColumn } from "@plane/types"; +import type { + IGroupByColumn, + IIssueDisplayProperties, + TGetColumns, + TIssueGroupByOptions, + TSpreadsheetColumn, +} from "@plane/types"; // components import { SpreadsheetAssigneeColumn, @@ -96,3 +103,13 @@ export const SPREADSHEET_COLUMNS: { [key in keyof IIssueDisplayProperties]: TSpr updated_on: SpreadsheetUpdatedOnColumn, attachment_count: SpreadsheetAttachmentColumn, }; + +export const useGroupByOptions = ( + options: TIssueGroupByOptions[] +): { + key: TIssueGroupByOptions; + titleTranslationKey: string; +}[] => { + const groupByOptions = ISSUE_GROUP_BY_OPTIONS.filter((option) => options.includes(option.key)); + return groupByOptions; +}; diff --git a/apps/web/ce/components/pages/editor/ai/menu.tsx b/apps/web/ce/components/pages/editor/ai/menu.tsx index 109af79743e..033c8ff88e3 100644 --- a/apps/web/ce/components/pages/editor/ai/menu.tsx +++ b/apps/web/ce/components/pages/editor/ai/menu.tsx @@ -2,9 +2,10 @@ import React, { useEffect, useRef, useState } from "react"; import type { LucideIcon } from "lucide-react"; -import { ChevronRight, CornerDownRight, RefreshCcw, Sparkles, TriangleAlert } from "lucide-react"; +import { CornerDownRight, RefreshCcw, Sparkles, TriangleAlert } from "lucide-react"; // plane editor import type { EditorRefApi } from "@plane/editor"; +import { ChevronRightIcon } from "@plane/propel/icons"; // plane ui import { Tooltip } from "@plane/propel/tooltip"; // components @@ -174,7 +175,7 @@ export const EditorAIMenu: React.FC = (props) => { {item.label} - {
{t("common.filters")} - +
} isFiltersApplied={isFiltersApplied} diff --git a/apps/web/ce/components/workspace/delete-workspace-section.tsx b/apps/web/ce/components/workspace/delete-workspace-section.tsx index aa72fdc32bd..a9975b8f598 100644 --- a/apps/web/ce/components/workspace/delete-workspace-section.tsx +++ b/apps/web/ce/components/workspace/delete-workspace-section.tsx @@ -1,11 +1,11 @@ import type { FC } from "react"; import { useState } from "react"; import { observer } from "mobx-react"; -import { ChevronDown, ChevronUp } from "lucide-react"; // types import { WORKSPACE_TRACKER_ELEMENTS } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; import { Button } from "@plane/propel/button"; +import { ChevronDownIcon, ChevronUpIcon } from "@plane/propel/icons"; import type { IWorkspace } from "@plane/types"; // ui import { Collapsible } from "@plane/ui"; @@ -42,7 +42,7 @@ export const DeleteWorkspaceSection: FC = observer((props) => {t("workspace_settings.settings.general.delete_workspace")} - {isOpen ? : } + {isOpen ? : } } > diff --git a/apps/web/ce/components/workspace/sidebar/app-search.tsx b/apps/web/ce/components/workspace/sidebar/app-search.tsx index 9e0f4cd9557..89d2607cf80 100644 --- a/apps/web/ce/components/workspace/sidebar/app-search.tsx +++ b/apps/web/ce/components/workspace/sidebar/app-search.tsx @@ -1,20 +1,21 @@ import { observer } from "mobx-react"; // plane imports import { useTranslation } from "@plane/i18n"; -// hooks +// components import { SidebarSearchButton } from "@/components/sidebar/search-button"; -import { useCommandPalette } from "@/hooks/store/use-command-palette"; +// hooks +import { usePowerK } from "@/hooks/store/use-power-k"; export const AppSearch = observer(() => { // store hooks - const { toggleCommandPaletteModal } = useCommandPalette(); + const { togglePowerKModal } = usePowerK(); // translation const { t } = useTranslation(); return (
); diff --git a/apps/web/core/components/account/auth-forms/forgot-password-popover.tsx b/apps/web/core/components/account/auth-forms/forgot-password-popover.tsx index 3b243b8942a..3e47dbe1c77 100644 --- a/apps/web/core/components/account/auth-forms/forgot-password-popover.tsx +++ b/apps/web/core/components/account/auth-forms/forgot-password-popover.tsx @@ -1,9 +1,9 @@ import { Fragment, useState } from "react"; import { usePopper } from "react-popper"; -import { X } from "lucide-react"; import { Popover } from "@headlessui/react"; // plane imports import { useTranslation } from "@plane/i18n"; +import { CloseIcon } from "@plane/propel/icons"; export const ForgotPasswordPopover = () => { // popper-js refs @@ -51,7 +51,7 @@ export const ForgotPasswordPopover = () => { onClick={() => close()} aria-label={t("aria_labels.auth_forms.close_popover")} > - +
)} diff --git a/apps/web/core/components/account/auth-forms/password.tsx b/apps/web/core/components/account/auth-forms/password.tsx index 52d32dc5d4d..5fb6a4276d1 100644 --- a/apps/web/core/components/account/auth-forms/password.tsx +++ b/apps/web/core/components/account/auth-forms/password.tsx @@ -4,11 +4,12 @@ import React, { useEffect, useMemo, useRef, useState } from "react"; import { observer } from "mobx-react"; import Link from "next/link"; // icons -import { Eye, EyeOff, Info, X, XCircle } from "lucide-react"; +import { Eye, EyeOff, Info, XCircle } from "lucide-react"; // plane imports import { API_BASE_URL, E_PASSWORD_STRENGTH, AUTH_TRACKER_EVENTS, AUTH_TRACKER_ELEMENTS } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; import { Button } from "@plane/propel/button"; +import { CloseIcon } from "@plane/propel/icons"; import { Input, PasswordStrengthIndicator, Spinner } from "@plane/ui"; import { getPasswordStrength } from "@plane/utils"; // components @@ -134,7 +135,7 @@ export const AuthPasswordForm: React.FC = observer((props: Props) => { className="relative ml-auto w-6 h-6 rounded-sm flex justify-center items-center transition-all cursor-pointer hover:bg-red-500/20 text-custom-primary-100/80" onClick={() => setBannerMessage(false)} > - +
)} diff --git a/apps/web/core/components/analytics/insight-table/data-table.tsx b/apps/web/core/components/analytics/insight-table/data-table.tsx index 83892ceb403..8c646879394 100644 --- a/apps/web/core/components/analytics/insight-table/data-table.tsx +++ b/apps/web/core/components/analytics/insight-table/data-table.tsx @@ -17,10 +17,11 @@ import { getSortedRowModel, useReactTable, } from "@tanstack/react-table"; -import { Search, X } from "lucide-react"; -// plane package imports +import { Search } from "lucide-react"; import { useTranslation } from "@plane/i18n"; import { EmptyStateCompact } from "@plane/propel/empty-state"; +import { CloseIcon } from "@plane/propel/icons"; +// plane package imports import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@plane/propel/table"; import { cn } from "@plane/utils"; // plane web components @@ -116,7 +117,7 @@ export function DataTable({ columns, data, searchPlaceholder, act setIsSearchOpen(false); }} > - + )} diff --git a/apps/web/core/components/analytics/work-items/modal/header.tsx b/apps/web/core/components/analytics/work-items/modal/header.tsx index 734eebbf87e..22f1a14c389 100644 --- a/apps/web/core/components/analytics/work-items/modal/header.tsx +++ b/apps/web/core/components/analytics/work-items/modal/header.tsx @@ -1,6 +1,7 @@ import { observer } from "mobx-react"; // plane package imports -import { Expand, Shrink, X } from "lucide-react"; +import { Expand, Shrink } from "lucide-react"; +import { CloseIcon } from "@plane/propel/icons"; import type { ICycle, IModule } from "@plane/types"; // icons @@ -34,7 +35,7 @@ export const WorkItemsModalHeader: React.FC = observer((props) => { className="grid place-items-center p-1 text-custom-text-200 hover:text-custom-text-100" onClick={handleClose} > - + diff --git a/apps/web/core/components/command-palette/actions/help-actions.tsx b/apps/web/core/components/command-palette/actions/help-actions.tsx deleted file mode 100644 index ba7c5b35365..00000000000 --- a/apps/web/core/components/command-palette/actions/help-actions.tsx +++ /dev/null @@ -1,85 +0,0 @@ -"use client"; -import { Command } from "cmdk"; -import { observer } from "mobx-react"; -import { GithubIcon, MessageSquare, Rocket } from "lucide-react"; -// ui -import { DiscordIcon, PageIcon } from "@plane/propel/icons"; -// hooks -import { useCommandPalette } from "@/hooks/store/use-command-palette"; -import { useTransient } from "@/hooks/store/use-transient"; - -type Props = { - closePalette: () => void; -}; - -export const CommandPaletteHelpActions: React.FC = observer((props) => { - const { closePalette } = props; - // hooks - const { toggleShortcutModal } = useCommandPalette(); - const { toggleIntercom } = useTransient(); - - return ( - - { - closePalette(); - toggleShortcutModal(true); - }} - className="focus:outline-none" - > -
- - Open keyboard shortcuts -
-
- { - closePalette(); - window.open("https://docs.plane.so/", "_blank"); - }} - className="focus:outline-none" - > -
- - Open Plane documentation -
-
- { - closePalette(); - window.open("https://discord.com/invite/A92xrEGCge", "_blank"); - }} - className="focus:outline-none" - > -
- - Join our Discord -
-
- { - closePalette(); - window.open("https://github.com/makeplane/plane/issues/new/choose", "_blank"); - }} - className="focus:outline-none" - > -
- - Report a bug -
-
- { - closePalette(); - toggleIntercom(true); - }} - className="focus:outline-none" - > -
- - Chat with us -
-
-
- ); -}); diff --git a/apps/web/core/components/command-palette/actions/index.ts b/apps/web/core/components/command-palette/actions/index.ts deleted file mode 100644 index 7c3af470e4d..00000000000 --- a/apps/web/core/components/command-palette/actions/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export * from "./issue-actions"; -export * from "./help-actions"; -export * from "./project-actions"; -export * from "./search-results"; -export * from "./theme-actions"; -export * from "./workspace-settings-actions"; diff --git a/apps/web/core/components/command-palette/actions/issue-actions/actions-list.tsx b/apps/web/core/components/command-palette/actions/issue-actions/actions-list.tsx deleted file mode 100644 index eefe3dabc80..00000000000 --- a/apps/web/core/components/command-palette/actions/issue-actions/actions-list.tsx +++ /dev/null @@ -1,164 +0,0 @@ -"use client"; - -import { Command } from "cmdk"; -import { observer } from "mobx-react"; -import { useParams } from "next/navigation"; -import { LinkIcon, Trash2, UserMinus2, UserPlus2 } from "lucide-react"; -import { StatePropertyIcon, PriorityPropertyIcon, MembersPropertyIcon } from "@plane/propel/icons"; -import { TOAST_TYPE, setToast } from "@plane/propel/toast"; -import type { TIssue } from "@plane/types"; -import { EIssueServiceType } from "@plane/types"; -// hooks -// helpers -import { copyTextToClipboard } from "@plane/utils"; -// hooks -import { useCommandPalette } from "@/hooks/store/use-command-palette"; -import { useIssueDetail } from "@/hooks/store/use-issue-detail"; -import { useUser } from "@/hooks/store/user"; - -type Props = { - closePalette: () => void; - issueDetails: TIssue | undefined; - pages: string[]; - setPages: (pages: string[]) => void; - setPlaceholder: (placeholder: string) => void; - setSearchTerm: (searchTerm: string) => void; -}; - -export const CommandPaletteIssueActions: React.FC = observer((props) => { - const { closePalette, issueDetails, pages, setPages, setPlaceholder, setSearchTerm } = props; - // router - const { workspaceSlug } = useParams(); - // hooks - const { updateIssue } = useIssueDetail(issueDetails?.is_epic ? EIssueServiceType.EPICS : EIssueServiceType.ISSUES); - const { toggleCommandPaletteModal, toggleDeleteIssueModal } = useCommandPalette(); - const { data: currentUser } = useUser(); - // derived values - const issueId = issueDetails?.id; - const projectId = issueDetails?.project_id; - - const handleUpdateIssue = async (formData: Partial) => { - if (!workspaceSlug || !projectId || !issueDetails) return; - - const payload = { ...formData }; - await updateIssue(workspaceSlug.toString(), projectId.toString(), issueDetails.id, payload).catch((e) => { - console.error(e); - }); - }; - - const handleIssueAssignees = (assignee: string) => { - if (!issueDetails || !assignee) return; - - closePalette(); - const updatedAssignees = issueDetails.assignee_ids ?? []; - - if (updatedAssignees.includes(assignee)) updatedAssignees.splice(updatedAssignees.indexOf(assignee), 1); - else updatedAssignees.push(assignee); - - handleUpdateIssue({ assignee_ids: updatedAssignees }); - }; - - const deleteIssue = () => { - toggleCommandPaletteModal(false); - toggleDeleteIssueModal(true); - }; - - const copyIssueUrlToClipboard = () => { - if (!issueId) return; - - const url = new URL(window.location.href); - copyTextToClipboard(url.href) - .then(() => { - setToast({ type: TOAST_TYPE.SUCCESS, title: "Copied to clipboard" }); - }) - .catch(() => { - setToast({ type: TOAST_TYPE.ERROR, title: "Some error occurred" }); - }); - }; - - const actionHeading = issueDetails?.is_epic ? "Epic actions" : "Work item actions"; - const entityType = issueDetails?.is_epic ? "epic" : "work item"; - - return ( - - { - setPlaceholder("Change state..."); - setSearchTerm(""); - setPages([...pages, "change-issue-state"]); - }} - className="focus:outline-none" - > -
- - Change state... -
-
- { - setPlaceholder("Change priority..."); - setSearchTerm(""); - setPages([...pages, "change-issue-priority"]); - }} - className="focus:outline-none" - > -
- - Change priority... -
-
- { - setPlaceholder("Assign to..."); - setSearchTerm(""); - setPages([...pages, "change-issue-assignee"]); - }} - className="focus:outline-none" - > -
- - Assign to... -
-
- { - handleIssueAssignees(currentUser?.id ?? ""); - setSearchTerm(""); - }} - className="focus:outline-none" - > -
- {issueDetails?.assignee_ids.includes(currentUser?.id ?? "") ? ( - <> - - Un-assign from me - - ) : ( - <> - - Assign to me - - )} -
-
- -
- - {`Delete ${entityType}`} -
-
- { - closePalette(); - copyIssueUrlToClipboard(); - }} - className="focus:outline-none" - > -
- - {`Copy ${entityType} URL`} -
-
-
- ); -}); diff --git a/apps/web/core/components/command-palette/actions/issue-actions/change-assignee.tsx b/apps/web/core/components/command-palette/actions/issue-actions/change-assignee.tsx deleted file mode 100644 index d68f437764b..00000000000 --- a/apps/web/core/components/command-palette/actions/issue-actions/change-assignee.tsx +++ /dev/null @@ -1,98 +0,0 @@ -"use client"; - -import { Command } from "cmdk"; -import { observer } from "mobx-react"; -import { useParams } from "next/navigation"; -import { Check } from "lucide-react"; -// plane types -import type { TIssue } from "@plane/types"; -import { EIssueServiceType } from "@plane/types"; -// plane ui -import { Avatar } from "@plane/ui"; -// helpers -import { getFileURL } from "@plane/utils"; -// hooks -import { useIssueDetail } from "@/hooks/store/use-issue-detail"; -import { useMember } from "@/hooks/store/use-member"; - -type Props = { closePalette: () => void; issue: TIssue }; - -export const ChangeIssueAssignee: React.FC = observer((props) => { - const { closePalette, issue } = props; - // router params - const { workspaceSlug } = useParams(); - // store - const { updateIssue } = useIssueDetail(issue?.is_epic ? EIssueServiceType.EPICS : EIssueServiceType.ISSUES); - const { - project: { getProjectMemberIds, getProjectMemberDetails }, - } = useMember(); - // derived values - const projectId = issue?.project_id ?? ""; - const projectMemberIds = getProjectMemberIds(projectId, false); - - const options = - projectMemberIds - ?.map((userId) => { - if (!projectId) return; - const memberDetails = getProjectMemberDetails(userId, projectId.toString()); - - return { - value: `${memberDetails?.member?.id}`, - query: `${memberDetails?.member?.display_name}`, - content: ( - <> -
- - {memberDetails?.member?.display_name} -
- {issue.assignee_ids.includes(memberDetails?.member?.id ?? "") && ( -
- -
- )} - - ), - }; - }) - .filter((o) => o !== undefined) ?? []; - - const handleUpdateIssue = async (formData: Partial) => { - if (!workspaceSlug || !projectId || !issue) return; - - const payload = { ...formData }; - await updateIssue(workspaceSlug.toString(), projectId.toString(), issue.id, payload).catch((e) => { - console.error(e); - }); - }; - - const handleIssueAssignees = (assignee: string) => { - const updatedAssignees = issue.assignee_ids ?? []; - - if (updatedAssignees.includes(assignee)) updatedAssignees.splice(updatedAssignees.indexOf(assignee), 1); - else updatedAssignees.push(assignee); - - handleUpdateIssue({ assignee_ids: updatedAssignees }); - closePalette(); - }; - - return ( - <> - {options.map( - (option) => - option && ( - handleIssueAssignees(option.value)} - className="focus:outline-none" - > - {option.content} - - ) - )} - - ); -}); diff --git a/apps/web/core/components/command-palette/actions/issue-actions/change-priority.tsx b/apps/web/core/components/command-palette/actions/issue-actions/change-priority.tsx deleted file mode 100644 index fe983faaae1..00000000000 --- a/apps/web/core/components/command-palette/actions/issue-actions/change-priority.tsx +++ /dev/null @@ -1,57 +0,0 @@ -"use client"; - -import { Command } from "cmdk"; -import { observer } from "mobx-react"; -import { useParams } from "next/navigation"; -import { Check } from "lucide-react"; -// plane constants -import { ISSUE_PRIORITIES } from "@plane/constants"; -// plane types -import { PriorityIcon } from "@plane/propel/icons"; -import type { TIssue, TIssuePriorities } from "@plane/types"; -import { EIssueServiceType } from "@plane/types"; -// mobx store -import { useIssueDetail } from "@/hooks/store/use-issue-detail"; -// ui -// types -// constants - -type Props = { closePalette: () => void; issue: TIssue }; - -export const ChangeIssuePriority: React.FC = observer((props) => { - const { closePalette, issue } = props; - // router params - const { workspaceSlug } = useParams(); - // store hooks - const { updateIssue } = useIssueDetail(issue?.is_epic ? EIssueServiceType.EPICS : EIssueServiceType.ISSUES); - // derived values - const projectId = issue?.project_id; - - const submitChanges = async (formData: Partial) => { - if (!workspaceSlug || !projectId || !issue) return; - - const payload = { ...formData }; - await updateIssue(workspaceSlug.toString(), projectId.toString(), issue.id, payload).catch((e) => { - console.error(e); - }); - }; - - const handleIssueState = (priority: TIssuePriorities) => { - submitChanges({ priority }); - closePalette(); - }; - - return ( - <> - {ISSUE_PRIORITIES.map((priority) => ( - handleIssueState(priority.key)} className="focus:outline-none"> -
- - {priority.title ?? "None"} -
-
{priority.key === issue.priority && }
-
- ))} - - ); -}); diff --git a/apps/web/core/components/command-palette/actions/issue-actions/change-state.tsx b/apps/web/core/components/command-palette/actions/issue-actions/change-state.tsx deleted file mode 100644 index 336eb231bf0..00000000000 --- a/apps/web/core/components/command-palette/actions/issue-actions/change-state.tsx +++ /dev/null @@ -1,46 +0,0 @@ -"use client"; - -import { observer } from "mobx-react"; -import { useParams } from "next/navigation"; -// plane imports -import type { TIssue } from "@plane/types"; -import { EIssueServiceType } from "@plane/types"; -// store hooks -import { useIssueDetail } from "@/hooks/store/use-issue-detail"; -// plane web imports -import { ChangeWorkItemStateList } from "@/plane-web/components/command-palette/actions/work-item-actions"; - -type Props = { closePalette: () => void; issue: TIssue }; - -export const ChangeIssueState: React.FC = observer((props) => { - const { closePalette, issue } = props; - // router params - const { workspaceSlug } = useParams(); - // store hooks - const { updateIssue } = useIssueDetail(issue?.is_epic ? EIssueServiceType.EPICS : EIssueServiceType.ISSUES); - // derived values - const projectId = issue?.project_id; - const currentStateId = issue?.state_id; - - const submitChanges = async (formData: Partial) => { - if (!workspaceSlug || !projectId || !issue) return; - - const payload = { ...formData }; - await updateIssue(workspaceSlug.toString(), projectId.toString(), issue.id, payload).catch((e) => { - console.error(e); - }); - }; - - const handleIssueState = (stateId: string) => { - submitChanges({ state_id: stateId }); - closePalette(); - }; - - return ( - - ); -}); diff --git a/apps/web/core/components/command-palette/actions/issue-actions/index.ts b/apps/web/core/components/command-palette/actions/issue-actions/index.ts deleted file mode 100644 index 305107d6067..00000000000 --- a/apps/web/core/components/command-palette/actions/issue-actions/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from "./actions-list"; -export * from "./change-state"; -export * from "./change-priority"; -export * from "./change-assignee"; diff --git a/apps/web/core/components/command-palette/actions/project-actions.tsx b/apps/web/core/components/command-palette/actions/project-actions.tsx deleted file mode 100644 index 9d90471469a..00000000000 --- a/apps/web/core/components/command-palette/actions/project-actions.tsx +++ /dev/null @@ -1,94 +0,0 @@ -"use client"; - -import { Command } from "cmdk"; -// hooks -import { - CYCLE_TRACKER_ELEMENTS, - MODULE_TRACKER_ELEMENTS, - PROJECT_PAGE_TRACKER_ELEMENTS, - PROJECT_VIEW_TRACKER_ELEMENTS, -} from "@plane/constants"; -import { CycleIcon, ModuleIcon, PageIcon, ViewsIcon } from "@plane/propel/icons"; -// hooks -import { useCommandPalette } from "@/hooks/store/use-command-palette"; -// ui - -type Props = { - closePalette: () => void; -}; - -export const CommandPaletteProjectActions: React.FC = (props) => { - const { closePalette } = props; - // store hooks - const { toggleCreateCycleModal, toggleCreateModuleModal, toggleCreatePageModal, toggleCreateViewModal } = - useCommandPalette(); - - return ( - <> - - { - closePalette(); - toggleCreateCycleModal(true); - }} - className="focus:outline-none" - > -
- - Create new cycle -
- Q -
-
- - { - closePalette(); - toggleCreateModuleModal(true); - }} - className="focus:outline-none" - > -
- - Create new module -
- M -
-
- - { - closePalette(); - toggleCreateViewModal(true); - }} - className="focus:outline-none" - > -
- - Create new view -
- V -
-
- - { - closePalette(); - toggleCreatePageModal({ isOpen: true }); - }} - className="focus:outline-none" - > -
- - Create new page -
- D -
-
- - ); -}; diff --git a/apps/web/core/components/command-palette/actions/search-results.tsx b/apps/web/core/components/command-palette/actions/search-results.tsx deleted file mode 100644 index e73ed71f06b..00000000000 --- a/apps/web/core/components/command-palette/actions/search-results.tsx +++ /dev/null @@ -1,66 +0,0 @@ -"use client"; - -import { Command } from "cmdk"; -import { observer } from "mobx-react"; -import { useParams } from "next/navigation"; -// plane imports -import type { IWorkspaceSearchResults } from "@plane/types"; -// hooks -import { useAppRouter } from "@/hooks/use-app-router"; -// plane web imports -import { commandGroups } from "@/plane-web/components/command-palette"; -// helpers -import { openProjectAndScrollToSidebar } from "./helper"; - -type Props = { - closePalette: () => void; - results: IWorkspaceSearchResults; -}; - -export const CommandPaletteSearchResults: React.FC = observer((props) => { - const { closePalette, results } = props; - // router - const router = useAppRouter(); - const { projectId: routerProjectId } = useParams(); - // derived values - const projectId = routerProjectId?.toString(); - - return ( - <> - {Object.keys(results.results).map((key) => { - // TODO: add type for results - const section = (results.results as any)[key]; - const currentSection = commandGroups[key]; - if (!currentSection) return null; - if (section.length > 0) { - return ( - - {section.map((item: any) => ( - { - closePalette(); - router.push(currentSection.path(item, projectId)); - const itemProjectId = - item?.project_id || - (Array.isArray(item?.project_ids) && item?.project_ids?.length > 0 - ? item?.project_ids[0] - : undefined); - if (itemProjectId) openProjectAndScrollToSidebar(itemProjectId); - }} - value={`${key}-${item?.id}-${item.name}-${item.project__identifier ?? ""}-${item.sequence_id ?? ""}`} - className="focus:outline-none" - > -
- {currentSection.icon} -

{currentSection.itemName(item)}

-
-
- ))} -
- ); - } - })} - - ); -}); diff --git a/apps/web/core/components/command-palette/actions/theme-actions.tsx b/apps/web/core/components/command-palette/actions/theme-actions.tsx deleted file mode 100644 index 062d7fd0b15..00000000000 --- a/apps/web/core/components/command-palette/actions/theme-actions.tsx +++ /dev/null @@ -1,65 +0,0 @@ -"use client"; - -import type { FC } from "react"; -import React, { useEffect, useState } from "react"; -import { Command } from "cmdk"; -import { observer } from "mobx-react"; -import { useTheme } from "next-themes"; -import { Settings } from "lucide-react"; -// plane imports -import { THEME_OPTIONS } from "@plane/constants"; -import { useTranslation } from "@plane/i18n"; -import { TOAST_TYPE, setToast } from "@plane/propel/toast"; -// hooks -import { useUserProfile } from "@/hooks/store/user"; - -type Props = { - closePalette: () => void; -}; - -export const CommandPaletteThemeActions: FC = observer((props) => { - const { closePalette } = props; - const { setTheme } = useTheme(); - // hooks - const { updateUserTheme } = useUserProfile(); - const { t } = useTranslation(); - // states - const [mounted, setMounted] = useState(false); - - const updateTheme = async (newTheme: string) => { - setTheme(newTheme); - return updateUserTheme({ theme: newTheme }).catch(() => { - setToast({ - type: TOAST_TYPE.ERROR, - title: "Failed to save user theme settings!", - }); - }); - }; - - // useEffect only runs on the client, so now we can safely show the UI - useEffect(() => { - setMounted(true); - }, []); - - if (!mounted) return null; - - return ( - <> - {THEME_OPTIONS.map((theme) => ( - { - updateTheme(theme.value); - closePalette(); - }} - className="focus:outline-none" - > -
- - {t(theme.i18n_label)} -
-
- ))} - - ); -}); diff --git a/apps/web/core/components/command-palette/actions/workspace-settings-actions.tsx b/apps/web/core/components/command-palette/actions/workspace-settings-actions.tsx deleted file mode 100644 index 87b92133747..00000000000 --- a/apps/web/core/components/command-palette/actions/workspace-settings-actions.tsx +++ /dev/null @@ -1,60 +0,0 @@ -"use client"; - -import { Command } from "cmdk"; -// hooks -import Link from "next/link"; -import { useParams } from "next/navigation"; -import { WORKSPACE_SETTINGS_LINKS, EUserPermissionsLevel } from "@plane/constants"; -import { useTranslation } from "@plane/i18n"; -// components -import { SettingIcon } from "@/components/icons"; -// hooks -import { useUserPermissions } from "@/hooks/store/user"; -import { useAppRouter } from "@/hooks/use-app-router"; -// plane wev constants -// plane web helpers -import { shouldRenderSettingLink } from "@/plane-web/helpers/workspace.helper"; - -type Props = { - closePalette: () => void; -}; - -export const CommandPaletteWorkspaceSettingsActions: React.FC = (props) => { - const { closePalette } = props; - // router - const router = useAppRouter(); - // router params - const { workspaceSlug } = useParams(); - // mobx store - const { t } = useTranslation(); - const { allowPermissions } = useUserPermissions(); - // derived values - - const redirect = (path: string) => { - closePalette(); - router.push(path); - }; - - return ( - <> - {WORKSPACE_SETTINGS_LINKS.map( - (setting) => - allowPermissions(setting.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString()) && - shouldRenderSettingLink(workspaceSlug.toString(), setting.key) && ( - redirect(`/${workspaceSlug}${setting.href}`)} - className="focus:outline-none" - > - -
- - {t(setting.i18n_label)} -
- -
- ) - )} - - ); -}; diff --git a/apps/web/core/components/command-palette/command-modal.tsx b/apps/web/core/components/command-palette/command-modal.tsx deleted file mode 100644 index 280b2d3bc97..00000000000 --- a/apps/web/core/components/command-palette/command-modal.tsx +++ /dev/null @@ -1,492 +0,0 @@ -"use client"; - -import React, { useEffect, useState } from "react"; -import { Command } from "cmdk"; -import { observer } from "mobx-react"; -import { useParams } from "next/navigation"; -import useSWR from "swr"; -import { CommandIcon, FolderPlus, Search, Settings, X } from "lucide-react"; -import { Dialog, Transition } from "@headlessui/react"; -// plane imports -import { - EUserPermissions, - EUserPermissionsLevel, - PROJECT_TRACKER_ELEMENTS, - WORK_ITEM_TRACKER_ELEMENTS, - WORKSPACE_DEFAULT_SEARCH_RESULT, -} from "@plane/constants"; -import { useTranslation } from "@plane/i18n"; -import { WorkItemsIcon } from "@plane/propel/icons"; -import type { IWorkspaceSearchResults } from "@plane/types"; -import { Loader, ToggleSwitch } from "@plane/ui"; -import { cn, getTabIndex } from "@plane/utils"; -// components -import { - ChangeIssueAssignee, - ChangeIssuePriority, - ChangeIssueState, - CommandPaletteHelpActions, - CommandPaletteIssueActions, - CommandPaletteProjectActions, - CommandPaletteSearchResults, - CommandPaletteThemeActions, - CommandPaletteWorkspaceSettingsActions, -} from "@/components/command-palette"; -import { SimpleEmptyState } from "@/components/empty-state/simple-empty-state-root"; -// helpers -// hooks -import { captureClick } from "@/helpers/event-tracker.helper"; -import { useCommandPalette } from "@/hooks/store/use-command-palette"; -import { useIssueDetail } from "@/hooks/store/use-issue-detail"; -import { useProject } from "@/hooks/store/use-project"; -import { useUser, useUserPermissions } from "@/hooks/store/user"; -import { useAppRouter } from "@/hooks/use-app-router"; -import useDebounce from "@/hooks/use-debounce"; -import { usePlatformOS } from "@/hooks/use-platform-os"; -// plane web components -import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path"; -import { IssueIdentifier } from "@/plane-web/components/issues/issue-details/issue-identifier"; -// plane web services -import { WorkspaceService } from "@/plane-web/services"; - -const workspaceService = new WorkspaceService(); - -export const CommandModal: React.FC = observer(() => { - // router - const router = useAppRouter(); - const { workspaceSlug, projectId: routerProjectId, workItem } = useParams(); - // states - const [placeholder, setPlaceholder] = useState("Type a command or search..."); - const [resultsCount, setResultsCount] = useState(0); - const [isLoading, setIsLoading] = useState(false); - const [isSearching, setIsSearching] = useState(false); - const [searchTerm, setSearchTerm] = useState(""); - const [results, setResults] = useState(WORKSPACE_DEFAULT_SEARCH_RESULT); - const [isWorkspaceLevel, setIsWorkspaceLevel] = useState(false); - const [pages, setPages] = useState([]); - const [searchInIssue, setSearchInIssue] = useState(false); - // plane hooks - const { t } = useTranslation(); - // hooks - const { - issue: { getIssueById }, - fetchIssueWithIdentifier, - } = useIssueDetail(); - const { workspaceProjectIds } = useProject(); - const { platform, isMobile } = usePlatformOS(); - const { canPerformAnyCreateAction } = useUser(); - const { isCommandPaletteOpen, toggleCommandPaletteModal, toggleCreateIssueModal, toggleCreateProjectModal } = - useCommandPalette(); - const { allowPermissions } = useUserPermissions(); - const projectIdentifier = workItem?.toString().split("-")[0]; - const sequence_id = workItem?.toString().split("-")[1]; - // fetch work item details using identifier - const { data: workItemDetailsSWR } = useSWR( - workspaceSlug && workItem ? `ISSUE_DETAIL_${workspaceSlug}_${projectIdentifier}_${sequence_id}` : null, - workspaceSlug && workItem - ? () => fetchIssueWithIdentifier(workspaceSlug.toString(), projectIdentifier, sequence_id) - : null - ); - - // derived values - const issueDetails = workItemDetailsSWR ? getIssueById(workItemDetailsSWR?.id) : null; - const issueId = issueDetails?.id; - const projectId = issueDetails?.project_id ?? routerProjectId; - const page = pages[pages.length - 1]; - const debouncedSearchTerm = useDebounce(searchTerm, 500); - const { baseTabIndex } = getTabIndex(undefined, isMobile); - const canPerformWorkspaceActions = allowPermissions( - [EUserPermissions.ADMIN, EUserPermissions.MEMBER], - EUserPermissionsLevel.WORKSPACE - ); - const resolvedPath = useResolvedAssetPath({ basePath: "/empty-state/search/search" }); - - useEffect(() => { - if (issueDetails && isCommandPaletteOpen) { - setSearchInIssue(true); - } - }, [issueDetails, isCommandPaletteOpen]); - - useEffect(() => { - if (!projectId && !isWorkspaceLevel) { - setIsWorkspaceLevel(true); - } else { - setIsWorkspaceLevel(false); - } - }, [projectId]); - - const closePalette = () => { - toggleCommandPaletteModal(false); - }; - - const createNewWorkspace = () => { - closePalette(); - router.push("/create-workspace"); - }; - - useEffect( - () => { - if (!workspaceSlug) return; - - setIsLoading(true); - - if (debouncedSearchTerm) { - setIsSearching(true); - workspaceService - .searchWorkspace(workspaceSlug.toString(), { - ...(projectId ? { project_id: projectId.toString() } : {}), - search: debouncedSearchTerm, - workspace_search: !projectId ? true : isWorkspaceLevel, - }) - .then((results) => { - setResults(results); - const count = Object.keys(results.results).reduce( - (accumulator, key) => (results.results as any)[key].length + accumulator, - 0 - ); - setResultsCount(count); - }) - .finally(() => { - setIsLoading(false); - setIsSearching(false); - }); - } else { - setResults(WORKSPACE_DEFAULT_SEARCH_RESULT); - setIsLoading(false); - setIsSearching(false); - } - }, - [debouncedSearchTerm, isWorkspaceLevel, projectId, workspaceSlug] // Only call effect if debounced search term changes - ); - - return ( - setSearchTerm("")} as={React.Fragment}> - { - closePalette(); - if (searchInIssue) { - setSearchInIssue(true); - } - }} - > - -
- - -
-
- - -
- { - if (value.toLowerCase().includes(search.toLowerCase())) return 1; - return 0; - }} - shouldFilter={searchTerm.length > 0} - onKeyDown={(e: any) => { - if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") { - e.preventDefault(); - e.stopPropagation(); - closePalette(); - return; - } - - if (e.key === "Tab") { - e.preventDefault(); - const commandList = document.querySelector("[cmdk-list]"); - const items = commandList?.querySelectorAll("[cmdk-item]") || []; - const selectedItem = commandList?.querySelector('[aria-selected="true"]'); - if (items.length === 0) return; - - const currentIndex = Array.from(items).indexOf(selectedItem as Element); - let nextIndex; - - if (e.shiftKey) { - nextIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1; - } else { - nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0; - } - - const nextItem = items[nextIndex] as HTMLElement; - if (nextItem) { - nextItem.setAttribute("aria-selected", "true"); - selectedItem?.setAttribute("aria-selected", "false"); - nextItem.focus(); - nextItem.scrollIntoView({ behavior: "smooth", block: "nearest" }); - } - } - - if (e.key === "Escape" && searchTerm) { - e.preventDefault(); - setSearchTerm(""); - } - - if (e.key === "Escape" && !page && !searchTerm) { - e.preventDefault(); - closePalette(); - } - - if (e.key === "Escape" || (e.key === "Backspace" && !searchTerm)) { - e.preventDefault(); - setPages((pages) => pages.slice(0, -1)); - setPlaceholder("Type a command or search..."); - } - }} - > -
-
-
- setSearchTerm(e)} - autoFocus - tabIndex={baseTabIndex} - /> -
- - - {searchTerm !== "" && ( -
- Search results for{" "} - - {'"'} - {searchTerm} - {'"'} - {" "} - in {!projectId || isWorkspaceLevel ? "workspace" : "project"}: -
- )} - - {!isLoading && resultsCount === 0 && searchTerm !== "" && debouncedSearchTerm !== "" && ( -
- -
- )} - - {(isLoading || isSearching) && ( - - - - - - - - - )} - - {debouncedSearchTerm !== "" && ( - - )} - - {!page && ( - <> - {/* issue actions */} - {issueId && issueDetails && searchInIssue && ( - setPages(newPages)} - setPlaceholder={(newPlaceholder) => setPlaceholder(newPlaceholder)} - setSearchTerm={(newSearchTerm) => setSearchTerm(newSearchTerm)} - /> - )} - {workspaceSlug && - workspaceProjectIds && - workspaceProjectIds.length > 0 && - canPerformAnyCreateAction && ( - - { - closePalette(); - captureClick({ - elementName: WORK_ITEM_TRACKER_ELEMENTS.COMMAND_PALETTE_ADD_BUTTON, - }); - toggleCreateIssueModal(true); - }} - className="focus:bg-custom-background-80" - > -
- - Create new work item -
- C -
-
- )} - {workspaceSlug && canPerformWorkspaceActions && ( - - { - closePalette(); - captureClick({ elementName: PROJECT_TRACKER_ELEMENTS.COMMAND_PALETTE_CREATE_BUTTON }); - toggleCreateProjectModal(true); - }} - className="focus:outline-none" - > -
- - Create new project -
- P -
-
- )} - - {/* project actions */} - {projectId && canPerformAnyCreateAction && ( - - )} - {canPerformWorkspaceActions && ( - - { - setPlaceholder("Search workspace settings..."); - setSearchTerm(""); - setPages([...pages, "settings"]); - }} - className="focus:outline-none" - > -
- - Search settings... -
-
-
- )} - - -
- - Create new workspace -
-
- { - setPlaceholder("Change interface theme..."); - setSearchTerm(""); - setPages([...pages, "change-interface-theme"]); - }} - className="focus:outline-none" - > -
- - Change interface theme... -
-
-
- - {/* help options */} - - - )} - - {/* workspace settings actions */} - {page === "settings" && workspaceSlug && ( - - )} - - {/* issue details page actions */} - {page === "change-issue-state" && issueDetails && ( - - )} - {page === "change-issue-priority" && issueDetails && ( - - )} - {page === "change-issue-assignee" && issueDetails && ( - - )} - - {/* theme actions */} - {page === "change-interface-theme" && ( - { - closePalette(); - setPages((pages) => pages.slice(0, -1)); - }} - /> - )} -
-
-
- {/* Bottom overlay */} -
-
- Actions -
-
- {platform === "MacOS" ? : "Ctrl"} -
- - K - -
-
-
- Workspace Level - setIsWorkspaceLevel((prevData) => !prevData)} - disabled={!projectId} - size="sm" - /> -
-
-
-
-
-
-
-
- ); -}); diff --git a/apps/web/core/components/command-palette/command-palette.tsx b/apps/web/core/components/command-palette/command-palette.tsx deleted file mode 100644 index b79b731cb3b..00000000000 --- a/apps/web/core/components/command-palette/command-palette.tsx +++ /dev/null @@ -1,270 +0,0 @@ -"use client"; - -import type { FC } from "react"; -import React, { useCallback, useEffect, useMemo } from "react"; -import { observer } from "mobx-react"; -import { useParams } from "next/navigation"; -import useSWR from "swr"; -// ui -import { COMMAND_PALETTE_TRACKER_ELEMENTS, EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; -import { TOAST_TYPE, setToast } from "@plane/propel/toast"; -// components -import { copyTextToClipboard } from "@plane/utils"; -import { CommandModal, ShortcutsModal } from "@/components/command-palette"; -// helpers -// hooks -import { captureClick } from "@/helpers/event-tracker.helper"; -import { useAppTheme } from "@/hooks/store/use-app-theme"; -import { useCommandPalette } from "@/hooks/store/use-command-palette"; -import { useIssueDetail } from "@/hooks/store/use-issue-detail"; -import { useUser, useUserPermissions } from "@/hooks/store/user"; -import { usePlatformOS } from "@/hooks/use-platform-os"; -// plane web components -import { - IssueLevelModals, - ProjectLevelModals, - WorkspaceLevelModals, -} from "@/plane-web/components/command-palette/modals"; -// plane web constants -// plane web helpers -import { - getGlobalShortcutsList, - getProjectShortcutsList, - getWorkspaceShortcutsList, - handleAdditionalKeyDownEvents, -} from "@/plane-web/helpers/command-palette"; - -export const CommandPalette: FC = observer(() => { - // router params - const { workspaceSlug, projectId: paramsProjectId, workItem } = useParams(); - // store hooks - const { fetchIssueWithIdentifier } = useIssueDetail(); - const { toggleSidebar, toggleExtendedSidebar } = useAppTheme(); - const { platform } = usePlatformOS(); - const { data: currentUser, canPerformAnyCreateAction } = useUser(); - const { toggleCommandPaletteModal, isShortcutModalOpen, toggleShortcutModal, isAnyModalOpen } = useCommandPalette(); - const { allowPermissions } = useUserPermissions(); - - // derived values - const projectIdentifier = workItem?.toString().split("-")[0]; - const sequence_id = workItem?.toString().split("-")[1]; - - const { data: issueDetails } = useSWR( - workspaceSlug && workItem ? `ISSUE_DETAIL_${workspaceSlug}_${projectIdentifier}_${sequence_id}` : null, - workspaceSlug && workItem - ? () => fetchIssueWithIdentifier(workspaceSlug.toString(), projectIdentifier, sequence_id) - : null - ); - - const issueId = issueDetails?.id; - const projectId = paramsProjectId?.toString() ?? issueDetails?.project_id; - - const canPerformWorkspaceMemberActions = allowPermissions( - [EUserPermissions.ADMIN, EUserPermissions.MEMBER], - EUserPermissionsLevel.WORKSPACE - ); - const canPerformProjectMemberActions = allowPermissions( - [EUserPermissions.ADMIN, EUserPermissions.MEMBER], - EUserPermissionsLevel.PROJECT, - workspaceSlug?.toString(), - projectId - ); - const canPerformProjectAdminActions = allowPermissions( - [EUserPermissions.ADMIN], - EUserPermissionsLevel.PROJECT, - workspaceSlug?.toString(), - projectId - ); - - const copyIssueUrlToClipboard = useCallback(() => { - if (!workItem) return; - - const url = new URL(window.location.href); - copyTextToClipboard(url.href) - .then(() => { - setToast({ - type: TOAST_TYPE.SUCCESS, - title: "Copied to clipboard", - }); - }) - .catch(() => { - setToast({ - type: TOAST_TYPE.ERROR, - title: "Some error occurred", - }); - }); - }, [workItem]); - - // auth - const performProjectCreateActions = useCallback( - (showToast: boolean = true) => { - if (!canPerformProjectMemberActions && showToast) - setToast({ - type: TOAST_TYPE.ERROR, - title: "You don't have permission to perform this action.", - }); - - return canPerformProjectMemberActions; - }, - [canPerformProjectMemberActions] - ); - - const performProjectBulkDeleteActions = useCallback( - (showToast: boolean = true) => { - if (!canPerformProjectAdminActions && projectId && showToast) - setToast({ - type: TOAST_TYPE.ERROR, - title: "You don't have permission to perform this action.", - }); - - return canPerformProjectAdminActions; - }, - [canPerformProjectAdminActions, projectId] - ); - - const performWorkspaceCreateActions = useCallback( - (showToast: boolean = true) => { - if (!canPerformWorkspaceMemberActions && showToast) - setToast({ - type: TOAST_TYPE.ERROR, - title: "You don't have permission to perform this action.", - }); - return canPerformWorkspaceMemberActions; - }, - [canPerformWorkspaceMemberActions] - ); - - const performAnyProjectCreateActions = useCallback( - (showToast: boolean = true) => { - if (!canPerformAnyCreateAction && showToast) - setToast({ - type: TOAST_TYPE.ERROR, - title: "You don't have permission to perform this action.", - }); - return canPerformAnyCreateAction; - }, - [canPerformAnyCreateAction] - ); - - const shortcutsList: { - global: Record void }>; - workspace: Record void }>; - project: Record void }>; - } = useMemo( - () => ({ - global: getGlobalShortcutsList(), - workspace: getWorkspaceShortcutsList(), - project: getProjectShortcutsList(), - }), - [] - ); - - const handleKeyDown = useCallback( - (e: KeyboardEvent) => { - const { key, ctrlKey, metaKey, altKey, shiftKey } = e; - if (!key) return; - - const keyPressed = key.toLowerCase(); - const cmdClicked = ctrlKey || metaKey; - const shiftClicked = shiftKey; - const deleteKey = keyPressed === "backspace" || keyPressed === "delete"; - - if (cmdClicked && keyPressed === "k" && !isAnyModalOpen) { - e.preventDefault(); - toggleCommandPaletteModal(true); - } - - // if on input, textarea or editor, don't do anything - if ( - e.target instanceof HTMLTextAreaElement || - e.target instanceof HTMLInputElement || - (e.target as Element)?.classList?.contains("ProseMirror") - ) - return; - - if (shiftClicked && (keyPressed === "?" || keyPressed === "/") && !isAnyModalOpen) { - e.preventDefault(); - toggleShortcutModal(true); - } - - if (deleteKey) { - if (performProjectBulkDeleteActions()) { - shortcutsList.project.delete.action(); - } - } else if (cmdClicked) { - if (keyPressed === "c" && ((platform === "MacOS" && ctrlKey) || altKey)) { - e.preventDefault(); - copyIssueUrlToClipboard(); - } else if (keyPressed === "b") { - e.preventDefault(); - toggleSidebar(); - toggleExtendedSidebar(false); - } - } else if (!isAnyModalOpen) { - captureClick({ elementName: COMMAND_PALETTE_TRACKER_ELEMENTS.COMMAND_PALETTE_SHORTCUT_KEY }); - if ( - Object.keys(shortcutsList.global).includes(keyPressed) && - ((!projectId && performAnyProjectCreateActions()) || performProjectCreateActions()) - ) { - shortcutsList.global[keyPressed].action(); - } - // workspace authorized actions - else if ( - Object.keys(shortcutsList.workspace).includes(keyPressed) && - workspaceSlug && - performWorkspaceCreateActions() - ) { - e.preventDefault(); - shortcutsList.workspace[keyPressed].action(); - } - // project authorized actions - else if ( - Object.keys(shortcutsList.project).includes(keyPressed) && - projectId && - performProjectCreateActions() - ) { - e.preventDefault(); - // actions that can be performed only inside a project - shortcutsList.project[keyPressed].action(); - } - } - // Additional keydown events - handleAdditionalKeyDownEvents(e); - }, - [ - copyIssueUrlToClipboard, - isAnyModalOpen, - platform, - performAnyProjectCreateActions, - performProjectBulkDeleteActions, - performProjectCreateActions, - performWorkspaceCreateActions, - projectId, - shortcutsList, - toggleCommandPaletteModal, - toggleShortcutModal, - toggleSidebar, - toggleExtendedSidebar, - workspaceSlug, - ] - ); - - useEffect(() => { - document.addEventListener("keydown", handleKeyDown); - return () => document.removeEventListener("keydown", handleKeyDown); - }, [handleKeyDown]); - - if (!currentUser) return null; - - return ( - <> - toggleShortcutModal(false)} /> - {workspaceSlug && } - {workspaceSlug && projectId && ( - - )} - - - - ); -}); diff --git a/apps/web/core/components/command-palette/index.ts b/apps/web/core/components/command-palette/index.ts deleted file mode 100644 index 5aee700af3e..00000000000 --- a/apps/web/core/components/command-palette/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from "./actions"; -export * from "./shortcuts-modal"; -export * from "./command-modal"; -export * from "./command-palette"; diff --git a/apps/web/core/components/command-palette/shortcuts-modal/commands-list.tsx b/apps/web/core/components/command-palette/shortcuts-modal/commands-list.tsx deleted file mode 100644 index c01eff48f65..00000000000 --- a/apps/web/core/components/command-palette/shortcuts-modal/commands-list.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { Command } from "lucide-react"; -// helpers -import { substringMatch } from "@plane/utils"; -// hooks -import { usePlatformOS } from "@/hooks/use-platform-os"; -// plane web helpers -import { - getAdditionalShortcutsList, - getCommonShortcutsList, - getNavigationShortcutsList, -} from "@/plane-web/helpers/command-palette"; - -type Props = { - searchQuery: string; -}; - -export const ShortcutCommandsList: React.FC = (props) => { - const { searchQuery } = props; - const { platform } = usePlatformOS(); - - const KEYBOARD_SHORTCUTS = [ - { - key: "navigation", - title: "Navigation", - shortcuts: getNavigationShortcutsList(), - }, - { - key: "common", - title: "Common", - shortcuts: getCommonShortcutsList(platform), - }, - ...getAdditionalShortcutsList(), - ]; - - const filteredShortcuts = KEYBOARD_SHORTCUTS.map((category) => { - const newCategory = { ...category }; - - newCategory.shortcuts = newCategory.shortcuts.filter((shortcut) => - substringMatch(shortcut.description, searchQuery) - ); - - return newCategory; - }); - - const isShortcutsEmpty = filteredShortcuts.every((category) => category.shortcuts.length === 0); - - return ( -
- {!isShortcutsEmpty ? ( - filteredShortcuts.map((category) => { - if (category.shortcuts.length === 0) return; - - return ( -
-
{category.title}
-
- {category.shortcuts.map((shortcut) => ( -
-
-

{shortcut.description}

-
- {shortcut.keys.split(",").map((key) => ( -
- {key === "Ctrl" ? ( -
- {platform === "MacOS" ? ( - - ) : ( - "Ctrl" - )} -
- ) : ( - - {key} - - )} -
- ))} -
-
-
- ))} -
-
- ); - }) - ) : ( -

- No shortcuts found for{" "} - - {`"`} - {searchQuery} - {`"`} - -

- )} -
- ); -}; diff --git a/apps/web/core/components/command-palette/shortcuts-modal/index.ts b/apps/web/core/components/command-palette/shortcuts-modal/index.ts deleted file mode 100644 index 9346fb2b4c3..00000000000 --- a/apps/web/core/components/command-palette/shortcuts-modal/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./commands-list"; -export * from "./modal"; diff --git a/apps/web/core/components/comments/card/edit-form.tsx b/apps/web/core/components/comments/card/edit-form.tsx index b370db0a22e..fcaa6b35252 100644 --- a/apps/web/core/components/comments/card/edit-form.tsx +++ b/apps/web/core/components/comments/card/edit-form.tsx @@ -1,9 +1,10 @@ import React, { useEffect, useRef } from "react"; import { observer } from "mobx-react"; import { useForm } from "react-hook-form"; -import { Check, X } from "lucide-react"; -// plane imports +import { Check } from "lucide-react"; import type { EditorRefApi } from "@plane/editor"; +import { CloseIcon } from "@plane/propel/icons"; +// plane imports import type { TCommentsOperations, TIssueComment } from "@plane/types"; import { isCommentEmpty } from "@plane/utils"; // components @@ -123,7 +124,7 @@ export const CommentCardEditForm: React.FC = observer((props) => { editorRef.current?.setEditorValue(comment.comment_html ?? "

"); }} > - + diff --git a/apps/web/core/components/common/applied-filters/date.tsx b/apps/web/core/components/common/applied-filters/date.tsx index 3ecf8a540fe..3d8d94cf824 100644 --- a/apps/web/core/components/common/applied-filters/date.tsx +++ b/apps/web/core/components/common/applied-filters/date.tsx @@ -1,8 +1,8 @@ import { observer } from "mobx-react"; // icons -import { X } from "lucide-react"; -// plane constants import { DATE_BEFORE_FILTER_OPTIONS } from "@plane/constants"; +import { CloseIcon } from "@plane/propel/icons"; +// plane constants import { renderFormattedDate, capitalizeFirstLetter } from "@plane/utils"; // helpers type Props = { @@ -44,7 +44,7 @@ export const AppliedDateFilters: React.FC = observer((props) => { className="grid place-items-center text-custom-text-300 hover:text-custom-text-200" onClick={() => handleRemove(date)} > - + )} diff --git a/apps/web/core/components/common/applied-filters/members.tsx b/apps/web/core/components/common/applied-filters/members.tsx index 49b0f1a3043..a1823132f5c 100644 --- a/apps/web/core/components/common/applied-filters/members.tsx +++ b/apps/web/core/components/common/applied-filters/members.tsx @@ -1,7 +1,7 @@ "use client"; import { observer } from "mobx-react"; -import { X } from "lucide-react"; +import { CloseIcon } from "@plane/propel/icons"; // plane ui import { Avatar } from "@plane/ui"; // helpers @@ -44,7 +44,7 @@ export const AppliedMembersFilters: React.FC = observer((props) => { className="grid place-items-center text-custom-text-300 hover:text-custom-text-200" onClick={() => handleRemove(memberId)} > - + )} diff --git a/apps/web/core/components/core/description-versions/modal.tsx b/apps/web/core/components/core/description-versions/modal.tsx index 1dbe5e552fb..15e29149420 100644 --- a/apps/web/core/components/core/description-versions/modal.tsx +++ b/apps/web/core/components/core/description-versions/modal.tsx @@ -1,10 +1,11 @@ import { useCallback, useRef } from "react"; import { observer } from "mobx-react"; -import { ChevronLeft, ChevronRight, Copy } from "lucide-react"; +import { Copy } from "lucide-react"; // plane imports import type { EditorRefApi } from "@plane/editor"; import { useTranslation } from "@plane/i18n"; import { Button, getButtonStyling } from "@plane/propel/button"; +import { ChevronLeftIcon, ChevronRightIcon } from "@plane/propel/icons"; import { setToast, TOAST_TYPE } from "@plane/propel/toast"; import { Tooltip } from "@plane/propel/tooltip"; import type { TDescriptionVersion } from "@plane/types"; @@ -102,7 +103,7 @@ export const DescriptionVersionsModal: React.FC = observer((props) => { )} disabled={isPrevDisabled} > - + diff --git a/apps/web/core/components/core/filters/date-filter-modal.tsx b/apps/web/core/components/core/filters/date-filter-modal.tsx index 334d15ae4d7..32a40741412 100644 --- a/apps/web/core/components/core/filters/date-filter-modal.tsx +++ b/apps/web/core/components/core/filters/date-filter-modal.tsx @@ -2,12 +2,12 @@ import { Fragment } from "react"; import { Controller, useForm } from "react-hook-form"; -import { X } from "lucide-react"; import { Dialog, Transition } from "@headlessui/react"; import { Button } from "@plane/propel/button"; import { Calendar } from "@plane/propel/calendar"; +import { CloseIcon } from "@plane/propel/icons"; import { renderFormattedPayloadDate, renderFormattedDate, getDate } from "@plane/utils"; import { DateFilterSelect } from "./date-filter-select"; type Props = { @@ -84,7 +84,7 @@ export const DateFilterModal: React.FC = ({ title, handleClose, isOpen, o )} /> - +
= (props) => { const { isMobile } = usePlatformOS(); const debouncedSearchTerm: string = useDebounce(searchTerm, 500); const { baseTabIndex } = getTabIndex(undefined, isMobile); + const hasInitializedSelection = useRef(false); const handleClose = () => { onClose(); setSearchTerm(""); setSelectedIssues([]); setIsWorkspaceLevel(false); + hasInitializedSelection.current = false; }; const onSubmit = async () => { @@ -117,10 +120,11 @@ export const ExistingIssuesListModal: React.FC = (props) => { }; useEffect(() => { - if (selectedWorkItemIds) { + if (isOpen && !hasInitializedSelection.current && selectedWorkItemIds && issues.length > 0) { setSelectedIssues(issues.filter((issue) => selectedWorkItemIds.includes(issue.id))); + hasInitializedSelection.current = true; } - }, [isOpen, selectedWorkItemIds]); + }, [isOpen, issues, selectedWorkItemIds]); useEffect(() => { handleSearch(); @@ -197,7 +201,7 @@ export const ExistingIssuesListModal: React.FC = (props) => { className="group p-1" onClick={() => setSelectedIssues((prevData) => prevData.filter((i) => i.id !== issue.id))} > - +
))} diff --git a/apps/web/core/components/cycles/analytics-sidebar/issue-progress.tsx b/apps/web/core/components/cycles/analytics-sidebar/issue-progress.tsx index 224e1ff676f..100311e78a0 100644 --- a/apps/web/core/components/cycles/analytics-sidebar/issue-progress.tsx +++ b/apps/web/core/components/cycles/analytics-sidebar/issue-progress.tsx @@ -5,10 +5,10 @@ import { useMemo } from "react"; import { isEmpty } from "lodash-es"; import { observer } from "mobx-react"; import { useSearchParams } from "next/navigation"; -import { ChevronUp, ChevronDown } from "lucide-react"; import { Disclosure, Transition } from "@headlessui/react"; // plane imports import { useTranslation } from "@plane/i18n"; +import { ChevronUpIcon, ChevronDownIcon } from "@plane/propel/icons"; import type { ICycle, TCyclePlotType, TProgressSnapshot } from "@plane/types"; import { EIssuesStoreType } from "@plane/types"; import { getDate } from "@plane/utils"; @@ -114,9 +114,9 @@ export const CycleAnalyticsProgress: FC = observer((pro {open ? ( - diff --git a/apps/web/core/components/cycles/analytics-sidebar/sidebar-header.tsx b/apps/web/core/components/cycles/analytics-sidebar/sidebar-header.tsx index 0c84571c997..41129e9c072 100644 --- a/apps/web/core/components/cycles/analytics-sidebar/sidebar-header.tsx +++ b/apps/web/core/components/cycles/analytics-sidebar/sidebar-header.tsx @@ -4,7 +4,7 @@ import type { FC } from "react"; import React, { useEffect } from "react"; import { observer } from "mobx-react"; import { Controller, useForm } from "react-hook-form"; -import { ArrowRight, ChevronRight } from "lucide-react"; +import { ArrowRight } from "lucide-react"; // Plane Imports import { CYCLE_TRACKER_EVENTS, @@ -14,6 +14,7 @@ import { CYCLE_TRACKER_ELEMENTS, } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; +import { ChevronRightIcon } from "@plane/propel/icons"; import { TOAST_TYPE, setToast } from "@plane/propel/toast"; import type { ICycle } from "@plane/types"; import { getDate, renderFormattedPayloadDate } from "@plane/utils"; @@ -160,7 +161,7 @@ export const CycleSidebarHeader: FC = observer((props) => { className="flex size-4 items-center justify-center rounded-full bg-custom-border-200" onClick={() => handleClose()} > - + diff --git a/apps/web/core/components/cycles/applied-filters/date.tsx b/apps/web/core/components/cycles/applied-filters/date.tsx index 18a5ece58ad..e8156fafc67 100644 --- a/apps/web/core/components/cycles/applied-filters/date.tsx +++ b/apps/web/core/components/cycles/applied-filters/date.tsx @@ -1,7 +1,7 @@ import { observer } from "mobx-react"; -import { X } from "lucide-react"; // helpers import { DATE_AFTER_FILTER_OPTIONS } from "@plane/constants"; +import { CloseIcon } from "@plane/propel/icons"; import { renderFormattedDate, capitalizeFirstLetter } from "@plane/utils"; // constants @@ -44,7 +44,7 @@ export const AppliedDateFilters: React.FC = observer((props) => { className="grid place-items-center text-custom-text-300 hover:text-custom-text-200" onClick={() => handleRemove(date)} > - + )} diff --git a/apps/web/core/components/cycles/applied-filters/root.tsx b/apps/web/core/components/cycles/applied-filters/root.tsx index 1ee80e738d1..59ebf55cb09 100644 --- a/apps/web/core/components/cycles/applied-filters/root.tsx +++ b/apps/web/core/components/cycles/applied-filters/root.tsx @@ -1,8 +1,8 @@ import { observer } from "mobx-react"; -import { X } from "lucide-react"; // plane imports import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; +import { CloseIcon } from "@plane/propel/icons"; import type { TCycleFilters } from "@plane/types"; import { Tag } from "@plane/ui"; import { replaceUnderscoreIfSnakeCase } from "@plane/utils"; @@ -67,7 +67,7 @@ export const CycleAppliedFiltersList: React.FC = observer((props) => { className="grid place-items-center text-custom-text-300 hover:text-custom-text-200" onClick={() => handleRemoveFilter(filterKey, null)} > - + )} @@ -78,7 +78,7 @@ export const CycleAppliedFiltersList: React.FC = observer((props) => { )} diff --git a/apps/web/core/components/cycles/applied-filters/status.tsx b/apps/web/core/components/cycles/applied-filters/status.tsx index ef2d63b1a37..e54edf6b70e 100644 --- a/apps/web/core/components/cycles/applied-filters/status.tsx +++ b/apps/web/core/components/cycles/applied-filters/status.tsx @@ -1,7 +1,7 @@ import { observer } from "mobx-react"; -import { X } from "lucide-react"; import { CYCLE_STATUS } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; +import { CloseIcon } from "@plane/propel/icons"; import { cn } from "@plane/utils"; type Props = { @@ -34,7 +34,7 @@ export const AppliedStatusFilters: React.FC = observer((props) => { className="grid place-items-center text-custom-text-300 hover:text-custom-text-200" onClick={() => handleRemove(status)} > - + )} diff --git a/apps/web/core/components/cycles/archived-cycles/header.tsx b/apps/web/core/components/cycles/archived-cycles/header.tsx index dbbb93dc17e..50e8fd67a4e 100644 --- a/apps/web/core/components/cycles/archived-cycles/header.tsx +++ b/apps/web/core/components/cycles/archived-cycles/header.tsx @@ -3,9 +3,10 @@ import { useCallback, useRef, useState } from "react"; import { observer } from "mobx-react"; import { useParams } from "next/navigation"; // icons -import { ListFilter, Search, X } from "lucide-react"; -// plane helpers +import { ListFilter, Search } from "lucide-react"; import { useOutsideClickDetector } from "@plane/hooks"; +import { CloseIcon } from "@plane/propel/icons"; +// plane helpers // types import type { TCycleFilters } from "@plane/types"; import { cn, calculateTotalFilters } from "@plane/utils"; @@ -109,7 +110,7 @@ export const ArchivedCyclesHeader: FC = observer(() => { setIsSearchOpen(false); }} > - + )} diff --git a/apps/web/core/components/cycles/cycles-view-header.tsx b/apps/web/core/components/cycles/cycles-view-header.tsx index 4276c1ed2b5..1cc782f59c7 100644 --- a/apps/web/core/components/cycles/cycles-view-header.tsx +++ b/apps/web/core/components/cycles/cycles-view-header.tsx @@ -1,11 +1,12 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { observer } from "mobx-react"; // icons -import { ListFilter, Search, X } from "lucide-react"; +import { ListFilter, Search } from "lucide-react"; // plane helpers import { useOutsideClickDetector } from "@plane/hooks"; // types import { useTranslation } from "@plane/i18n"; +import { CloseIcon } from "@plane/propel/icons"; import type { TCycleFilters } from "@plane/types"; import { cn, calculateTotalFilters } from "@plane/utils"; // components @@ -109,7 +110,7 @@ export const CyclesViewHeader: React.FC = observer((props) => { setIsSearchOpen(false); }} > - + )} diff --git a/apps/web/core/components/cycles/dropdowns/filters/root.tsx b/apps/web/core/components/cycles/dropdowns/filters/root.tsx index 550d7daf7ec..148de99463c 100644 --- a/apps/web/core/components/cycles/dropdowns/filters/root.tsx +++ b/apps/web/core/components/cycles/dropdowns/filters/root.tsx @@ -1,6 +1,7 @@ import { useState } from "react"; import { observer } from "mobx-react"; -import { Search, X } from "lucide-react"; +import { Search } from "lucide-react"; +import { CloseIcon } from "@plane/propel/icons"; // plane imports import type { TCycleFilters, TCycleGroups } from "@plane/types"; // hooks @@ -38,7 +39,7 @@ export const CycleFiltersSelection: React.FC = observer((props) => { /> {filtersSearchQuery !== "" && ( )} diff --git a/apps/web/core/components/cycles/list/cycle-list-group-header.tsx b/apps/web/core/components/cycles/list/cycle-list-group-header.tsx index 5ab265dcf48..074bc500ced 100644 --- a/apps/web/core/components/cycles/list/cycle-list-group-header.tsx +++ b/apps/web/core/components/cycles/list/cycle-list-group-header.tsx @@ -2,9 +2,8 @@ import type { FC } from "react"; import React from "react"; -import { ChevronDown } from "lucide-react"; // types -import { CycleGroupIcon } from "@plane/propel/icons"; +import { CycleGroupIcon, ChevronDownIcon } from "@plane/propel/icons"; import type { TCycleGroups } from "@plane/types"; // icons import { Row } from "@plane/ui"; @@ -33,7 +32,7 @@ export const CycleListGroupHeader: FC = (props) => { {showCount &&
{`${count ?? "0"}`}
} - = observer((props) => { if (!project) return null; return ( - = observer((props) => {

Transfer work items

diff --git a/apps/web/core/components/dropdowns/cycle/index.tsx b/apps/web/core/components/dropdowns/cycle/index.tsx index 15569315ca3..cb2df468354 100644 --- a/apps/web/core/components/dropdowns/cycle/index.tsx +++ b/apps/web/core/components/dropdowns/cycle/index.tsx @@ -3,10 +3,9 @@ import type { ReactNode } from "react"; import { useRef, useState } from "react"; import { observer } from "mobx-react"; -import { ChevronDown } from "lucide-react"; import { useTranslation } from "@plane/i18n"; // ui -import { CycleIcon } from "@plane/propel/icons"; +import { CycleIcon, ChevronDownIcon } from "@plane/propel/icons"; import { ComboDropDown } from "@plane/ui"; // helpers import { cn } from "@plane/utils"; @@ -126,7 +125,7 @@ export const CycleDropdown: React.FC = observer((props) => { {selectedName ?? placeholder} )} {dropdownArrow && ( -
@@ -210,7 +210,7 @@ const BackgroundButton = (props: ButtonProps) => { {priorityDetails?.title ?? t("common.priority") ?? placeholder} )} {dropdownArrow && ( -