From 216a69f99127fbb1a55702ceaededc7ad96e686f Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Tue, 10 Dec 2024 19:12:24 +0530 Subject: [PATCH 01/11] chore: workspace draft and inbox issue local db mutation (#6180) --- web/core/store/inbox/inbox-issue.store.ts | 2 +- web/core/store/issue/workspace-draft/issue.store.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/core/store/inbox/inbox-issue.store.ts b/web/core/store/inbox/inbox-issue.store.ts index e080225aaf..74e6534d67 100644 --- a/web/core/store/inbox/inbox-issue.store.ts +++ b/web/core/store/inbox/inbox-issue.store.ts @@ -98,7 +98,7 @@ export class InboxIssueStore implements IInboxIssueStore { // If issue accepted sync issue to local db if (status === EInboxIssueStatus.ACCEPTED) { - addIssueToPersistanceLayer(inboxIssue.issue); + addIssueToPersistanceLayer({ ...this.issue, ...inboxIssue.issue }); } } catch { runInAction(() => set(this, "status", previousData.status)); diff --git a/web/core/store/issue/workspace-draft/issue.store.ts b/web/core/store/issue/workspace-draft/issue.store.ts index 6518cff006..e6b0730dbd 100644 --- a/web/core/store/issue/workspace-draft/issue.store.ts +++ b/web/core/store/issue/workspace-draft/issue.store.ts @@ -352,7 +352,7 @@ export class WorkspaceDraftIssues implements IWorkspaceDraftIssues { } // sync issue to local db - addIssueToPersistanceLayer(response); + addIssueToPersistanceLayer({ ...payload, ...response }); // Update draft issue count in workspaceUserInfo this.updateWorkspaceUserDraftIssueCount(workspaceSlug, -1); From 6e56ea4c601370cceaf4bf1ba4cf84a0233b6757 Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Tue, 10 Dec 2024 20:28:51 +0530 Subject: [PATCH 02/11] fix: updated changelog url in apiserver --- apiserver/Dockerfile.dev | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apiserver/Dockerfile.dev b/apiserver/Dockerfile.dev index c81966de4f..3ec8c6340a 100644 --- a/apiserver/Dockerfile.dev +++ b/apiserver/Dockerfile.dev @@ -4,7 +4,7 @@ FROM python:3.12.5-alpine AS backend ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 ENV PIP_DISABLE_PIP_VERSION_CHECK=1 -ENV INSTANCE_CHANGELOG_URL https://api.plane.so/api/public/anchor/8e1c2e4c7bc5493eb7731be3862f6960/pages/ +ENV INSTANCE_CHANGELOG_URL https://sites.plane.so/pages/691ef037bcfe416a902e48cb55f59891/ RUN apk --no-cache add \ "bash~=5.2" \ From f06b1b8c4affbc820ed86a7646f57b08c5de78df Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Tue, 10 Dec 2024 21:02:29 +0530 Subject: [PATCH 03/11] fix: updated package version --- admin/package.json | 2 +- apiserver/package.json | 2 +- live/package.json | 2 +- package.json | 2 +- packages/constants/package.json | 2 +- packages/editor/package.json | 2 +- packages/eslint-config/package.json | 2 +- packages/hooks/package.json | 2 +- packages/tailwind-config-custom/package.json | 2 +- packages/types/package.json | 2 +- packages/typescript-config/package.json | 2 +- packages/ui/package.json | 2 +- packages/utils/package.json | 2 +- space/package.json | 2 +- web/package.json | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/admin/package.json b/admin/package.json index ff9bf6a67d..e2fe4cf331 100644 --- a/admin/package.json +++ b/admin/package.json @@ -1,6 +1,6 @@ { "name": "admin", - "version": "0.24.0", + "version": "0.24.1", "private": true, "scripts": { "dev": "turbo run develop", diff --git a/apiserver/package.json b/apiserver/package.json index 4b44b3898c..6d350d83c9 100644 --- a/apiserver/package.json +++ b/apiserver/package.json @@ -1,4 +1,4 @@ { "name": "plane-api", - "version": "0.24.0" + "version": "0.24.1" } diff --git a/live/package.json b/live/package.json index 18124e3963..e098564e11 100644 --- a/live/package.json +++ b/live/package.json @@ -1,6 +1,6 @@ { "name": "live", - "version": "0.24.0", + "version": "0.24.1", "description": "", "main": "./src/server.ts", "private": true, diff --git a/package.json b/package.json index 0700636092..f14aa4ac79 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "repository": "https://github.com/makeplane/plane.git", - "version": "0.24.0", + "version": "0.24.1", "license": "AGPL-3.0", "private": true, "workspaces": [ diff --git a/packages/constants/package.json b/packages/constants/package.json index ce02a4946b..c1fe71a306 100644 --- a/packages/constants/package.json +++ b/packages/constants/package.json @@ -1,6 +1,6 @@ { "name": "@plane/constants", - "version": "0.24.0", + "version": "0.24.1", "private": true, "main": "./src/index.ts" } diff --git a/packages/editor/package.json b/packages/editor/package.json index 19c361629b..8f7295a0ed 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,6 +1,6 @@ { "name": "@plane/editor", - "version": "0.24.0", + "version": "0.24.1", "description": "Core Editor that powers Plane", "private": true, "main": "./dist/index.mjs", diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 8487dbb47d..66557c74ed 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -1,7 +1,7 @@ { "name": "@plane/eslint-config", "private": true, - "version": "0.24.0", + "version": "0.24.1", "files": [ "library.js", "next.js", diff --git a/packages/hooks/package.json b/packages/hooks/package.json index ad5fe24e5c..b45723305c 100644 --- a/packages/hooks/package.json +++ b/packages/hooks/package.json @@ -1,6 +1,6 @@ { "name": "@plane/hooks", - "version": "0.24.0", + "version": "0.24.1", "description": "React hooks that are shared across multiple apps internally", "private": true, "main": "./dist/index.js", diff --git a/packages/tailwind-config-custom/package.json b/packages/tailwind-config-custom/package.json index 5c31544b3d..a7fac6401b 100644 --- a/packages/tailwind-config-custom/package.json +++ b/packages/tailwind-config-custom/package.json @@ -1,6 +1,6 @@ { "name": "tailwind-config-custom", - "version": "0.24.0", + "version": "0.24.1", "description": "common tailwind configuration across monorepo", "main": "index.js", "private": true, diff --git a/packages/types/package.json b/packages/types/package.json index 9ce0fd077d..6fc823e902 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@plane/types", - "version": "0.24.0", + "version": "0.24.1", "private": true, "types": "./src/index.d.ts", "main": "./src/index.d.ts" diff --git a/packages/typescript-config/package.json b/packages/typescript-config/package.json index 2438773661..3237a184b4 100644 --- a/packages/typescript-config/package.json +++ b/packages/typescript-config/package.json @@ -1,6 +1,6 @@ { "name": "@plane/typescript-config", - "version": "0.24.0", + "version": "0.24.1", "private": true, "files": [ "base.json", diff --git a/packages/ui/package.json b/packages/ui/package.json index 600f0d7265..2cfcc3643c 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -2,7 +2,7 @@ "name": "@plane/ui", "description": "UI components shared across multiple apps internally", "private": true, - "version": "0.24.0", + "version": "0.24.1", "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", diff --git a/packages/utils/package.json b/packages/utils/package.json index 368c14e74e..a0e0ecca14 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@plane/utils", - "version": "0.24.0", + "version": "0.24.1", "description": "Helper functions shared across multiple apps internally", "private": true, "main": "./dist/index.js", diff --git a/space/package.json b/space/package.json index 2c05ef43f7..941f8419fa 100644 --- a/space/package.json +++ b/space/package.json @@ -1,6 +1,6 @@ { "name": "space", - "version": "0.24.0", + "version": "0.24.1", "private": true, "scripts": { "dev": "turbo run develop", diff --git a/web/package.json b/web/package.json index 93fdbb5922..cd0a5ddcca 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "web", - "version": "0.24.0", + "version": "0.24.1", "private": true, "scripts": { "dev": "turbo run develop", From d0f3987aebd98303ef723d7a63a6b6a7c3c53a38 Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Tue, 10 Dec 2024 21:03:44 +0530 Subject: [PATCH 04/11] fix: instance changelog url updated --- apiserver/Dockerfile.api | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apiserver/Dockerfile.api b/apiserver/Dockerfile.api index 97a2b2d410..b0fa447885 100644 --- a/apiserver/Dockerfile.api +++ b/apiserver/Dockerfile.api @@ -4,7 +4,7 @@ FROM python:3.12.5-alpine AS backend ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 ENV PIP_DISABLE_PIP_VERSION_CHECK=1 -ENV INSTANCE_CHANGELOG_URL https://api.plane.so/api/public/anchor/8e1c2e4c7bc5493eb7731be3862f6960/pages/ +ENV INSTANCE_CHANGELOG_URL https://sites.plane.so/pages/691ef037bcfe416a902e48cb55f59891/ WORKDIR /code From 134644fdf12a5b0d7598df61fb2b40c86a99a351 Mon Sep 17 00:00:00 2001 From: Vamsi Krishna <46787868+mathalav55@users.noreply.github.com> Date: Wed, 11 Dec 2024 13:41:19 +0530 Subject: [PATCH 05/11] [WEB-2382]chore:notification files restructuring (#6181) * chore: adjusted increment/decrement for unread count * chore: improved param handling for unread notification count function * chore:file restructuring * fix:notification types * chore:file restructuring * chore:modified notfication types * chore: modified types for notification * chore:removed redundant checks for id --- .../types/src/workspace-notifications.d.ts | 2 +- .../(projects)/notifications/layout.tsx | 2 +- .../workspace-notifications/index.ts | 2 +- .../notification-card/root.tsx | 0 .../workspace-notifications/index.ts | 1 + .../workspace-notifications/root.tsx | 3 ++- .../sidebar/notification-card/index.ts | 1 - web/core/store/notifications/notification.ts | 18 ++---------------- 8 files changed, 8 insertions(+), 21 deletions(-) rename web/{core/components/workspace-notifications/sidebar => ce/components/workspace-notifications}/notification-card/root.tsx (100%) rename web/{ce => core}/components/workspace-notifications/root.tsx (97%) diff --git a/packages/types/src/workspace-notifications.d.ts b/packages/types/src/workspace-notifications.d.ts index 7d960015b9..0e0e15af17 100644 --- a/packages/types/src/workspace-notifications.d.ts +++ b/packages/types/src/workspace-notifications.d.ts @@ -35,7 +35,7 @@ export type TNotificationData = { }; export type TNotification = { - id: string | undefined; + id: string; title: string | undefined; data: TNotificationData | undefined; entity_identifier: string | undefined; diff --git a/web/app/[workspaceSlug]/(projects)/notifications/layout.tsx b/web/app/[workspaceSlug]/(projects)/notifications/layout.tsx index 7d71948d83..e3d7303636 100644 --- a/web/app/[workspaceSlug]/(projects)/notifications/layout.tsx +++ b/web/app/[workspaceSlug]/(projects)/notifications/layout.tsx @@ -1,7 +1,7 @@ "use client"; // components -import { NotificationsSidebarRoot } from "@/plane-web/components/workspace-notifications"; +import { NotificationsSidebarRoot } from "@/components/workspace-notifications"; export default function ProjectInboxIssuesLayout({ children }: { children: React.ReactNode }) { return ( diff --git a/web/ce/components/workspace-notifications/index.ts b/web/ce/components/workspace-notifications/index.ts index c8711b96a4..18c4afa968 100644 --- a/web/ce/components/workspace-notifications/index.ts +++ b/web/ce/components/workspace-notifications/index.ts @@ -1 +1 @@ -export * from './root' \ No newline at end of file +export * from "./notification-card/root"; diff --git a/web/core/components/workspace-notifications/sidebar/notification-card/root.tsx b/web/ce/components/workspace-notifications/notification-card/root.tsx similarity index 100% rename from web/core/components/workspace-notifications/sidebar/notification-card/root.tsx rename to web/ce/components/workspace-notifications/notification-card/root.tsx diff --git a/web/core/components/workspace-notifications/index.ts b/web/core/components/workspace-notifications/index.ts index 2682c9114f..8bc361a7c9 100644 --- a/web/core/components/workspace-notifications/index.ts +++ b/web/core/components/workspace-notifications/index.ts @@ -1,2 +1,3 @@ export * from "./notification-app-sidebar-option"; export * from "./sidebar"; +export * from "./root"; diff --git a/web/ce/components/workspace-notifications/root.tsx b/web/core/components/workspace-notifications/root.tsx similarity index 97% rename from web/ce/components/workspace-notifications/root.tsx rename to web/core/components/workspace-notifications/root.tsx index 35c61263df..fa17060d59 100644 --- a/web/ce/components/workspace-notifications/root.tsx +++ b/web/core/components/workspace-notifications/root.tsx @@ -11,7 +11,6 @@ import { NotificationEmptyState, NotificationSidebarHeader, AppliedFilters, - NotificationCardListRoot, } from "@/components/workspace-notifications"; // constants import { NOTIFICATION_TABS, TNotificationTab } from "@/constants/notification"; @@ -21,6 +20,8 @@ import { getNumberCount } from "@/helpers/string.helper"; // hooks import { useWorkspace, useWorkspaceNotifications } from "@/hooks/store"; +import { NotificationCardListRoot } from "@/plane-web/components/workspace-notifications"; + export const NotificationsSidebarRoot: FC = observer(() => { const { workspaceSlug } = useParams(); // hooks diff --git a/web/core/components/workspace-notifications/sidebar/notification-card/index.ts b/web/core/components/workspace-notifications/sidebar/notification-card/index.ts index d4000aa9e7..8c086a5a81 100644 --- a/web/core/components/workspace-notifications/sidebar/notification-card/index.ts +++ b/web/core/components/workspace-notifications/sidebar/notification-card/index.ts @@ -1,3 +1,2 @@ -export * from "./root"; export * from "./item"; export * from "./options"; diff --git a/web/core/store/notifications/notification.ts b/web/core/store/notifications/notification.ts index dff2755fdc..74c0bf38cd 100644 --- a/web/core/store/notifications/notification.ts +++ b/web/core/store/notifications/notification.ts @@ -26,7 +26,7 @@ export interface INotification extends TNotification { export class Notification implements INotification { // observables - id: string | undefined = undefined; + id: string; title: string | undefined = undefined; data: TNotificationData | undefined = undefined; entity_identifier: string | undefined = undefined; @@ -54,6 +54,7 @@ export class Notification implements INotification { private store: CoreRootStore, private notification: TNotification ) { + this.id = this.notification.id; makeObservable(this, { // observables id: observable.ref, @@ -90,7 +91,6 @@ export class Notification implements INotification { snoozeNotification: action, unSnoozeNotification: action, }); - this.id = this.notification.id; this.title = this.notification.title; this.data = this.notification.data; this.entity_identifier = this.notification.entity_identifier; @@ -169,8 +169,6 @@ export class Notification implements INotification { workspaceSlug: string, payload: Partial ): Promise => { - if (!this.id) return undefined; - try { const notification = await workspaceNotificationService.updateNotificationById(workspaceSlug, this.id, payload); if (notification) { @@ -188,8 +186,6 @@ export class Notification implements INotification { * @returns { TNotification | undefined } */ markNotificationAsRead = async (workspaceSlug: string): Promise => { - if (!this.id) return undefined; - const currentNotificationReadAt = this.read_at; try { const payload: Partial = { @@ -215,8 +211,6 @@ export class Notification implements INotification { * @returns { TNotification | undefined } */ markNotificationAsUnRead = async (workspaceSlug: string): Promise => { - if (!this.id) return undefined; - const currentNotificationReadAt = this.read_at; try { const payload: Partial = { @@ -242,8 +236,6 @@ export class Notification implements INotification { * @returns { TNotification | undefined } */ archiveNotification = async (workspaceSlug: string): Promise => { - if (!this.id) return undefined; - const currentNotificationArchivedAt = this.archived_at; try { const payload: Partial = { @@ -267,8 +259,6 @@ export class Notification implements INotification { * @returns { TNotification | undefined } */ unArchiveNotification = async (workspaceSlug: string): Promise => { - if (!this.id) return undefined; - const currentNotificationArchivedAt = this.archived_at; try { const payload: Partial = { @@ -293,8 +283,6 @@ export class Notification implements INotification { * @returns { TNotification | undefined } */ snoozeNotification = async (workspaceSlug: string, snoozeTill: Date): Promise => { - if (!this.id) return undefined; - const currentNotificationSnoozeTill = this.snoozed_till; try { const payload: Partial = { @@ -315,8 +303,6 @@ export class Notification implements INotification { * @returns { TNotification | undefined } */ unSnoozeNotification = async (workspaceSlug: string): Promise => { - if (!this.id) return undefined; - const currentNotificationSnoozeTill = this.snoozed_till; try { const payload: Partial = { From 5a9ae666808a586abe37c69c33a23abc5524da7e Mon Sep 17 00:00:00 2001 From: rahulramesha <71900764+rahulramesha@users.noreply.github.com> Date: Wed, 11 Dec 2024 13:43:48 +0530 Subject: [PATCH 06/11] chore: Remove shouldIgnoreDependencies flags while dragging in timeline view (#6150) * remove shouldEnable dependency flags for timeline view * chore: error handling --------- Co-authored-by: Prateek Shourya --- .../blockResizables/use-gantt-resizable.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/web/core/components/gantt-chart/helpers/blockResizables/use-gantt-resizable.ts b/web/core/components/gantt-chart/helpers/blockResizables/use-gantt-resizable.ts index e6a1a887e0..c96a257250 100644 --- a/web/core/components/gantt-chart/helpers/blockResizables/use-gantt-resizable.ts +++ b/web/core/components/gantt-chart/helpers/blockResizables/use-gantt-resizable.ts @@ -1,6 +1,6 @@ import { useRef, useState } from "react"; // Plane -import { setToast } from "@plane/ui"; +import { setToast, TOAST_TYPE } from "@plane/ui"; // hooks import { useTimeLineChartStore } from "@/hooks/use-timeline-chart"; // @@ -103,7 +103,7 @@ export const useGanttResizable = ( const deltaWidth = Math.round((width - (block.position?.width ?? 0)) / dayWidth) * dayWidth; // call update blockPosition - if (deltaWidth || deltaLeft) updateBlockPosition(block.id, deltaLeft, deltaWidth, dragDirection !== "move"); + if (deltaWidth || deltaLeft) updateBlockPosition(block.id, deltaLeft, deltaWidth); }; // remove event listeners and call updateBlockDates @@ -119,10 +119,14 @@ export const useGanttResizable = ( (dragDirection === "left" && !block.start_date) || (dragDirection === "right" && !block.target_date); try { - const blockUpdates = getUpdatedPositionAfterDrag(block.id, shouldUpdateHalfBlock, dragDirection !== "move"); - updateBlockDates && updateBlockDates(blockUpdates); - } catch (e) { - setToast; + const blockUpdates = getUpdatedPositionAfterDrag(block.id, shouldUpdateHalfBlock); + if (updateBlockDates) updateBlockDates(blockUpdates); + } catch { + setToast({ + type: TOAST_TYPE.ERROR, + title: "Error", + message: "Something went wrong while updating block dates", + }); } setIsDragging(false); From 0ac68f27311c212c9cd4603fd362b165370e9b71 Mon Sep 17 00:00:00 2001 From: Prateek Shourya Date: Wed, 11 Dec 2024 15:14:15 +0530 Subject: [PATCH 07/11] improvement: refactored issue grouping logic to access MobX store directly (#6134) * improvement: refactored issue grouping logic to access MobX store directly * chore: minor updates --- packages/types/src/issues.d.ts | 2 +- .../issues/issue-layouts/kanban/default.tsx | 30 +-- .../issues/issue-layouts/kanban/swimlanes.tsx | 44 +--- .../issues/issue-layouts/list/default.tsx | 25 +- .../components/issues/issue-layouts/utils.tsx | 228 +++++++++--------- web/core/store/cycle.store.ts | 96 ++------ web/core/store/module.store.ts | 22 +- 7 files changed, 172 insertions(+), 275 deletions(-) diff --git a/packages/types/src/issues.d.ts b/packages/types/src/issues.d.ts index eff81f857b..9bbfa36b1f 100644 --- a/packages/types/src/issues.d.ts +++ b/packages/types/src/issues.d.ts @@ -216,7 +216,7 @@ export type GroupByColumnTypes = export interface IGroupByColumn { id: string; name: string; - icon: ReactElement | undefined; + icon?: ReactElement | undefined; payload: Partial; isDropDisabled?: boolean; dropErrorMessage?: string; diff --git a/web/core/components/issues/issue-layouts/kanban/default.tsx b/web/core/components/issues/issue-layouts/kanban/default.tsx index d1144995c8..f1107c8fb7 100644 --- a/web/core/components/issues/issue-layouts/kanban/default.tsx +++ b/web/core/components/issues/issue-layouts/kanban/default.tsx @@ -18,7 +18,7 @@ import { ContentWrapper } from "@plane/ui"; import RenderIfVisible from "@/components/core/render-if-visible-HOC"; import { KanbanColumnLoader } from "@/components/ui"; // hooks -import { useCycle, useKanbanView, useLabel, useMember, useModule, useProject, useProjectState } from "@/hooks/store"; +import { useKanbanView } from "@/hooks/store"; import { useIssueStoreType } from "@/hooks/use-issue-layout-store"; // types // parent components @@ -87,30 +87,16 @@ export const KanBan: React.FC = observer((props) => { dropErrorMessage, subGroupIndex = 0, } = props; - + // store hooks const storeType = useIssueStoreType(); - - const member = useMember(); - const project = useProject(); - const label = useLabel(); - const cycle = useCycle(); - const moduleInfo = useModule(); - const projectState = useProjectState(); const issueKanBanView = useKanbanView(); - + // derived values const isDragDisabled = !issueKanBanView?.getCanUserDragDrop(group_by, sub_group_by); - - const list = getGroupByColumns( - group_by as GroupByColumnTypes, - project, - cycle, - moduleInfo, - label, - projectState, - member, - true, - isWorkspaceLevel(storeType) - ); + const list = getGroupByColumns({ + groupBy: group_by as GroupByColumnTypes, + includeNone: true, + isWorkspaceLevel: isWorkspaceLevel(storeType), + }); if (!list) return null; diff --git a/web/core/components/issues/issue-layouts/kanban/swimlanes.tsx b/web/core/components/issues/issue-layouts/kanban/swimlanes.tsx index ba1dce5c1c..e2d52e7ca5 100644 --- a/web/core/components/issues/issue-layouts/kanban/swimlanes.tsx +++ b/web/core/components/issues/issue-layouts/kanban/swimlanes.tsx @@ -15,7 +15,6 @@ import { // UI import { Row } from "@plane/ui"; // hooks -import { useCycle, useLabel, useMember, useModule, useProject, useProjectState } from "@/hooks/store"; import { useIssueStoreType } from "@/hooks/use-issue-layout-store"; // components import { TRenderQuickActions } from "../list/list-view-types"; @@ -262,38 +261,19 @@ export const KanBanSwimLanes: React.FC = observer((props) => { quickAddCallback, scrollableContainerRef, } = props; - + // store hooks const storeType = useIssueStoreType(); - - const member = useMember(); - const project = useProject(); - const label = useLabel(); - const cycle = useCycle(); - const projectModule = useModule(); - const projectState = useProjectState(); - - const groupByList = getGroupByColumns( - group_by as GroupByColumnTypes, - project, - cycle, - projectModule, - label, - projectState, - member, - true, - isWorkspaceLevel(storeType) - ); - const subGroupByList = getGroupByColumns( - sub_group_by as GroupByColumnTypes, - project, - cycle, - projectModule, - label, - projectState, - member, - true, - isWorkspaceLevel(storeType) - ); + // derived values + const groupByList = getGroupByColumns({ + groupBy: group_by as GroupByColumnTypes, + includeNone: true, + isWorkspaceLevel: isWorkspaceLevel(storeType), + }); + const subGroupByList = getGroupByColumns({ + groupBy: sub_group_by as GroupByColumnTypes, + includeNone: true, + isWorkspaceLevel: isWorkspaceLevel(storeType), + }); if (!groupByList || !subGroupByList) return null; diff --git a/web/core/components/issues/issue-layouts/list/default.tsx b/web/core/components/issues/issue-layouts/list/default.tsx index fca8d68eb2..01a81cc61b 100644 --- a/web/core/components/issues/issue-layouts/list/default.tsx +++ b/web/core/components/issues/issue-layouts/list/default.tsx @@ -18,9 +18,7 @@ import { } from "@plane/types"; // components import { MultipleSelectGroup } from "@/components/core"; - // hooks -import { useCycle, useLabel, useMember, useModule, useProject, useProjectState } from "@/hooks/store"; import { useIssueStoreType } from "@/hooks/use-issue-layout-store"; // plane web components import { IssueBulkOperationsRoot } from "@/plane-web/components/issues"; @@ -75,29 +73,16 @@ export const List: React.FC = observer((props) => { } = props; const storeType = useIssueStoreType(); - // store hooks - const member = useMember(); - const project = useProject(); - const label = useLabel(); - const projectState = useProjectState(); - const cycle = useCycle(); - const projectModule = useModule(); // plane web hooks const isBulkOperationsEnabled = useBulkOperationStatus(); const containerRef = useRef(null); - const groups = getGroupByColumns( - group_by as GroupByColumnTypes, - project, - cycle, - projectModule, - label, - projectState, - member, - true, - isWorkspaceLevel(storeType) - ); + const groups = getGroupByColumns({ + groupBy: group_by as GroupByColumnTypes, + includeNone: true, + isWorkspaceLevel: isWorkspaceLevel(storeType), + }); // Enable Auto Scroll for Main Kanban useEffect(() => { diff --git a/web/core/components/issues/issue-layouts/utils.tsx b/web/core/components/issues/issue-layouts/utils.tsx index 21f40a5bc1..9cb6fa8f5b 100644 --- a/web/core/components/issues/issue-layouts/utils.tsx +++ b/web/core/components/issues/issue-layouts/utils.tsx @@ -36,13 +36,8 @@ import { STATE_GROUPS } from "@/constants/state"; import { renderFormattedDate } from "@/helpers/date-time.helper"; import { getFileURL } from "@/helpers/file.helper"; // store -import { ICycleStore } from "@/store/cycle.store"; +import { store } from "@/lib/store-context"; import { ISSUE_FILTER_DEFAULT_DATA } from "@/store/issue/helpers/base-issues.store"; -import { ILabelStore } from "@/store/label.store"; -import { IMemberRootStore } from "@/store/member"; -import { IModuleStore } from "@/store/module.store"; -import { IProjectStore } from "@/store/project/project.store"; -import { IStateStore } from "@/store/state.store"; export const HIGHLIGHT_CLASS = "highlight"; export const HIGHLIGHT_WITH_LINE = "highlight-with-line"; @@ -65,51 +60,61 @@ export type IssueUpdates = { export const isWorkspaceLevel = (type: EIssuesStoreType) => [EIssuesStoreType.PROFILE, EIssuesStoreType.GLOBAL].includes(type) ? true : false; -export const getGroupByColumns = ( - groupBy: GroupByColumnTypes | null, - project: IProjectStore, - cycle: ICycleStore, - module: IModuleStore, - label: ILabelStore, - projectState: IStateStore, - member: IMemberRootStore, - includeNone?: boolean, - isWorkspaceLevel?: boolean -): IGroupByColumn[] | undefined => { - switch (groupBy) { - case "project": - return getProjectColumns(project); - case "cycle": - return getCycleColumns(project, cycle); - case "module": - return getModuleColumns(project, module); - case "state": - return getStateColumns(projectState); - case "state_detail.group": - return getStateGroupColumns(); - case "priority": - return getPriorityColumns(); - case "labels": - return getLabelsColumns(label, isWorkspaceLevel) as any; - case "assignees": - return getAssigneeColumns(member) as any; - case "created_by": - return getCreatedByColumns(member) as any; - default: - if (includeNone) return [{ id: `All Issues`, name: `All Issues`, payload: {}, icon: undefined }]; - } +type TGetGroupByColumns = { + groupBy: GroupByColumnTypes | null; + includeNone: boolean; + isWorkspaceLevel: boolean; }; -const getProjectColumns = (project: IProjectStore): IGroupByColumn[] | undefined => { - const { workspaceProjectIds: projectIds, projectMap } = project; +// NOTE: Type of groupBy is different compared to what's being passed from the components. +// We are using `as` to typecast it to the expected type. +// It can break the includeNone logic if not handled properly. +export const getGroupByColumns = ({ + groupBy, + includeNone, + isWorkspaceLevel, +}: TGetGroupByColumns): IGroupByColumn[] | undefined => { + // If no groupBy is specified and includeNone is true, return "All Issues" group + if (!groupBy && includeNone) { + return [ + { + id: "All Issues", + name: "All Issues", + payload: {}, + icon: undefined, + }, + ]; + } + // Return undefined if no valid groupBy + if (!groupBy) return undefined; + + // Map of group by options to their corresponding column getter functions + const groupByColumnMap: Record IGroupByColumn[] | undefined> = { + project: getProjectColumns, + cycle: getCycleColumns, + module: getModuleColumns, + state: getStateColumns, + "state_detail.group": getStateGroupColumns, + priority: getPriorityColumns, + labels: () => getLabelsColumns(isWorkspaceLevel), + assignees: getAssigneeColumns, + created_by: getCreatedByColumns, + }; + + // Get and return the columns for the specified group by option + return groupByColumnMap[groupBy]?.(); +}; + +const getProjectColumns = (): IGroupByColumn[] | undefined => { + const { joinedProjectIds: projectIds, projectMap } = store.projectRoot.project; + // Return undefined if no project ids if (!projectIds) return; - + // Map project ids to project columns return projectIds - .filter((projectId) => !!projectMap[projectId]) - .map((projectId) => { + .map((projectId: string) => { const project = projectMap[projectId]; - + if (!project) return; return { id: project.id, name: project.name, @@ -120,78 +125,71 @@ const getProjectColumns = (project: IProjectStore): IGroupByColumn[] | undefined ), payload: { project_id: project.id }, }; - }) as any; + }) + .filter((column) => column !== undefined) as IGroupByColumn[]; }; -const getCycleColumns = (projectStore: IProjectStore, cycleStore: ICycleStore): IGroupByColumn[] | undefined => { - const { currentProjectDetails } = projectStore; - const { getProjectCycleIds, getCycleById } = cycleStore; - +const getCycleColumns = (): IGroupByColumn[] | undefined => { + const { currentProjectDetails } = store.projectRoot.project; + // Check for the current project details if (!currentProjectDetails || !currentProjectDetails?.id) return; - - const cycleIds = currentProjectDetails?.id ? getProjectCycleIds(currentProjectDetails?.id) : undefined; - if (!cycleIds) return; - - const cycles = []; - - cycleIds.map((cycleId) => { - const cycle = getCycleById(cycleId); - if (cycle) { - const cycleStatus = cycle.status ? (cycle.status.toLocaleLowerCase() as TCycleGroups) : "draft"; - const isDropDisabled = cycleStatus === "completed"; - cycles.push({ - id: cycle.id, - name: cycle.name, - icon: , - payload: { cycle_id: cycle.id }, - isDropDisabled, - dropErrorMessage: isDropDisabled ? "Issue cannot be moved to completed cycles" : undefined, - }); - } + const { getProjectCycleDetails } = store.cycle; + // Get the cycle details for the current project + const cycleDetails = currentProjectDetails?.id ? getProjectCycleDetails(currentProjectDetails?.id) : undefined; + // Map the cycle details to the group by columns + const cycles: IGroupByColumn[] = []; + cycleDetails?.map((cycle) => { + const cycleStatus = cycle.status ? (cycle.status.toLocaleLowerCase() as TCycleGroups) : "draft"; + const isDropDisabled = cycleStatus === "completed"; + cycles.push({ + id: cycle.id, + name: cycle.name, + icon: , + payload: { cycle_id: cycle.id }, + isDropDisabled, + dropErrorMessage: isDropDisabled ? "Issue cannot be moved to completed cycles" : undefined, + }); }); cycles.push({ id: "None", name: "None", icon: , + payload: {}, }); - - return cycles as any; + return cycles; }; -const getModuleColumns = (projectStore: IProjectStore, moduleStore: IModuleStore): IGroupByColumn[] | undefined => { - const { currentProjectDetails } = projectStore; - const { getProjectModuleIds, getModuleById } = moduleStore; - +const getModuleColumns = (): IGroupByColumn[] | undefined => { + // get current project details + const { currentProjectDetails } = store.projectRoot.project; if (!currentProjectDetails || !currentProjectDetails?.id) return; - - const moduleIds = currentProjectDetails?.id ? getProjectModuleIds(currentProjectDetails?.id) : undefined; - if (!moduleIds) return; - - const modules = []; - - moduleIds.map((moduleId) => { - const moduleInfo = getModuleById(moduleId); - if (moduleInfo) - modules.push({ - id: moduleInfo.id, - name: moduleInfo.name, - icon: , - payload: { module_ids: [moduleInfo.id] }, - }); - }) as any; + // get project module ids and module details + const { getProjectModuleDetails } = store.module; + // get module details + const moduleDetails = currentProjectDetails?.id ? getProjectModuleDetails(currentProjectDetails?.id) : undefined; + // map module details to group by columns + const modules: IGroupByColumn[] = []; + moduleDetails?.map((module) => { + modules.push({ + id: module.id, + name: module.name, + icon: , + payload: { module_ids: [module.id] }, + }); + }); modules.push({ id: "None", name: "None", icon: , + payload: {}, }); - - return modules as any; + return modules; }; -const getStateColumns = (projectState: IStateStore): IGroupByColumn[] | undefined => { - const { projectStates } = projectState; +const getStateColumns = (): IGroupByColumn[] | undefined => { + const { projectStates } = store.state; if (!projectStates) return; - + // map project states to group by columns return projectStates.map((state) => ({ id: state.id, name: state.name, @@ -201,12 +199,12 @@ const getStateColumns = (projectState: IStateStore): IGroupByColumn[] | undefine ), payload: { state_id: state.id }, - })) as any; + })); }; -const getStateGroupColumns = () => { +const getStateGroupColumns = (): IGroupByColumn[] => { const stateGroups = STATE_GROUPS; - + // map state groups to group by columns return Object.values(stateGroups).map((stateGroup) => ({ id: stateGroup.key, name: stateGroup.label, @@ -219,9 +217,9 @@ const getStateGroupColumns = () => { })); }; -const getPriorityColumns = () => { +const getPriorityColumns = (): IGroupByColumn[] => { const priorities = ISSUE_PRIORITIES; - + // map priorities to group by columns return priorities.map((priority) => ({ id: priority.key, name: priority.title, @@ -230,14 +228,14 @@ const getPriorityColumns = () => { })); }; -const getLabelsColumns = (label: ILabelStore, isWorkspaceLevel: boolean = false) => { - const { workspaceLabels, projectLabels } = label; - +const getLabelsColumns = (isWorkspaceLevel: boolean = false): IGroupByColumn[] => { + const { workspaceLabels, projectLabels } = store.label; + // map labels to group by columns const labels = [ ...(isWorkspaceLevel ? workspaceLabels || [] : projectLabels || []), { id: "None", name: "None", color: "#666" }, ]; - + // map labels to group by columns return labels.map((label) => ({ id: label.id, name: label.name, @@ -248,15 +246,14 @@ const getLabelsColumns = (label: ILabelStore, isWorkspaceLevel: boolean = false) })); }; -const getAssigneeColumns = (member: IMemberRootStore) => { +const getAssigneeColumns = (): IGroupByColumn[] | undefined => { const { project: { projectMemberIds }, getUserDetails, - } = member; - + } = store.memberRoot; if (!projectMemberIds) return; - - const assigneeColumns: any = projectMemberIds.map((memberId) => { + // Map project member ids to group by assignee columns + const assigneeColumns: IGroupByColumn[] = projectMemberIds.map((memberId) => { const member = getUserDetails(memberId); return { id: memberId, @@ -265,20 +262,17 @@ const getAssigneeColumns = (member: IMemberRootStore) => { payload: { assignee_ids: [memberId] }, }; }); - assigneeColumns.push({ id: "None", name: "None", icon: , payload: {} }); - return assigneeColumns; }; -const getCreatedByColumns = (member: IMemberRootStore) => { +const getCreatedByColumns = (): IGroupByColumn[] | undefined => { const { project: { projectMemberIds }, getUserDetails, - } = member; - + } = store.memberRoot; if (!projectMemberIds) return; - + // Map project member ids to group by created by columns return projectMemberIds.map((memberId) => { const member = getUserDetails(memberId); return { diff --git a/web/core/store/cycle.store.ts b/web/core/store/cycle.store.ts index dd90cfe1d5..4e56e56c11 100644 --- a/web/core/store/cycle.store.ts +++ b/web/core/store/cycle.store.ts @@ -1,4 +1,4 @@ -import { isFuture, isPast, isToday } from "date-fns"; +import { isPast, isToday } from "date-fns"; import isEmpty from "lodash/isEmpty"; import set from "lodash/set"; import sortBy from "lodash/sortBy"; @@ -7,16 +7,14 @@ import { computedFn } from "mobx-utils"; // types import { ICycle, - CycleDateCheckData, TCyclePlotType, TProgressSnapshot, TCycleEstimateDistribution, TCycleDistribution, TCycleEstimateType, - TCycleProgress, } from "@plane/types"; // helpers -import { orderCycles, shouldFilterCycle, formatActiveCycle } from "@/helpers/cycle.helper"; +import { orderCycles, shouldFilterCycle } from "@/helpers/cycle.helper"; import { getDate } from "@/helpers/date-time.helper"; import { DistributionUpdates, updateDistribution } from "@/helpers/distribution-update.helper"; // services @@ -42,21 +40,18 @@ export interface ICycleStore { // computed currentProjectCycleIds: string[] | null; currentProjectCompletedCycleIds: string[] | null; - currentProjectUpcomingCycleIds: string[] | null; currentProjectIncompleteCycleIds: string[] | null; - currentProjectDraftCycleIds: string[] | null; currentProjectActiveCycleId: string | null; currentProjectArchivedCycleIds: string[] | null; currentProjectActiveCycle: ICycle | null; // computed actions - getActiveCycleProgress: (cycleId?: string) => { cycle: ICycle; isBurnDown: boolean; isTypeIssue: boolean } | null; getFilteredCycleIds: (projectId: string, sortByManual: boolean) => string[] | null; getFilteredCompletedCycleIds: (projectId: string) => string[] | null; getFilteredArchivedCycleIds: (projectId: string) => string[] | null; getCycleById: (cycleId: string) => ICycle | null; getCycleNameById: (cycleId: string) => string | undefined; - getActiveCycleById: (cycleId: string) => ICycle | null; + getProjectCycleDetails: (projectId: string) => ICycle[] | null; getProjectCycleIds: (projectId: string) => string[] | null; getPlotTypeByCycleId: (cycleId: string) => TCyclePlotType; getEstimateTypeByCycleId: (cycleId: string) => TCycleEstimateType; @@ -64,7 +59,6 @@ export interface ICycleStore { // actions updateCycleDistribution: (distributionUpdates: DistributionUpdates, cycleId: string) => void; - validateDate: (workspaceSlug: string, projectId: string, payload: CycleDateCheckData) => Promise; setPlotType: (cycleId: string, plotType: TCyclePlotType) => void; setEstimateType: (cycleId: string, estimateType: TCycleEstimateType) => void; // fetch @@ -130,15 +124,12 @@ export class CycleStore implements ICycleStore { // computed currentProjectCycleIds: computed, currentProjectCompletedCycleIds: computed, - currentProjectUpcomingCycleIds: computed, currentProjectIncompleteCycleIds: computed, - currentProjectDraftCycleIds: computed, currentProjectActiveCycleId: computed, currentProjectArchivedCycleIds: computed, currentProjectActiveCycle: computed, // actions - setPlotType: action, setEstimateType: action, fetchWorkspaceCycles: action, fetchAllCycles: action, @@ -195,22 +186,6 @@ export class CycleStore implements ICycleStore { return completedCycleIds; } - /** - * returns all upcoming cycle ids for a project - */ - get currentProjectUpcomingCycleIds() { - const projectId = this.rootStore.router.projectId; - if (!projectId || !this.fetchedMap[projectId]) return null; - let upcomingCycles = Object.values(this.cycleMap ?? {}).filter((c) => { - const startDate = getDate(c.start_date); - const isStartDateUpcoming = startDate && isFuture(startDate); - return c.project_id === projectId && isStartDateUpcoming && !c?.archived_at; - }); - upcomingCycles = sortBy(upcomingCycles, [(c) => c.sort_order]); - const upcomingCycleIds = upcomingCycles.map((c) => c.id); - return upcomingCycleIds; - } - /** * returns all incomplete cycle ids for a project */ @@ -227,20 +202,6 @@ export class CycleStore implements ICycleStore { return incompleteCycleIds; } - /** - * returns all draft cycle ids for a project - */ - get currentProjectDraftCycleIds() { - const projectId = this.rootStore.router.projectId; - if (!projectId || !this.fetchedMap[projectId]) return null; - let draftCycles = Object.values(this.cycleMap ?? {}).filter( - (c) => c.project_id === projectId && !c.start_date && !c.end_date && !c?.archived_at - ); - draftCycles = sortBy(draftCycles, [(c) => c.sort_order]); - const draftCycleIds = draftCycles.map((c) => c.id); - return draftCycleIds; - } - /** * returns active cycle id for a project */ @@ -285,19 +246,6 @@ export class CycleStore implements ICycleStore { } else return false; }); - /** - * returns active cycle progress for a project - */ - getActiveCycleProgress = computedFn((cycleId?: string) => { - const cycle = cycleId ? this.cycleMap[cycleId] : this.currentProjectActiveCycle; - if (!cycle) return null; - - const isTypeIssue = this.getEstimateTypeByCycleId(cycle.id) === "issues"; - const isBurnDown = this.getPlotTypeByCycleId(cycle.id) === "burndown"; - - return { cycle, isTypeIssue, isBurnDown }; - }); - /** * @description returns filtered cycle ids based on display filters and filters * @param {TCycleDisplayFilters} displayFilters @@ -379,37 +327,28 @@ export class CycleStore implements ICycleStore { getCycleNameById = computedFn((cycleId: string): string => this.cycleMap?.[cycleId]?.name); /** - * @description returns active cycle details by cycle id - * @param cycleId - * @returns + * @description returns list of cycle details of the project id passed as argument + * @param projectId */ - getActiveCycleById = computedFn((cycleId: string): ICycle | null => - this.activeCycleIdMap?.[cycleId] && this.cycleMap?.[cycleId] ? this.cycleMap?.[cycleId] : null - ); + getProjectCycleDetails = computedFn((projectId: string): ICycle[] | null => { + if (!this.fetchedMap[projectId]) return null; + + let cycles = Object.values(this.cycleMap ?? {}).filter((c) => c.project_id === projectId && !c?.archived_at); + cycles = sortBy(cycles, [(c) => c.sort_order]); + return cycles || null; + }); /** * @description returns list of cycle ids of the project id passed as argument * @param projectId */ getProjectCycleIds = computedFn((projectId: string): string[] | null => { - if (!this.fetchedMap[projectId]) return null; - - let cycles = Object.values(this.cycleMap ?? {}).filter((c) => c.project_id === projectId && !c?.archived_at); - cycles = sortBy(cycles, [(c) => c.sort_order]); + const cycles = this.getProjectCycleDetails(projectId); + if (!cycles) return null; const cycleIds = cycles.map((c) => c.id); return cycleIds || null; }); - /** - * @description validates cycle dates - * @param workspaceSlug - * @param projectId - * @param payload - * @returns - */ - validateDate = async (workspaceSlug: string, projectId: string, payload: CycleDateCheckData) => - await this.cycleService.cycleDateCheck(workspaceSlug, projectId, payload); - /** * @description gets the plot type for the cycle store * @param {TCyclePlotType} plotType @@ -473,14 +412,16 @@ export class CycleStore implements ICycleStore { runInAction(() => { response.forEach((cycle) => { set(this.cycleMap, [cycle.id], cycle); - cycle.status?.toLowerCase() === "current" && set(this.activeCycleIdMap, [cycle.id], true); + if (cycle.status?.toLowerCase() === "current") { + set(this.activeCycleIdMap, [cycle.id], true); + } }); set(this.fetchedMap, projectId, true); this.loader = false; }); return response; }); - } catch (error) { + } catch { this.loader = false; return undefined; } @@ -553,6 +494,7 @@ export class CycleStore implements ICycleStore { * @param cycleId * @returns */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars fetchActiveCycleProgressPro = action(async (workspaceSlug: string, projectId: string, cycleId: string) => {}); /** diff --git a/web/core/store/module.store.ts b/web/core/store/module.store.ts index 787b8b091e..06a08b92c1 100644 --- a/web/core/store/module.store.ts +++ b/web/core/store/module.store.ts @@ -32,6 +32,7 @@ export interface IModuleStore { getFilteredArchivedModuleIds: (projectId: string) => string[] | null; getModuleById: (moduleId: string) => IModule | null; getModuleNameById: (moduleId: string) => string; + getProjectModuleDetails: (projectId: string) => IModule[] | null; getProjectModuleIds: (projectId: string) => string[] | null; getPlotTypeByModuleId: (moduleId: string) => TModulePlotType; // actions @@ -210,15 +211,24 @@ export class ModulesStore implements IModuleStore { */ getModuleNameById = computedFn((moduleId: string) => this.moduleMap?.[moduleId]?.name); + /** + * @description returns list of module details of the project id passed as argument + * @param projectId + */ + getProjectModuleDetails = computedFn((projectId: string) => { + if (!this.fetchedMap[projectId]) return null; + let projectModules = Object.values(this.moduleMap).filter((m) => m.project_id === projectId && !m.archived_at); + projectModules = sortBy(projectModules, [(m) => m.sort_order]); + return projectModules; + }); + /** * @description returns list of module ids of the project id passed as argument * @param projectId */ getProjectModuleIds = computedFn((projectId: string) => { - if (!this.fetchedMap[projectId]) return null; - - let projectModules = Object.values(this.moduleMap).filter((m) => m.project_id === projectId && !m.archived_at); - projectModules = sortBy(projectModules, [(m) => m.sort_order]); + const projectModules = this.getProjectModuleDetails(projectId); + if (!projectModules) return null; const projectModuleIds = projectModules.map((m) => m.id); return projectModuleIds; }); @@ -282,7 +292,7 @@ export class ModulesStore implements IModuleStore { }); return response; }); - } catch (error) { + } catch { this.loader = false; return undefined; } @@ -308,7 +318,7 @@ export class ModulesStore implements IModuleStore { }); return projectModules; }); - } catch (error) { + } catch { this.loader = false; return undefined; } From 7fca7fd86cef2df01e132bf578761695bab5e724 Mon Sep 17 00:00:00 2001 From: Vamsi Krishna <46787868+mathalav55@users.noreply.github.com> Date: Wed, 11 Dec 2024 16:29:39 +0530 Subject: [PATCH 08/11] [WEB-2774] fix:favorites reorder (#6179) * fix:favorites reorder * chore: added error handling Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../sidebar/favorites/favorite-folder.tsx | 11 ++++-- .../sidebar/favorites/favorite-items/root.tsx | 2 +- .../sidebar/favorites/favorites-menu.tsx | 20 ++++------- web/core/store/favorite.store.ts | 34 +++++-------------- 4 files changed, 25 insertions(+), 42 deletions(-) diff --git a/web/core/components/workspace/sidebar/favorites/favorite-folder.tsx b/web/core/components/workspace/sidebar/favorites/favorite-folder.tsx index 6fc2c1b524..8806b2d081 100644 --- a/web/core/components/workspace/sidebar/favorites/favorite-folder.tsx +++ b/web/core/components/workspace/sidebar/favorites/favorite-folder.tsx @@ -27,6 +27,7 @@ import { CustomMenu, Tooltip, DropIndicator, FavoriteFolderIcon, DragHandle } fr import { cn } from "@/helpers/common.helper"; // hooks import { useAppTheme } from "@/hooks/store"; +import { useFavorite } from "@/hooks/store/use-favorite"; import { usePlatformOS } from "@/hooks/use-platform-os"; // constants import { FavoriteRoot } from "./favorite-items"; @@ -45,7 +46,7 @@ export const FavoriteFolder: React.FC = (props) => { const { favorite, handleRemoveFromFavorites, isLastChild, handleDrop } = props; // store hooks const { sidebarCollapsed: isSidebarCollapsed } = useAppTheme(); - + const { getGroupedFavorites } = useFavorite(); const { isMobile } = usePlatformOS(); const { workspaceSlug } = useParams(); // states @@ -58,6 +59,12 @@ export const FavoriteFolder: React.FC = (props) => { const actionSectionRef = useRef(null); const elementRef = useRef(null); + useEffect(() => { + if (favorite.children === undefined && workspaceSlug) { + getGroupedFavorites(workspaceSlug.toString(), favorite.id); + } + }, [favorite.id, favorite.children, workspaceSlug, getGroupedFavorites]); + useEffect(() => { const element = elementRef.current; @@ -123,7 +130,7 @@ export const FavoriteFolder: React.FC = (props) => { }) ); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isDragging, favorite.id]); + }, [isDragging, favorite.id, isLastChild, favorite.id]); useOutsideClickDetector(actionSectionRef, () => setIsMenuActive(false)); diff --git a/web/core/components/workspace/sidebar/favorites/favorite-items/root.tsx b/web/core/components/workspace/sidebar/favorites/favorite-items/root.tsx index 2ae16528ae..49931802e8 100644 --- a/web/core/components/workspace/sidebar/favorites/favorite-items/root.tsx +++ b/web/core/components/workspace/sidebar/favorites/favorite-items/root.tsx @@ -131,7 +131,7 @@ export const FavoriteRoot: FC = observer((props) => { }) ); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [elementRef?.current, isDragging]); + }, [elementRef?.current, isDragging, isLastChild, favorite.id]); useOutsideClickDetector(actionSectionRef, () => setIsMenuActive(false)); diff --git a/web/core/components/workspace/sidebar/favorites/favorites-menu.tsx b/web/core/components/workspace/sidebar/favorites/favorites-menu.tsx index 8aea968eca..6773e3a64b 100644 --- a/web/core/components/workspace/sidebar/favorites/favorites-menu.tsx +++ b/web/core/components/workspace/sidebar/favorites/favorites-menu.tsx @@ -87,33 +87,27 @@ export const SidebarFavoritesMenu = observer(() => { const sourceData = source.data as TargetData; if (!sourceData.id) return; - if (isFolder) { // handle move to a new parent folder if dropped on a folder if (parentId && parentId !== sourceData.parentId) { - handleMoveToFolder(sourceData.id, parentId); + handleMoveToFolder(sourceData.id, parentId); /**parent id */ } - //handle remove from folder if dropped outside of the folder - if (parentId && parentId !== sourceData.parentId && sourceData.isChild) { - handleRemoveFromFavoritesFolder(sourceData.id); - } - // handle reordering at root level if (droppedFavId) { if (instruction != "make-child") { - handleReorder(sourceData.id, droppedFavId, instruction); + handleReorder(sourceData.id, droppedFavId, instruction); /** sequence */ } } } else { //handling reordering for favorites if (droppedFavId) { - handleReorder(sourceData.id, droppedFavId, instruction); + handleReorder(sourceData.id, droppedFavId, instruction); /** sequence */ } + } - // handle removal from folder if dropped outside a folder - if (!parentId && sourceData.isChild) { - handleRemoveFromFavoritesFolder(sourceData.id); - } + /**remove if dropped outside and source is a child */ + if (!parentId && sourceData.isChild) { + handleRemoveFromFavoritesFolder(sourceData.id); /**parent null */ } }; diff --git a/web/core/store/favorite.store.ts b/web/core/store/favorite.store.ts index 3f4f636d34..a588f2af3b 100644 --- a/web/core/store/favorite.store.ts +++ b/web/core/store/favorite.store.ts @@ -174,23 +174,14 @@ export class FavoriteStore implements IFavoriteStore { * @returns Promise */ moveFavoriteToFolder = async (workspaceSlug: string, favoriteId: string, data: Partial) => { - const oldParent = this.favoriteMap[favoriteId].parent; try { + await this.favoriteService.updateFavorite(workspaceSlug, favoriteId, data); runInAction(() => { // add parent of the favorite set(this.favoriteMap, [favoriteId, "parent"], data.parent); }); - await this.favoriteService.updateFavorite(workspaceSlug, favoriteId, data); } catch (error) { - console.error("Failed to move favorite from favorite store"); - - // revert the changes - runInAction(() => { - if (!data.parent) return; - - // revert the parent - set(this.favoriteMap, [favoriteId, "parent"], oldParent); - }); + console.error("Failed to move favorite to folder", error); throw error; } }; @@ -201,7 +192,6 @@ export class FavoriteStore implements IFavoriteStore { destinationId: string, edge: string | undefined ) => { - const initialSequence = this.favoriteMap[favoriteId].sequence; try { let resultSequence = 10000; if (edge) { @@ -221,35 +211,27 @@ export class FavoriteStore implements IFavoriteStore { } } } + + await this.favoriteService.updateFavorite(workspaceSlug, favoriteId, { sequence: resultSequence }); + runInAction(() => { set(this.favoriteMap, [favoriteId, "sequence"], resultSequence); }); - - await this.favoriteService.updateFavorite(workspaceSlug, favoriteId, { sequence: resultSequence }); } catch (error) { console.error("Failed to move favorite folder"); - runInAction(() => { - set(this.favoriteMap, [favoriteId, "sequence"], initialSequence); - throw error; - }); + throw error; } }; removeFromFavoriteFolder = async (workspaceSlug: string, favoriteId: string) => { - const parent = this.favoriteMap[favoriteId].parent; try { + await this.favoriteService.updateFavorite(workspaceSlug, favoriteId, { parent: null }); runInAction(() => { //remove parent set(this.favoriteMap, [favoriteId, "parent"], null); }); - await this.favoriteService.updateFavorite(workspaceSlug, favoriteId, { parent: null }); } catch (error) { console.error("Failed to move favorite"); - runInAction(() => { - set(this.favoriteMap, [favoriteId, "parent"], parent); - - throw error; - }); throw error; } }; @@ -384,7 +366,7 @@ export class FavoriteStore implements IFavoriteStore { set(this.favoriteMap, [favorite.id], favorite); this.favoriteIds.push(favorite.id); this.favoriteIds = uniqBy(this.favoriteIds, (id) => id); - favorite.entity_identifier && set(this.entityMap, [favorite.entity_identifier], favorite); + if (favorite.entity_identifier) set(this.entityMap, [favorite.entity_identifier], favorite); }); }); From ca0d50b22909d0d5a0f414e2f551952378ee4823 Mon Sep 17 00:00:00 2001 From: Vamsi Krishna <46787868+mathalav55@users.noreply.github.com> Date: Wed, 11 Dec 2024 17:57:27 +0530 Subject: [PATCH 09/11] fix: no activity while moving inbox issues (#6185) --- .../issue-detail/issue-activity/activity/activity-list.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/web/core/components/issues/issue-detail/issue-activity/activity/activity-list.tsx b/web/core/components/issues/issue-detail/issue-activity/activity/activity-list.tsx index 148cf1f26c..d8d0b59dd3 100644 --- a/web/core/components/issues/issue-detail/issue-activity/activity/activity-list.tsx +++ b/web/core/components/issues/issue-detail/issue-activity/activity/activity-list.tsx @@ -82,6 +82,7 @@ export const IssueActivityItem: FC = observer((props) => { return ; case "archived_at": return ; + case "intake": case "inbox": return ; case "type": From a9bd2e243af33a5f8bc92c9e533e0328a2c15731 Mon Sep 17 00:00:00 2001 From: Prateek Shourya Date: Wed, 11 Dec 2024 18:02:58 +0530 Subject: [PATCH 10/11] refactor: enhance command palette modularity (#6139) * refactor: enhance command palette modularity * chore: minor updates to command palette store --- packages/types/src/command-palette.d.ts | 15 ++ packages/types/src/index.d.ts | 1 + .../command-palette/modals/index.ts | 3 + .../command-palette/modals/issue-level.tsx | 73 +++++++ .../command-palette/modals/project-level.tsx | 59 +++++ .../modals/workspace-level.tsx | 25 +++ web/ce/helpers/command-palette.ts | 95 ++++++++ web/ce/store/command-palette.store.ts | 12 + .../command-palette/command-palette.tsx | 206 +++--------------- .../shortcuts-modal/commands-list.tsx | 30 ++- web/core/hooks/store/use-command-palette.ts | 2 +- ...store.ts => base-command-palette.store.ts} | 28 ++- web/core/store/root.store.ts | 2 +- .../command-palette/modals/index.ts | 1 + web/ee/store/command-palette.store.ts | 1 + web/helpers/command-palette.ts | 1 + 16 files changed, 342 insertions(+), 212 deletions(-) create mode 100644 packages/types/src/command-palette.d.ts create mode 100644 web/ce/components/command-palette/modals/index.ts create mode 100644 web/ce/components/command-palette/modals/issue-level.tsx create mode 100644 web/ce/components/command-palette/modals/project-level.tsx create mode 100644 web/ce/components/command-palette/modals/workspace-level.tsx create mode 100644 web/ce/helpers/command-palette.ts create mode 100644 web/ce/store/command-palette.store.ts rename web/core/store/{command-palette.store.ts => base-command-palette.store.ts} (92%) create mode 100644 web/ee/components/command-palette/modals/index.ts create mode 100644 web/ee/store/command-palette.store.ts create mode 100644 web/helpers/command-palette.ts diff --git a/packages/types/src/command-palette.d.ts b/packages/types/src/command-palette.d.ts new file mode 100644 index 0000000000..6e072ab8fb --- /dev/null +++ b/packages/types/src/command-palette.d.ts @@ -0,0 +1,15 @@ +export type TCommandPaletteActionList = Record< + string, + { title: string; description: string; action: () => void } +>; + +export type TCommandPaletteShortcutList = { + key: string; + title: string; + shortcuts: TCommandPaletteShortcut[]; +}; + +export type TCommandPaletteShortcut = { + keys: string; // comma separated keys + description: string; +}; diff --git a/packages/types/src/index.d.ts b/packages/types/src/index.d.ts index 10e5197006..9c66c629a7 100644 --- a/packages/types/src/index.d.ts +++ b/packages/types/src/index.d.ts @@ -32,3 +32,4 @@ export * from "./workspace-notifications"; export * from "./favorite"; export * from "./file"; export * from "./workspace-draft-issues/base"; +export * from "./command-palette"; diff --git a/web/ce/components/command-palette/modals/index.ts b/web/ce/components/command-palette/modals/index.ts new file mode 100644 index 0000000000..a4fac4b91e --- /dev/null +++ b/web/ce/components/command-palette/modals/index.ts @@ -0,0 +1,3 @@ +export * from "./workspace-level"; +export * from "./project-level"; +export * from "./issue-level"; diff --git a/web/ce/components/command-palette/modals/issue-level.tsx b/web/ce/components/command-palette/modals/issue-level.tsx new file mode 100644 index 0000000000..84a7dddc64 --- /dev/null +++ b/web/ce/components/command-palette/modals/issue-level.tsx @@ -0,0 +1,73 @@ +import { observer } from "mobx-react"; +import { useParams, usePathname } from "next/navigation"; +import useSWR from "swr"; +// components +import { BulkDeleteIssuesModal } from "@/components/core"; +import { CreateUpdateIssueModal, DeleteIssueModal } from "@/components/issues"; +// constants +import { ISSUE_DETAILS } from "@/constants/fetch-keys"; +// hooks +import { useCommandPalette, useUser } from "@/hooks/store"; +import { useAppRouter } from "@/hooks/use-app-router"; +import { useIssuesStore } from "@/hooks/use-issue-layout-store"; +// services +import { IssueService } from "@/services/issue"; + +// services +const issueService = new IssueService(); + +export const IssueLevelModals = observer(() => { + // router + const pathname = usePathname(); + const { workspaceSlug, projectId, issueId, cycleId, moduleId } = useParams(); + const router = useAppRouter(); + // store hooks + const { data: currentUser } = useUser(); + const { + issues: { removeIssue }, + } = useIssuesStore(); + const { + isCreateIssueModalOpen, + toggleCreateIssueModal, + isDeleteIssueModalOpen, + toggleDeleteIssueModal, + isBulkDeleteIssueModalOpen, + toggleBulkDeleteIssueModal, + } = useCommandPalette(); + // derived values + const isDraftIssue = pathname?.includes("draft-issues") || false; + + const { data: issueDetails } = useSWR( + workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null, + workspaceSlug && projectId && issueId + ? () => issueService.retrieve(workspaceSlug as string, projectId as string, issueId as string) + : null + ); + + return ( + <> + toggleCreateIssueModal(false)} + data={cycleId ? { cycle_id: cycleId.toString() } : moduleId ? { module_ids: [moduleId.toString()] } : undefined} + isDraft={isDraftIssue} + /> + {workspaceSlug && projectId && issueId && issueDetails && ( + toggleDeleteIssueModal(false)} + isOpen={isDeleteIssueModalOpen} + data={issueDetails} + onSubmit={async () => { + await removeIssue(workspaceSlug.toString(), projectId.toString(), issueId.toString()); + router.push(`/${workspaceSlug}/projects/${projectId}/issues`); + }} + /> + )} + toggleBulkDeleteIssueModal(false)} + user={currentUser} + /> + + ); +}); diff --git a/web/ce/components/command-palette/modals/project-level.tsx b/web/ce/components/command-palette/modals/project-level.tsx new file mode 100644 index 0000000000..324af8d483 --- /dev/null +++ b/web/ce/components/command-palette/modals/project-level.tsx @@ -0,0 +1,59 @@ +import { observer } from "mobx-react"; +// components +import { CycleCreateUpdateModal } from "@/components/cycles"; +import { CreateUpdateModuleModal } from "@/components/modules"; +import { CreatePageModal } from "@/components/pages"; +import { CreateUpdateProjectViewModal } from "@/components/views"; +// hooks +import { useCommandPalette } from "@/hooks/store"; + +export type TProjectLevelModalsProps = { + workspaceSlug: string; + projectId: string; +}; + +export const ProjectLevelModals = observer((props: TProjectLevelModalsProps) => { + const { workspaceSlug, projectId } = props; + // store hooks + const { + isCreateCycleModalOpen, + toggleCreateCycleModal, + isCreateModuleModalOpen, + toggleCreateModuleModal, + isCreateViewModalOpen, + toggleCreateViewModal, + createPageModal, + toggleCreatePageModal, + } = useCommandPalette(); + + return ( + <> + toggleCreateCycleModal(false)} + workspaceSlug={workspaceSlug.toString()} + projectId={projectId.toString()} + /> + toggleCreateModuleModal(false)} + workspaceSlug={workspaceSlug.toString()} + projectId={projectId.toString()} + /> + toggleCreateViewModal(false)} + workspaceSlug={workspaceSlug.toString()} + projectId={projectId.toString()} + /> + toggleCreatePageModal({ isOpen: false })} + redirectionEnabled + /> + + ); +}); diff --git a/web/ce/components/command-palette/modals/workspace-level.tsx b/web/ce/components/command-palette/modals/workspace-level.tsx new file mode 100644 index 0000000000..64d22493e1 --- /dev/null +++ b/web/ce/components/command-palette/modals/workspace-level.tsx @@ -0,0 +1,25 @@ +import { observer } from "mobx-react"; +// components +import { CreateProjectModal } from "@/components/project"; +// hooks +import { useCommandPalette } from "@/hooks/store"; + +export type TWorkspaceLevelModalsProps = { + workspaceSlug: string; +}; + +export const WorkspaceLevelModals = observer((props: TWorkspaceLevelModalsProps) => { + const { workspaceSlug } = props; + // store hooks + const { isCreateProjectModalOpen, toggleCreateProjectModal } = useCommandPalette(); + + return ( + <> + toggleCreateProjectModal(false)} + workspaceSlug={workspaceSlug.toString()} + /> + + ); +}); diff --git a/web/ce/helpers/command-palette.ts b/web/ce/helpers/command-palette.ts new file mode 100644 index 0000000000..fccfcbaa4d --- /dev/null +++ b/web/ce/helpers/command-palette.ts @@ -0,0 +1,95 @@ +// types +import { TCommandPaletteActionList, TCommandPaletteShortcut, TCommandPaletteShortcutList } from "@plane/types"; +// store +import { store } from "@/lib/store-context"; + +export const getGlobalShortcutsList: () => TCommandPaletteActionList = () => { + const { toggleCreateIssueModal } = store.commandPalette; + + return { + c: { + title: "Create a new issue", + description: "Create a new issue in the current project", + action: () => toggleCreateIssueModal(true), + }, + }; +}; + +export const getWorkspaceShortcutsList: () => TCommandPaletteActionList = () => { + const { toggleCreateProjectModal } = store.commandPalette; + + return { + p: { + title: "Create a new project", + description: "Create a new project in the current workspace", + action: () => toggleCreateProjectModal(true), + }, + }; +}; + +export const getProjectShortcutsList: () => TCommandPaletteActionList = () => { + const { + toggleCreatePageModal, + toggleCreateModuleModal, + toggleCreateCycleModal, + toggleCreateViewModal, + toggleBulkDeleteIssueModal, + } = store.commandPalette; + + return { + d: { + title: "Create a new page", + description: "Create a new page in the current project", + action: () => toggleCreatePageModal({ isOpen: true }), + }, + m: { + title: "Create a new module", + description: "Create a new module in the current project", + action: () => toggleCreateModuleModal(true), + }, + q: { + title: "Create a new cycle", + description: "Create a new cycle in the current project", + action: () => toggleCreateCycleModal(true), + }, + v: { + title: "Create a new view", + description: "Create a new view in the current project", + action: () => toggleCreateViewModal(true), + }, + backspace: { + title: "Bulk delete issues", + description: "Bulk delete issues in the current project", + action: () => toggleBulkDeleteIssueModal(true), + }, + delete: { + title: "Bulk delete issues", + description: "Bulk delete issues in the current project", + action: () => toggleBulkDeleteIssueModal(true), + }, + }; +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export const handleAdditionalKeyDownEvents = (e: KeyboardEvent) => null; + +export const getNavigationShortcutsList = (): TCommandPaletteShortcut[] => [ + { keys: "Ctrl,K", description: "Open command menu" }, +]; + +export const getCommonShortcutsList = (platform: string): TCommandPaletteShortcut[] => [ + { keys: "P", description: "Create project" }, + { keys: "C", description: "Create issue" }, + { keys: "Q", description: "Create cycle" }, + { keys: "M", description: "Create module" }, + { keys: "V", description: "Create view" }, + { keys: "D", description: "Create page" }, + { keys: "Delete", description: "Bulk delete issues" }, + { keys: "Shift,/", description: "Open shortcuts guide" }, + { + keys: platform === "MacOS" ? "Ctrl,control,C" : "Ctrl,Alt,C", + description: "Copy issue URL from the issue details page", + }, +]; + +export const getAdditionalShortcutsList = (): TCommandPaletteShortcutList[] => []; diff --git a/web/ce/store/command-palette.store.ts b/web/ce/store/command-palette.store.ts new file mode 100644 index 0000000000..47c9280cd4 --- /dev/null +++ b/web/ce/store/command-palette.store.ts @@ -0,0 +1,12 @@ +import { makeObservable } from "mobx"; +// types / constants +import { BaseCommandPaletteStore, IBaseCommandPaletteStore } from "@/store/base-command-palette.store"; + +export type ICommandPaletteStore = IBaseCommandPaletteStore; + +export class CommandPaletteStore extends BaseCommandPaletteStore implements ICommandPaletteStore { + constructor() { + super(); + makeObservable(this, {}); + } +} diff --git a/web/core/components/command-palette/command-palette.tsx b/web/core/components/command-palette/command-palette.tsx index c38266c400..4e59234cde 100644 --- a/web/core/components/command-palette/command-palette.tsx +++ b/web/core/components/command-palette/command-palette.tsx @@ -2,87 +2,43 @@ import React, { useCallback, useEffect, FC, useMemo } from "react"; import { observer } from "mobx-react"; -import { useParams, usePathname } from "next/navigation"; -import useSWR from "swr"; +import { useParams } from "next/navigation"; // ui import { TOAST_TYPE, setToast } from "@plane/ui"; // components import { CommandModal, ShortcutsModal } from "@/components/command-palette"; -import { BulkDeleteIssuesModal } from "@/components/core"; -import { CycleCreateUpdateModal } from "@/components/cycles"; -import { CreateUpdateIssueModal, DeleteIssueModal } from "@/components/issues"; -import { CreateUpdateModuleModal } from "@/components/modules"; -import { CreatePageModal } from "@/components/pages"; -import { CreateProjectModal } from "@/components/project"; -import { CreateUpdateProjectViewModal } from "@/components/views"; -// constants -import { ISSUE_DETAILS } from "@/constants/fetch-keys"; // helpers import { copyTextToClipboard } from "@/helpers/string.helper"; // hooks import { useEventTracker, useUser, useAppTheme, useCommandPalette, useUserPermissions } from "@/hooks/store"; -import { useAppRouter } from "@/hooks/use-app-router"; -import { useIssuesStore } from "@/hooks/use-issue-layout-store"; import { usePlatformOS } from "@/hooks/use-platform-os"; +// plane web components +import { + IssueLevelModals, + ProjectLevelModals, + WorkspaceLevelModals, +} from "@/plane-web/components/command-palette/modals"; +// plane web constants import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions"; -// services -import { IssueService } from "@/services/issue"; - -// services -const issueService = new IssueService(); +// plane web helpers +import { + getGlobalShortcutsList, + getProjectShortcutsList, + getWorkspaceShortcutsList, + handleAdditionalKeyDownEvents, +} from "@/plane-web/helpers/command-palette"; export const CommandPalette: FC = observer(() => { - // router - const router = useAppRouter(); // router params - const { workspaceSlug, projectId, issueId, cycleId, moduleId } = useParams(); - // pathname - const pathname = usePathname(); + const { workspaceSlug, projectId, issueId } = useParams(); // store hooks const { toggleSidebar } = useAppTheme(); const { setTrackElement } = useEventTracker(); const { platform } = usePlatformOS(); - const { - data: currentUser, - // canPerformProjectMemberActions, - // canPerformWorkspaceMemberActions, - canPerformAnyCreateAction, - // canPerformProjectAdminActions, - } = useUser(); - const { - issues: { removeIssue }, - } = useIssuesStore(); - const { - toggleCommandPaletteModal, - isCreateIssueModalOpen, - toggleCreateIssueModal, - isCreateCycleModalOpen, - toggleCreateCycleModal, - createPageModal, - toggleCreatePageModal, - isCreateProjectModalOpen, - toggleCreateProjectModal, - isCreateModuleModalOpen, - toggleCreateModuleModal, - isCreateViewModalOpen, - toggleCreateViewModal, - isShortcutModalOpen, - toggleShortcutModal, - isBulkDeleteIssueModalOpen, - toggleBulkDeleteIssueModal, - isDeleteIssueModalOpen, - toggleDeleteIssueModal, - isAnyModalOpen, - } = useCommandPalette(); + const { data: currentUser, canPerformAnyCreateAction } = useUser(); + const { toggleCommandPaletteModal, isShortcutModalOpen, toggleShortcutModal, isAnyModalOpen } = useCommandPalette(); const { allowPermissions } = useUserPermissions(); - const { data: issueDetails } = useSWR( - workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null, - workspaceSlug && projectId && issueId - ? () => issueService.retrieve(workspaceSlug as string, projectId as string, issueId as string) - : null - ); - // derived values const canPerformWorkspaceMemberActions = allowPermissions( [EUserPermissions.ADMIN, EUserPermissions.MEMBER], @@ -170,62 +126,11 @@ export const CommandPalette: FC = observer(() => { project: Record void }>; } = useMemo( () => ({ - global: { - c: { - title: "Create a new issue", - description: "Create a new issue in the current project", - action: () => toggleCreateIssueModal(true), - }, - }, - workspace: { - p: { - title: "Create a new project", - description: "Create a new project in the current workspace", - action: () => toggleCreateProjectModal(true), - }, - }, - project: { - d: { - title: "Create a new page", - description: "Create a new page in the current project", - action: () => toggleCreatePageModal({ isOpen: true }), - }, - m: { - title: "Create a new module", - description: "Create a new module in the current project", - action: () => toggleCreateModuleModal(true), - }, - q: { - title: "Create a new cycle", - description: "Create a new cycle in the current project", - action: () => toggleCreateCycleModal(true), - }, - v: { - title: "Create a new view", - description: "Create a new view in the current project", - action: () => toggleCreateViewModal(true), - }, - backspace: { - title: "Bulk delete issues", - description: "Bulk delete issues in the current project", - action: () => toggleBulkDeleteIssueModal(true), - }, - delete: { - title: "Bulk delete issues", - description: "Bulk delete issues in the current project", - action: () => toggleBulkDeleteIssueModal(true), - }, - }, + global: getGlobalShortcutsList(), + workspace: getWorkspaceShortcutsList(), + project: getProjectShortcutsList(), }), - [ - toggleBulkDeleteIssueModal, - toggleCreateCycleModal, - toggleCreateIssueModal, - toggleCreateModuleModal, - toggleCreatePageModal, - toggleCreateProjectModal, - toggleCreateViewModal, - ] + [] ); const handleKeyDown = useCallback( @@ -296,6 +201,8 @@ export const CommandPalette: FC = observer(() => { shortcutsList.project[keyPressed].action(); } } + // Additional keydown events + handleAdditionalKeyDownEvents(e); }, [ copyIssueUrlToClipboard, @@ -320,75 +227,16 @@ export const CommandPalette: FC = observer(() => { return () => document.removeEventListener("keydown", handleKeyDown); }, [handleKeyDown]); - const isDraftIssue = pathname?.includes("draft-issues") || false; - if (!currentUser) return null; return ( <> toggleShortcutModal(false)} /> - {workspaceSlug && ( - toggleCreateProjectModal(false)} - workspaceSlug={workspaceSlug.toString()} - /> - )} + {workspaceSlug && } {workspaceSlug && projectId && ( - <> - toggleCreateCycleModal(false)} - workspaceSlug={workspaceSlug.toString()} - projectId={projectId.toString()} - /> - toggleCreateModuleModal(false)} - workspaceSlug={workspaceSlug.toString()} - projectId={projectId.toString()} - /> - toggleCreateViewModal(false)} - workspaceSlug={workspaceSlug.toString()} - projectId={projectId.toString()} - /> - toggleCreatePageModal({ isOpen: false })} - redirectionEnabled - /> - + )} - - toggleCreateIssueModal(false)} - data={cycleId ? { cycle_id: cycleId.toString() } : moduleId ? { module_ids: [moduleId.toString()] } : undefined} - isDraft={isDraftIssue} - /> - - {workspaceSlug && projectId && issueId && issueDetails && ( - toggleDeleteIssueModal(false)} - isOpen={isDeleteIssueModalOpen} - data={issueDetails} - onSubmit={async () => { - await removeIssue(workspaceSlug.toString(), projectId.toString(), issueId.toString()); - router.push(`/${workspaceSlug}/projects/${projectId}/issues`); - }} - /> - )} - - toggleBulkDeleteIssueModal(false)} - user={currentUser} - /> + ); diff --git a/web/core/components/command-palette/shortcuts-modal/commands-list.tsx b/web/core/components/command-palette/shortcuts-modal/commands-list.tsx index e72e92e0b9..570cb02fa7 100644 --- a/web/core/components/command-palette/shortcuts-modal/commands-list.tsx +++ b/web/core/components/command-palette/shortcuts-modal/commands-list.tsx @@ -3,6 +3,12 @@ import { Command } from "lucide-react"; import { substringMatch } from "@/helpers/string.helper"; // 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; @@ -16,26 +22,14 @@ export const ShortcutCommandsList: React.FC = (props) => { { key: "navigation", title: "Navigation", - shortcuts: [{ keys: "Ctrl,K", description: "Open command menu" }], + shortcuts: getNavigationShortcutsList(), }, { key: "common", title: "Common", - shortcuts: [ - { keys: "P", description: "Create project" }, - { keys: "C", description: "Create issue" }, - { keys: "Q", description: "Create cycle" }, - { keys: "M", description: "Create module" }, - { keys: "V", description: "Create view" }, - { keys: "D", description: "Create page" }, - { keys: "Delete", description: "Bulk delete issues" }, - { keys: "Shift,/", description: "Open shortcuts guide" }, - { - keys: platform === "MacOS" ? "Ctrl,control,C" : "Ctrl,Alt,C", - description: "Copy issue URL from the issue details page", - }, - ], + shortcuts: getCommonShortcutsList(platform), }, + ...getAdditionalShortcutsList(), ]; const filteredShortcuts = KEYBOARD_SHORTCUTS.map((category) => { @@ -69,7 +63,11 @@ export const ShortcutCommandsList: React.FC = (props) => {
{key === "Ctrl" ? (
- { platform === "MacOS" ? : 'Ctrl'} + {platform === "MacOS" ? ( + + ) : ( + "Ctrl" + )}
) : ( diff --git a/web/core/hooks/store/use-command-palette.ts b/web/core/hooks/store/use-command-palette.ts index 7b399387b1..cdf6a09ec6 100644 --- a/web/core/hooks/store/use-command-palette.ts +++ b/web/core/hooks/store/use-command-palette.ts @@ -2,7 +2,7 @@ import { useContext } from "react"; // mobx store import { StoreContext } from "@/lib/store-context"; // types -import { ICommandPaletteStore } from "@/store/command-palette.store"; +import { ICommandPaletteStore } from "@/plane-web/store/command-palette.store"; export const useCommandPalette = (): ICommandPaletteStore => { const context = useContext(StoreContext); diff --git a/web/core/store/command-palette.store.ts b/web/core/store/base-command-palette.store.ts similarity index 92% rename from web/core/store/command-palette.store.ts rename to web/core/store/base-command-palette.store.ts index 4cede34347..9b0ff52b57 100644 --- a/web/core/store/command-palette.store.ts +++ b/web/core/store/base-command-palette.store.ts @@ -9,9 +9,8 @@ export interface ModalData { viewId: string; } -export interface ICommandPaletteStore { +export interface IBaseCommandPaletteStore { // observables - isCommandPaletteOpen: boolean; isShortcutModalOpen: boolean; isCreateProjectModalOpen: boolean; @@ -22,6 +21,7 @@ export interface ICommandPaletteStore { isCreateIssueModalOpen: boolean; isDeleteIssueModalOpen: boolean; isBulkDeleteIssueModalOpen: boolean; + createIssueStoreType: TCreateModalStoreTypes; // computed isAnyModalOpen: boolean; // toggle actions @@ -35,11 +35,9 @@ export interface ICommandPaletteStore { toggleCreateModuleModal: (value?: boolean) => void; toggleDeleteIssueModal: (value?: boolean) => void; toggleBulkDeleteIssueModal: (value?: boolean) => void; - - createIssueStoreType: TCreateModalStoreTypes; } -export class CommandPaletteStore implements ICommandPaletteStore { +export abstract class BaseCommandPaletteStore implements IBaseCommandPaletteStore { // observables isCommandPaletteOpen: boolean = false; isShortcutModalOpen: boolean = false; @@ -51,7 +49,6 @@ export class CommandPaletteStore implements ICommandPaletteStore { isDeleteIssueModalOpen: boolean = false; isBulkDeleteIssueModalOpen: boolean = false; createPageModal: TCreatePageModal = DEFAULT_CREATE_PAGE_MODAL_DATA; - createIssueStoreType: TCreateModalStoreTypes = EIssuesStoreType.PROJECT; constructor() { @@ -67,6 +64,7 @@ export class CommandPaletteStore implements ICommandPaletteStore { isDeleteIssueModalOpen: observable.ref, isBulkDeleteIssueModalOpen: observable.ref, createPageModal: observable, + createIssueStoreType: observable, // computed isAnyModalOpen: computed, // projectPages: computed, @@ -85,20 +83,20 @@ export class CommandPaletteStore implements ICommandPaletteStore { } /** - * Checks whether any modal is open or not. + * Checks whether any modal is open or not in the base command palette. * @returns boolean */ get isAnyModalOpen() { return Boolean( this.isCreateIssueModalOpen || - this.isCreateCycleModalOpen || - this.isCreateProjectModalOpen || - this.isCreateModuleModalOpen || - this.isCreateViewModalOpen || - this.isShortcutModalOpen || - this.isBulkDeleteIssueModalOpen || - this.isDeleteIssueModalOpen || - this.createPageModal.isOpen + this.isCreateCycleModalOpen || + this.isCreateProjectModalOpen || + this.isCreateModuleModalOpen || + this.isCreateViewModalOpen || + this.isShortcutModalOpen || + this.isBulkDeleteIssueModalOpen || + this.isDeleteIssueModalOpen || + this.createPageModal.isOpen ); } diff --git a/web/core/store/root.store.ts b/web/core/store/root.store.ts index 33e4e7e92a..621f5f8082 100644 --- a/web/core/store/root.store.ts +++ b/web/core/store/root.store.ts @@ -1,9 +1,9 @@ import { enableStaticRendering } from "mobx-react"; // plane web store +import { CommandPaletteStore, ICommandPaletteStore } from "@/plane-web/store/command-palette.store"; import { RootStore } from "@/plane-web/store/root.store"; import { IStateStore, StateStore } from "@/plane-web/store/state.store"; // stores -import { CommandPaletteStore, ICommandPaletteStore } from "./command-palette.store"; import { CycleStore, ICycleStore } from "./cycle.store"; import { CycleFilterStore, ICycleFilterStore } from "./cycle_filter.store"; import { DashboardStore, IDashboardStore } from "./dashboard.store"; diff --git a/web/ee/components/command-palette/modals/index.ts b/web/ee/components/command-palette/modals/index.ts new file mode 100644 index 0000000000..fabf334b9c --- /dev/null +++ b/web/ee/components/command-palette/modals/index.ts @@ -0,0 +1 @@ +export * from "ce/components/command-palette/modals"; diff --git a/web/ee/store/command-palette.store.ts b/web/ee/store/command-palette.store.ts new file mode 100644 index 0000000000..b191f9f2df --- /dev/null +++ b/web/ee/store/command-palette.store.ts @@ -0,0 +1 @@ +export * from "ce/store/command-palette.store"; diff --git a/web/helpers/command-palette.ts b/web/helpers/command-palette.ts new file mode 100644 index 0000000000..7a45183215 --- /dev/null +++ b/web/helpers/command-palette.ts @@ -0,0 +1 @@ +export * from "ce/helpers/command-palette"; From 38e8a5c807bfd87f4d366f170427c077d7c980f0 Mon Sep 17 00:00:00 2001 From: Prateek Shourya Date: Wed, 11 Dec 2024 18:19:09 +0530 Subject: [PATCH 11/11] fix: command palette build (#6186) --- web/ee/helpers/command-palette.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 web/ee/helpers/command-palette.ts diff --git a/web/ee/helpers/command-palette.ts b/web/ee/helpers/command-palette.ts new file mode 100644 index 0000000000..7a45183215 --- /dev/null +++ b/web/ee/helpers/command-palette.ts @@ -0,0 +1 @@ +export * from "ce/helpers/command-palette";