From 9ee41ece983d2e4a1dc5d207f56e8333bd10ebca Mon Sep 17 00:00:00 2001 From: guru_sainath Date: Sat, 7 Dec 2024 17:55:50 +0530 Subject: [PATCH 01/34] fix: email check validation to handle case in-sensitive email (#6168) --- apiserver/plane/authentication/views/app/check.py | 3 +++ apiserver/plane/authentication/views/space/check.py | 1 + 2 files changed, 4 insertions(+) diff --git a/apiserver/plane/authentication/views/app/check.py b/apiserver/plane/authentication/views/app/check.py index c7e4b8a5e2f..0ad1db61f38 100644 --- a/apiserver/plane/authentication/views/app/check.py +++ b/apiserver/plane/authentication/views/app/check.py @@ -60,6 +60,9 @@ def post(self, request): ) return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) + # Lower the email + email = str(email).lower().strip() + # Validate email try: validate_email(email) diff --git a/apiserver/plane/authentication/views/space/check.py b/apiserver/plane/authentication/views/space/check.py index 9b4d8aa56d2..c8a4539b712 100644 --- a/apiserver/plane/authentication/views/space/check.py +++ b/apiserver/plane/authentication/views/space/check.py @@ -60,6 +60,7 @@ def post(self, request): ) return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST) + email = str(email).lower().strip() # Validate email try: validate_email(email) From 02308eeb15344cbe8711b40925b51ba11ca8e1c6 Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Mon, 9 Dec 2024 02:28:06 +0530 Subject: [PATCH 02/34] fix: django version upgrade --- apiserver/requirements/base.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apiserver/requirements/base.txt b/apiserver/requirements/base.txt index 854ab95f900..40e90aedfc4 100644 --- a/apiserver/requirements/base.txt +++ b/apiserver/requirements/base.txt @@ -1,7 +1,7 @@ # base requirements # django -Django==4.2.16 +Django==4.2.17 # rest framework djangorestframework==3.15.2 # postgres From cba41e0755ab3dd7f37766867233935aa1352623 Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Mon, 9 Dec 2024 02:35:48 +0530 Subject: [PATCH 03/34] fix: upgrading the express version --- live/package.json | 2 +- yarn.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/live/package.json b/live/package.json index 5fae765df4c..18124e39635 100644 --- a/live/package.json +++ b/live/package.json @@ -30,7 +30,7 @@ "compression": "^1.7.4", "cors": "^2.8.5", "dotenv": "^16.4.5", - "express": "^4.20.0", + "express": "^4.21.2", "express-ws": "^5.0.2", "helmet": "^7.1.0", "ioredis": "^5.4.1", diff --git a/yarn.lock b/yarn.lock index ddcaed14fd6..176641b6a16 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6805,10 +6805,10 @@ express-ws@^5.0.2: dependencies: ws "^7.4.6" -express@^4.20.0: - version "4.21.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.21.1.tgz#9dae5dda832f16b4eec941a4e44aa89ec481b281" - integrity sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ== +express@^4.21.2: + version "4.21.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" + integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== dependencies: accepts "~1.3.8" array-flatten "1.1.1" @@ -6829,7 +6829,7 @@ express@^4.20.0: methods "~1.1.2" on-finished "2.4.1" parseurl "~1.3.3" - path-to-regexp "0.1.10" + path-to-regexp "0.1.12" proxy-addr "~2.0.7" qs "6.13.0" range-parser "~1.2.1" @@ -9394,10 +9394,10 @@ path-scurry@^1.10.1, path-scurry@^1.11.1, path-scurry@^1.6.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" -path-to-regexp@0.1.10: - version "0.1.10" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b" - integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w== +path-to-regexp@0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" + integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== path-type@^4.0.0: version "4.0.0" From b21d190ce0c9ce6819f713b0c957855b13314f02 Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Mon, 9 Dec 2024 02:55:09 +0530 Subject: [PATCH 04/34] fix: added github pull request template --- .github/pull_request_template.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000000..fa445360f0a --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,20 @@ +### Description + + +### Type of Change + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] Feature (non-breaking change which adds functionality) +- [ ] Improvement (change that would cause existing functionality to not work as expected) +- [ ] Code refactoring +- [ ] Performance improvements +- [ ] Documentation update + +### Screenshots and Media (if applicable) + + +### Test Scenarios + + +### References + \ No newline at end of file From a85e592adab8cec169567518812e5c5941858f4a Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Mon, 9 Dec 2024 12:15:10 +0530 Subject: [PATCH 05/34] fix: creating a new sub-issue from workspace level (#6169) --- .../issue-detail-widgets/issue-detail-widget-modals.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/core/components/issues/issue-detail-widgets/issue-detail-widget-modals.tsx b/web/core/components/issues/issue-detail-widgets/issue-detail-widget-modals.tsx index fa9aa9d6518..ec99d6ebe97 100644 --- a/web/core/components/issues/issue-detail-widgets/issue-detail-widget-modals.tsx +++ b/web/core/components/issues/issue-detail-widgets/issue-detail-widget-modals.tsx @@ -121,7 +121,10 @@ export const IssueDetailWidgetModals: FC = observer((props) => { }; // helpers - const createUpdateModalData = { parent_id: issueCrudOperationState?.create?.parentIssueId }; + const createUpdateModalData: Partial = { + parent_id: issueCrudOperationState?.create?.parentIssueId, + project_id: projectId, + }; const existingIssuesModalSearchParams = { sub_issue: true, From 5c907db0e2ef9a40be8f02a4d13d5bea95faf0b3 Mon Sep 17 00:00:00 2001 From: Anmol Singh Bhatia <121005188+anmolsinghbhatia@users.noreply.github.com> Date: Mon, 9 Dec 2024 14:37:04 +0530 Subject: [PATCH 06/34] [WEB-2818] chore: project navigation items code refactor (#6170) * chore: project navigation items code refactor * fix: build error * chore: code refactor * chore: code refactor --- web/ce/components/sidebar/index.ts | 1 + .../sidebar/project-navigation-root.tsx | 15 ++ .../components/workspace/sidebar/index.ts | 1 + .../workspace/sidebar/project-navigation.tsx | 163 ++++++++++++++++++ .../workspace/sidebar/projects-list-item.tsx | 126 +------------- web/core/local-db/utils/load-workspace.ts | 6 +- 6 files changed, 190 insertions(+), 122 deletions(-) create mode 100644 web/ce/components/sidebar/project-navigation-root.tsx create mode 100644 web/core/components/workspace/sidebar/project-navigation.tsx diff --git a/web/ce/components/sidebar/index.ts b/web/ce/components/sidebar/index.ts index 5cda1afb5fe..129f4202072 100644 --- a/web/ce/components/sidebar/index.ts +++ b/web/ce/components/sidebar/index.ts @@ -1 +1,2 @@ export * from "./app-switcher"; +export * from "./project-navigation-root"; diff --git a/web/ce/components/sidebar/project-navigation-root.tsx b/web/ce/components/sidebar/project-navigation-root.tsx new file mode 100644 index 00000000000..25a0dd9d8ca --- /dev/null +++ b/web/ce/components/sidebar/project-navigation-root.tsx @@ -0,0 +1,15 @@ +"use client"; + +import { FC } from "react"; +// components +import { ProjectNavigation } from "@/components/workspace"; + +type TProjectItemsRootProps = { + workspaceSlug: string; + projectId: string; +}; + +export const ProjectNavigationRoot: FC = (props) => { + const { workspaceSlug, projectId } = props; + return ; +}; diff --git a/web/core/components/workspace/sidebar/index.ts b/web/core/components/workspace/sidebar/index.ts index c1759bf0ff2..564e3177157 100644 --- a/web/core/components/workspace/sidebar/index.ts +++ b/web/core/components/workspace/sidebar/index.ts @@ -3,6 +3,7 @@ export * from "./favorites"; export * from "./help-section"; export * from "./projects-list-item"; export * from "./projects-list"; +export * from "./project-navigation"; export * from "./quick-actions"; export * from "./user-menu"; export * from "./workspace-menu"; diff --git a/web/core/components/workspace/sidebar/project-navigation.tsx b/web/core/components/workspace/sidebar/project-navigation.tsx new file mode 100644 index 00000000000..3e75adbfdd0 --- /dev/null +++ b/web/core/components/workspace/sidebar/project-navigation.tsx @@ -0,0 +1,163 @@ +"use client"; + +import React, { FC, useCallback, useMemo } from "react"; +import { observer } from "mobx-react"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { FileText, Layers } from "lucide-react"; +// plane ui +import { Tooltip, DiceIcon, ContrastIcon, LayersIcon, Intake } from "@plane/ui"; +// components +import { SidebarNavItem } from "@/components/sidebar"; +// hooks +import { useAppTheme, useProject, useUserPermissions } from "@/hooks/store"; +import { usePlatformOS } from "@/hooks/use-platform-os"; +// plane-web constants +import { EUserPermissions } from "@/plane-web/constants"; +import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions"; + +export type TNavigationItem = { + name: string; + href: string; + icon: React.ElementType; + access: EUserPermissions[]; + shouldRender: boolean; + sortOrder: number; +}; + +type TProjectItemsProps = { + workspaceSlug: string; + projectId: string; + additionalNavigationItems?: (workspaceSlug: string, projectId: string) => TNavigationItem[]; +}; + +export const ProjectNavigation: FC = observer((props) => { + const { workspaceSlug, projectId, additionalNavigationItems } = props; + // store hooks + const { sidebarCollapsed: isSidebarCollapsed, toggleSidebar } = useAppTheme(); + const { getProjectById } = useProject(); + const { isMobile } = usePlatformOS(); + const { allowPermissions } = useUserPermissions(); + // pathname + const pathname = usePathname(); + // derived values + const project = getProjectById(projectId); + // handlers + const handleProjectClick = () => { + if (window.innerWidth < 768) { + toggleSidebar(); + } + }; + + if (!project) return null; + + const baseNavigation = useCallback( + (workspaceSlug: string, projectId: string): TNavigationItem[] => [ + { + name: "Issues", + href: `/${workspaceSlug}/projects/${projectId}/issues`, + icon: LayersIcon, + access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST], + shouldRender: true, + sortOrder: 1, + }, + { + name: "Cycles", + href: `/${workspaceSlug}/projects/${projectId}/cycles`, + icon: ContrastIcon, + access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER], + shouldRender: project.cycle_view, + sortOrder: 2, + }, + { + name: "Modules", + href: `/${workspaceSlug}/projects/${projectId}/modules`, + icon: DiceIcon, + access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER], + shouldRender: project.module_view, + sortOrder: 3, + }, + { + name: "Views", + href: `/${workspaceSlug}/projects/${projectId}/views`, + icon: Layers, + access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST], + shouldRender: project.issue_views_view, + sortOrder: 4, + }, + { + name: "Pages", + href: `/${workspaceSlug}/projects/${projectId}/pages`, + icon: FileText, + access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST], + shouldRender: project.page_view, + sortOrder: 5, + }, + { + name: "Intake", + href: `/${workspaceSlug}/projects/${projectId}/inbox`, + icon: Intake, + access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST], + shouldRender: project.inbox_view, + sortOrder: 6, + }, + ], + [project] + ); + + // memoized navigation items and adding additional navigation items + const navigationItemsMemo = useMemo(() => { + const navigationItems = (workspaceSlug: string, projectId: string): TNavigationItem[] => { + const navItems = baseNavigation(workspaceSlug, projectId); + + if (additionalNavigationItems) { + navItems.push(...additionalNavigationItems(workspaceSlug, projectId)); + } + + return navItems; + }; + + // sort navigation items by sortOrder + const sortedNavigationItems = navigationItems(workspaceSlug, projectId).sort( + (a, b) => (a.sortOrder || 0) - (b.sortOrder || 0) + ); + + return sortedNavigationItems; + }, [workspaceSlug, projectId, baseNavigation, additionalNavigationItems]); + + return ( + <> + {navigationItemsMemo.map((item) => { + if (!item.shouldRender) return; + + const hasAccess = allowPermissions(item.access, EUserPermissionsLevel.PROJECT, workspaceSlug, project.id); + if (!hasAccess) return null; + + return ( + + + +
+ + {!isSidebarCollapsed && {item.name}} +
+
+ +
+ ); + })} + + ); +}); diff --git a/web/core/components/workspace/sidebar/projects-list-item.tsx b/web/core/components/workspace/sidebar/projects-list-item.tsx index 209832c6a3f..9e1e4d769ad 100644 --- a/web/core/components/workspace/sidebar/projects-list-item.tsx +++ b/web/core/components/workspace/sidebar/projects-list-item.tsx @@ -8,46 +8,24 @@ import { setCustomNativeDragPreview } from "@atlaskit/pragmatic-drag-and-drop/el import { attachInstruction, extractInstruction } from "@atlaskit/pragmatic-drag-and-drop-hitbox/tree-item"; import { observer } from "mobx-react"; import Link from "next/link"; -import { useParams, usePathname, useRouter } from "next/navigation"; +import { useParams, useRouter } from "next/navigation"; import { createRoot } from "react-dom/client"; -import { - PenSquare, - LinkIcon, - Star, - FileText, - Settings, - Share2, - LogOut, - MoreHorizontal, - ChevronRight, - Layers, -} from "lucide-react"; +import { LinkIcon, Star, Settings, Share2, LogOut, MoreHorizontal, ChevronRight } from "lucide-react"; import { Disclosure, Transition } from "@headlessui/react"; // plane helpers import { useOutsideClickDetector } from "@plane/hooks"; // ui -import { - CustomMenu, - Tooltip, - ArchiveIcon, - DiceIcon, - ContrastIcon, - LayersIcon, - setPromiseToast, - DropIndicator, - DragHandle, - Intake, - ControlLink, -} from "@plane/ui"; +import { CustomMenu, Tooltip, ArchiveIcon, setPromiseToast, DropIndicator, DragHandle, ControlLink } from "@plane/ui"; // components import { Logo } from "@/components/common"; import { LeaveProjectModal, PublishProjectModal } from "@/components/project"; -import { SidebarNavItem } from "@/components/sidebar"; // helpers import { cn } from "@/helpers/common.helper"; // hooks import { useAppTheme, useEventTracker, useProject, useUserPermissions } from "@/hooks/store"; import { usePlatformOS } from "@/hooks/use-platform-os"; +// plane-web components +import { ProjectNavigationRoot } from "@/plane-web/components/sidebar"; // constants import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions"; import { HIGHLIGHT_CLASS, highlightIssueOnDrop } from "../../issues/issue-layouts/utils"; @@ -66,50 +44,11 @@ type Props = { isLastChild: boolean; }; -const navigation = (workspaceSlug: string, projectId: string) => [ - { - name: "Issues", - href: `/${workspaceSlug}/projects/${projectId}/issues`, - Icon: LayersIcon, - access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST], - }, - { - name: "Cycles", - href: `/${workspaceSlug}/projects/${projectId}/cycles`, - Icon: ContrastIcon, - access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER], - }, - { - name: "Modules", - href: `/${workspaceSlug}/projects/${projectId}/modules`, - Icon: DiceIcon, - access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER], - }, - { - name: "Views", - href: `/${workspaceSlug}/projects/${projectId}/views`, - Icon: Layers, - access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST], - }, - { - name: "Pages", - href: `/${workspaceSlug}/projects/${projectId}/pages`, - Icon: FileText, - access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST], - }, - { - name: "Intake", - href: `/${workspaceSlug}/projects/${projectId}/inbox`, - Icon: Intake, - access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST], - }, -]; - export const SidebarProjectsListItem: React.FC = observer((props) => { const { projectId, handleCopyText, disableDrag, disableDrop, isLastChild, handleOnProjectDrop, projectListType } = props; // store hooks - const { sidebarCollapsed: isSidebarCollapsed, toggleSidebar } = useAppTheme(); + const { sidebarCollapsed: isSidebarCollapsed } = useAppTheme(); const { setTrackElement } = useEventTracker(); const { addProjectToFavorites, removeProjectFromFavorites, getProjectById } = useProject(); const { isMobile } = usePlatformOS(); @@ -128,8 +67,6 @@ export const SidebarProjectsListItem: React.FC = observer((props) => { // router const router = useRouter(); const { workspaceSlug, projectId: URLProjectId } = useParams(); - // pathname - const pathname = usePathname(); // derived values const project = getProjectById(projectId); // auth @@ -185,12 +122,6 @@ export const SidebarProjectsListItem: React.FC = observer((props) => { setLeaveProjectModal(true); }; - const handleProjectClick = () => { - if (window.innerWidth < 768) { - toggleSidebar(); - } - }; - useEffect(() => { const element = projectRef.current; const dragHandleElement = dragHandleRef.current; @@ -503,50 +434,7 @@ export const SidebarProjectsListItem: React.FC = observer((props) => { > {isProjectListOpen && ( - {navigation(workspaceSlug?.toString(), project?.id).map((item) => { - if ( - (item.name === "Cycles" && !project.cycle_view) || - (item.name === "Modules" && !project.module_view) || - (item.name === "Views" && !project.issue_views_view) || - (item.name === "Pages" && !project.page_view) || - (item.name === "Intake" && !project.inbox_view) - ) - return; - return ( - <> - {allowPermissions( - item.access, - EUserPermissionsLevel.PROJECT, - workspaceSlug.toString(), - project.id - ) && ( - - - -
- - {!isSidebarCollapsed && {item.name}} -
-
- -
- )} - - ); - })} +
)} diff --git a/web/core/local-db/utils/load-workspace.ts b/web/core/local-db/utils/load-workspace.ts index b0e2f60485f..42a79f9dd64 100644 --- a/web/core/local-db/utils/load-workspace.ts +++ b/web/core/local-db/utils/load-workspace.ts @@ -150,7 +150,7 @@ export const syncIssuesWithDeletedModules = async (deletedModuleIds: string[]) = return; } - const issues = await persistence.getIssues("", "", { modules: deletedModuleIds.join(","), cursor: "10000:0:0" }, {}); + const issues = await persistence.getIssues("", "", { module: deletedModuleIds.join(","), cursor: "10000:0:0" }, {}); if (issues?.results && Array.isArray(issues.results)) { const promises = issues.results.map(async (issue: TIssue) => { const updatedIssue = { @@ -177,7 +177,7 @@ export const syncIssuesWithDeletedCycles = async (deletedCycleIds: string[]) => return; } - const issues = await persistence.getIssues("", "", { cycles: deletedCycleIds.join(","), cursor: "10000:0:0" }, {}); + const issues = await persistence.getIssues("", "", { cycle: deletedCycleIds.join(","), cursor: "10000:0:0" }, {}); if (issues?.results && Array.isArray(issues.results)) { const promises = issues.results.map(async (issue: TIssue) => { const updatedIssue = { @@ -204,7 +204,7 @@ export const syncIssuesWithDeletedStates = async (deletedStateIds: string[]) => return; } - const issues = await persistence.getIssues("", "", { states: deletedStateIds.join(","), cursor: "10000:0:0" }, {}); + const issues = await persistence.getIssues("", "", { state: deletedStateIds.join(","), cursor: "10000:0:0" }, {}); if (issues?.results && Array.isArray(issues.results)) { const promises = issues.results.map(async (issue: TIssue) => { const updatedIssue = { From 547c13808400ab38fbc4d65e0c71ee97801d9b32 Mon Sep 17 00:00:00 2001 From: sriram veeraghanta Date: Mon, 9 Dec 2024 15:56:20 +0530 Subject: [PATCH 07/34] fix: ui package module resolution --- packages/ui/package.json | 9 +- yarn.lock | 894 ++++++++++++++++++++------------------- 2 files changed, 465 insertions(+), 438 deletions(-) diff --git a/packages/ui/package.json b/packages/ui/package.json index e4ebc0fe405..600f0d72658 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -31,9 +31,9 @@ "@blueprintjs/core": "^4.16.3", "@blueprintjs/popover2": "^1.13.3", "@headlessui/react": "^1.7.3", - "@popperjs/core": "^2.11.8", "@plane/hooks": "*", "@plane/utils": "*", + "@popperjs/core": "^2.11.8", "clsx": "^2.0.0", "emoji-picker-react": "^4.5.16", "lodash": "^4.17.21", @@ -46,6 +46,8 @@ }, "devDependencies": { "@chromatic-com/storybook": "^1.4.0", + "@plane/eslint-config": "*", + "@plane/typescript-config": "*", "@storybook/addon-essentials": "^8.1.1", "@storybook/addon-interactions": "^8.1.1", "@storybook/addon-links": "^8.1.1", @@ -63,14 +65,15 @@ "@types/react-dom": "^18.2.18", "autoprefixer": "^10.4.19", "classnames": "^2.3.2", - "@plane/eslint-config": "*", "postcss-cli": "^11.0.0", "postcss-nested": "^6.0.1", "storybook": "^8.1.1", "tailwind-config-custom": "*", "tailwindcss": "^3.4.3", - "@plane/typescript-config": "*", "tsup": "^7.2.0", "typescript": "5.3.3" + }, + "resolutions": { + "@types/react": "^18.0.0" } } diff --git a/yarn.lock b/yarn.lock index 176641b6a16..95377ec6cb1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -46,9 +46,9 @@ raf-schd "^4.0.3" "@babel/cli@^7.25.6": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.25.9.tgz#51036166fd0e9cfb26eee1b9ddc264a0d6d5f843" - integrity sha512-I+02IfrTiSanpxJBlZQYb18qCxB6c2Ih371cVpfgIrPQrjAYkf45XxomTJOG8JBWX5GY35/+TmhCMdJ4ZPkL8Q== + version "7.26.4" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.26.4.tgz#4101ff8ee5de8447a6c395397a97921056411d20" + integrity sha512-+mORf3ezU3p3qr+82WvJSnQNE1GAYeoCfEv4fik6B5/2cvKZ75AX8oawWQdXtM9MmndooQj15Jr9kelRFWsuRw== dependencies: "@jridgewell/trace-mapping" "^0.3.25" commander "^6.2.0" @@ -850,9 +850,9 @@ "@babel/types" "^7.25.9" "@babel/traverse@^7.18.9", "@babel/traverse@^7.25.9": - version "7.26.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.3.tgz#1ebfc75bd748d8f96b3cc63af5e82ebd4c37ba35" - integrity sha512-yTmc8J+Sj8yLzwr4PD5Xb/WF3bOYu2C2OoSZPzbuqRm4n98XirsbzaX+GloeO376UnSYIYJ4NCanwV5/ugZkwA== + version "7.26.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.4.tgz#ac3a2a84b908dde6d463c3bfa2c5fdc1653574bd" + integrity sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w== dependencies: "@babel/code-frame" "^7.26.2" "@babel/generator" "^7.26.3" @@ -985,10 +985,10 @@ source-map "^0.5.7" stylis "4.2.0" -"@emotion/cache@^11.11.0", "@emotion/cache@^11.13.5": - version "11.13.5" - resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.13.5.tgz#e78dad0489e1ed7572507ba8ed9d2130529e4266" - integrity sha512-Z3xbtJ+UcK76eWkagZ1onvn/wAVb1GOMuR15s30Fm2wrMgC7jzpnO2JZXr4eujTTqoQFUrZIw/rT0c6Zzjca1g== +"@emotion/cache@^11.11.0", "@emotion/cache@^11.14.0": + version "11.14.0" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.14.0.tgz#ee44b26986eeb93c8be82bb92f1f7a9b21b2ed76" + integrity sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA== dependencies: "@emotion/memoize" "^0.9.0" "@emotion/sheet" "^1.4.0" @@ -1014,15 +1014,15 @@ integrity sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ== "@emotion/react@^11.11.1": - version "11.13.5" - resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.13.5.tgz#fc818ff5b13424f86501ba4d0740f343ae20b8d9" - integrity sha512-6zeCUxUH+EPF1s+YF/2hPVODeV/7V07YU5x+2tfuRL8MdW6rv5vb2+CBEGTGwBdux0OIERcOS+RzxeK80k2DsQ== + version "11.14.0" + resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.14.0.tgz#cfaae35ebc67dd9ef4ea2e9acc6cd29e157dd05d" + integrity sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA== dependencies: "@babel/runtime" "^7.18.3" "@emotion/babel-plugin" "^11.13.5" - "@emotion/cache" "^11.13.5" + "@emotion/cache" "^11.14.0" "@emotion/serialize" "^1.3.3" - "@emotion/use-insertion-effect-with-fallbacks" "^1.1.0" + "@emotion/use-insertion-effect-with-fallbacks" "^1.2.0" "@emotion/utils" "^1.4.2" "@emotion/weak-memoize" "^0.4.0" hoist-non-react-statics "^3.3.1" @@ -1044,15 +1044,15 @@ integrity sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg== "@emotion/styled@^11.11.0": - version "11.13.5" - resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.13.5.tgz#0fa6602227414c5e42cf267506e3c35bae655df9" - integrity sha512-gnOQ+nGLPvDXgIx119JqGalys64lhMdnNQA9TMxhDA4K0Hq5+++OE20Zs5GxiCV9r814xQ2K5WmtofSpHVW6BQ== + version "11.14.0" + resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.14.0.tgz#f47ca7219b1a295186d7661583376fcea95f0ff3" + integrity sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA== dependencies: "@babel/runtime" "^7.18.3" "@emotion/babel-plugin" "^11.13.5" "@emotion/is-prop-valid" "^1.3.0" "@emotion/serialize" "^1.3.3" - "@emotion/use-insertion-effect-with-fallbacks" "^1.1.0" + "@emotion/use-insertion-effect-with-fallbacks" "^1.2.0" "@emotion/utils" "^1.4.2" "@emotion/unitless@^0.10.0": @@ -1060,10 +1060,10 @@ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.10.0.tgz#2af2f7c7e5150f497bdabd848ce7b218a27cf745" integrity sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg== -"@emotion/use-insertion-effect-with-fallbacks@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz#1a818a0b2c481efba0cf34e5ab1e0cb2dcb9dfaf" - integrity sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw== +"@emotion/use-insertion-effect-with-fallbacks@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz#8a8cb77b590e09affb960f4ff1e9a89e532738bf" + integrity sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg== "@emotion/utils@^1.4.2": version "1.4.2" @@ -2588,95 +2588,100 @@ estree-walker "^2.0.2" picomatch "^4.0.2" -"@rollup/rollup-android-arm-eabi@4.28.0": - version "4.28.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.0.tgz#462e7ecdd60968bc9eb95a20d185e74f8243ec1b" - integrity sha512-wLJuPLT6grGZsy34g4N1yRfYeouklTgPhH1gWXCYspenKYD0s3cR99ZevOGw5BexMNywkbV3UkjADisozBmpPQ== - -"@rollup/rollup-android-arm64@4.28.0": - version "4.28.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.28.0.tgz#78a2b8a8a55f71a295eb860a654ae90a2b168f40" - integrity sha512-eiNkznlo0dLmVG/6wf+Ifi/v78G4d4QxRhuUl+s8EWZpDewgk7PX3ZyECUXU0Zq/Ca+8nU8cQpNC4Xgn2gFNDA== - -"@rollup/rollup-darwin-arm64@4.28.0": - version "4.28.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.28.0.tgz#5b783af714f434f1e66e3cdfa3817e0b99216d84" - integrity sha512-lmKx9yHsppblnLQZOGxdO66gT77bvdBtr/0P+TPOseowE7D9AJoBw8ZDULRasXRWf1Z86/gcOdpBrV6VDUY36Q== - -"@rollup/rollup-darwin-x64@4.28.0": - version "4.28.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.28.0.tgz#f72484e842521a5261978034e18e20f778a2850d" - integrity sha512-8hxgfReVs7k9Js1uAIhS6zq3I+wKQETInnWQtgzt8JfGx51R1N6DRVy3F4o0lQwumbErRz52YqwjfvuwRxGv1w== - -"@rollup/rollup-freebsd-arm64@4.28.0": - version "4.28.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.28.0.tgz#3c919dff72b2fe344811a609c674a8347b033f62" - integrity sha512-lA1zZB3bFx5oxu9fYud4+g1mt+lYXCoch0M0V/xhqLoGatbzVse0wlSQ1UYOWKpuSu3gyN4qEc0Dxf/DII1bhQ== - -"@rollup/rollup-freebsd-x64@4.28.0": - version "4.28.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.28.0.tgz#b62a3a8365b363b3fdfa6da11a9188b6ab4dca7c" - integrity sha512-aI2plavbUDjCQB/sRbeUZWX9qp12GfYkYSJOrdYTL/C5D53bsE2/nBPuoiJKoWp5SN78v2Vr8ZPnB+/VbQ2pFA== - -"@rollup/rollup-linux-arm-gnueabihf@4.28.0": - version "4.28.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.28.0.tgz#0d02cc55bd229bd8ca5c54f65f916ba5e0591c94" - integrity sha512-WXveUPKtfqtaNvpf0iOb0M6xC64GzUX/OowbqfiCSXTdi/jLlOmH0Ba94/OkiY2yTGTwteo4/dsHRfh5bDCZ+w== - -"@rollup/rollup-linux-arm-musleabihf@4.28.0": - version "4.28.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.28.0.tgz#c51d379263201e88a60e92bd8e90878f0c044425" - integrity sha512-yLc3O2NtOQR67lI79zsSc7lk31xjwcaocvdD1twL64PK1yNaIqCeWI9L5B4MFPAVGEVjH5k1oWSGuYX1Wutxpg== - -"@rollup/rollup-linux-arm64-gnu@4.28.0": - version "4.28.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.28.0.tgz#93ce2addc337b5cfa52b84f8e730d2e36eb4339b" - integrity sha512-+P9G9hjEpHucHRXqesY+3X9hD2wh0iNnJXX/QhS/J5vTdG6VhNYMxJ2rJkQOxRUd17u5mbMLHM7yWGZdAASfcg== - -"@rollup/rollup-linux-arm64-musl@4.28.0": - version "4.28.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.28.0.tgz#730af6ddc091a5ba5baac28a3510691725dc808b" - integrity sha512-1xsm2rCKSTpKzi5/ypT5wfc+4bOGa/9yI/eaOLW0oMs7qpC542APWhl4A37AENGZ6St6GBMWhCCMM6tXgTIplw== - -"@rollup/rollup-linux-powerpc64le-gnu@4.28.0": - version "4.28.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.28.0.tgz#b5565aac20b4de60ca1e557f525e76478b5436af" - integrity sha512-zgWxMq8neVQeXL+ouSf6S7DoNeo6EPgi1eeqHXVKQxqPy1B2NvTbaOUWPn/7CfMKL7xvhV0/+fq/Z/J69g1WAQ== - -"@rollup/rollup-linux-riscv64-gnu@4.28.0": - version "4.28.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.28.0.tgz#d488290bf9338bad4ae9409c4aa8a1728835a20b" - integrity sha512-VEdVYacLniRxbRJLNtzwGt5vwS0ycYshofI7cWAfj7Vg5asqj+pt+Q6x4n+AONSZW/kVm+5nklde0qs2EUwU2g== - -"@rollup/rollup-linux-s390x-gnu@4.28.0": - version "4.28.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.28.0.tgz#eb2e3f3a06acf448115045c11a5a96868c95a556" - integrity sha512-LQlP5t2hcDJh8HV8RELD9/xlYtEzJkm/aWGsauvdO2ulfl3QYRjqrKW+mGAIWP5kdNCBheqqqYIGElSRCaXfpw== - -"@rollup/rollup-linux-x64-gnu@4.28.0": - version "4.28.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.28.0.tgz#065952ef2aea7e837dc7e02aa500feeaff4fc507" - integrity sha512-Nl4KIzteVEKE9BdAvYoTkW19pa7LR/RBrT6F1dJCV/3pbjwDcaOq+edkP0LXuJ9kflW/xOK414X78r+K84+msw== - -"@rollup/rollup-linux-x64-musl@4.28.0": - version "4.28.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.28.0.tgz#3435d484d05f5c4d1ffd54541b4facce2887103a" - integrity sha512-eKpJr4vBDOi4goT75MvW+0dXcNUqisK4jvibY9vDdlgLx+yekxSm55StsHbxUsRxSTt3JEQvlr3cGDkzcSP8bw== - -"@rollup/rollup-win32-arm64-msvc@4.28.0": - version "4.28.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.28.0.tgz#69682a2a10d9fedc334f87583cfca83c39c08077" - integrity sha512-Vi+WR62xWGsE/Oj+mD0FNAPY2MEox3cfyG0zLpotZdehPFXwz6lypkGs5y38Jd/NVSbOD02aVad6q6QYF7i8Bg== - -"@rollup/rollup-win32-ia32-msvc@4.28.0": - version "4.28.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.28.0.tgz#b64470f9ac79abb386829c56750b9a4711be3332" - integrity sha512-kN/Vpip8emMLn/eOza+4JwqDZBL6MPNpkdaEsgUtW1NYN3DZvZqSQrbKzJcTL6hd8YNmFTn7XGWMwccOcJBL0A== - -"@rollup/rollup-win32-x64-msvc@4.28.0": - version "4.28.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.28.0.tgz#cb313feef9ac6e3737067fdf34f42804ac65a6f2" - integrity sha512-Bvno2/aZT6usSa7lRDL2+hMjVAGjuqaymF1ApZm31JXzniR/hvr14jpU+/z4X6Gt5BPlzosscyJZGUvguXIqeQ== +"@rollup/rollup-android-arm-eabi@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.28.1.tgz#7f4c4d8cd5ccab6e95d6750dbe00321c1f30791e" + integrity sha512-2aZp8AES04KI2dy3Ss6/MDjXbwBzj+i0GqKtWXgw2/Ma6E4jJvujryO6gJAghIRVz7Vwr9Gtl/8na3nDUKpraQ== + +"@rollup/rollup-android-arm64@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.28.1.tgz#17ea71695fb1518c2c324badbe431a0bd1879f2d" + integrity sha512-EbkK285O+1YMrg57xVA+Dp0tDBRB93/BZKph9XhMjezf6F4TpYjaUSuPt5J0fZXlSag0LmZAsTmdGGqPp4pQFA== + +"@rollup/rollup-darwin-arm64@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.28.1.tgz#dac0f0d0cfa73e7d5225ae6d303c13c8979e7999" + integrity sha512-prduvrMKU6NzMq6nxzQw445zXgaDBbMQvmKSJaxpaZ5R1QDM8w+eGxo6Y/jhT/cLoCvnZI42oEqf9KQNYz1fqQ== + +"@rollup/rollup-darwin-x64@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.28.1.tgz#8f63baa1d31784904a380d2e293fa1ddf53dd4a2" + integrity sha512-WsvbOunsUk0wccO/TV4o7IKgloJ942hVFK1CLatwv6TJspcCZb9umQkPdvB7FihmdxgaKR5JyxDjWpCOp4uZlQ== + +"@rollup/rollup-freebsd-arm64@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.28.1.tgz#30ed247e0df6e8858cdc6ae4090e12dbeb8ce946" + integrity sha512-HTDPdY1caUcU4qK23FeeGxCdJF64cKkqajU0iBnTVxS8F7H/7BewvYoG+va1KPSL63kQ1PGNyiwKOfReavzvNA== + +"@rollup/rollup-freebsd-x64@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.28.1.tgz#57846f382fddbb508412ae07855b8a04c8f56282" + integrity sha512-m/uYasxkUevcFTeRSM9TeLyPe2QDuqtjkeoTpP9SW0XxUWfcYrGDMkO/m2tTw+4NMAF9P2fU3Mw4ahNvo7QmsQ== + +"@rollup/rollup-linux-arm-gnueabihf@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.28.1.tgz#378ca666c9dae5e6f94d1d351e7497c176e9b6df" + integrity sha512-QAg11ZIt6mcmzpNE6JZBpKfJaKkqTm1A9+y9O+frdZJEuhQxiugM05gnCWiANHj4RmbgeVJpTdmKRmH/a+0QbA== + +"@rollup/rollup-linux-arm-musleabihf@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.28.1.tgz#a692eff3bab330d5c33a5d5813a090c15374cddb" + integrity sha512-dRP9PEBfolq1dmMcFqbEPSd9VlRuVWEGSmbxVEfiq2cs2jlZAl0YNxFzAQS2OrQmsLBLAATDMb3Z6MFv5vOcXg== + +"@rollup/rollup-linux-arm64-gnu@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.28.1.tgz#6b1719b76088da5ac1ae1feccf48c5926b9e3db9" + integrity sha512-uGr8khxO+CKT4XU8ZUH1TTEUtlktK6Kgtv0+6bIFSeiSlnGJHG1tSFSjm41uQ9sAO/5ULx9mWOz70jYLyv1QkA== + +"@rollup/rollup-linux-arm64-musl@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.28.1.tgz#865baf5b6f5ff67acb32e5a359508828e8dc5788" + integrity sha512-QF54q8MYGAqMLrX2t7tNpi01nvq5RI59UBNx+3+37zoKX5KViPo/gk2QLhsuqok05sSCRluj0D00LzCwBikb0A== + +"@rollup/rollup-linux-loongarch64-gnu@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.28.1.tgz#23c6609ba0f7fa7a7f2038b6b6a08555a5055a87" + integrity sha512-vPul4uodvWvLhRco2w0GcyZcdyBfpfDRgNKU+p35AWEbJ/HPs1tOUrkSueVbBS0RQHAf/A+nNtDpvw95PeVKOA== + +"@rollup/rollup-linux-powerpc64le-gnu@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.28.1.tgz#652ef0d9334a9f25b9daf85731242801cb0fc41c" + integrity sha512-pTnTdBuC2+pt1Rmm2SV7JWRqzhYpEILML4PKODqLz+C7Ou2apEV52h19CR7es+u04KlqplggmN9sqZlekg3R1A== + +"@rollup/rollup-linux-riscv64-gnu@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.28.1.tgz#1eb6651839ee6ebca64d6cc64febbd299e95e6bd" + integrity sha512-vWXy1Nfg7TPBSuAncfInmAI/WZDd5vOklyLJDdIRKABcZWojNDY0NJwruY2AcnCLnRJKSaBgf/GiJfauu8cQZA== + +"@rollup/rollup-linux-s390x-gnu@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.28.1.tgz#015c52293afb3ff2a293cf0936b1d43975c1e9cd" + integrity sha512-/yqC2Y53oZjb0yz8PVuGOQQNOTwxcizudunl/tFs1aLvObTclTwZ0JhXF2XcPT/zuaymemCDSuuUPXJJyqeDOg== + +"@rollup/rollup-linux-x64-gnu@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.28.1.tgz#b83001b5abed2bcb5e2dbeec6a7e69b194235c1e" + integrity sha512-fzgeABz7rrAlKYB0y2kSEiURrI0691CSL0+KXwKwhxvj92VULEDQLpBYLHpF49MSiPG4sq5CK3qHMnb9tlCjBw== + +"@rollup/rollup-linux-x64-musl@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.28.1.tgz#6cc7c84cd4563737f8593e66f33b57d8e228805b" + integrity sha512-xQTDVzSGiMlSshpJCtudbWyRfLaNiVPXt1WgdWTwWz9n0U12cI2ZVtWe/Jgwyv/6wjL7b66uu61Vg0POWVfz4g== + +"@rollup/rollup-win32-arm64-msvc@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.28.1.tgz#631ffeee094d71279fcd1fe8072bdcf25311bc11" + integrity sha512-wSXmDRVupJstFP7elGMgv+2HqXelQhuNf+IS4V+nUpNVi/GUiBgDmfwD0UGN3pcAnWsgKG3I52wMOBnk1VHr/A== + +"@rollup/rollup-win32-ia32-msvc@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.28.1.tgz#06d1d60d5b9f718e8a6c4a43f82e3f9e3254587f" + integrity sha512-ZkyTJ/9vkgrE/Rk9vhMXhf8l9D+eAhbAVbsGsXKy2ohmJaWg0LPQLnIxRdRp/bKyr8tXuPlXhIoGlEB5XpJnGA== + +"@rollup/rollup-win32-x64-msvc@4.28.1": + version "4.28.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.28.1.tgz#4dff5c4259ebe6c5b4a8f2c5bc3829b7a8447ff0" + integrity sha512-ZvK2jBafvttJjoIdKm/Q/Bh7IJ1Ose9IBOwpOXcOvW3ikGTQGmKDgxTC6oCAzW6PynbkKP8+um1du81XJHZ0JA== "@rtsao/scc@^1.1.0": version "1.1.0" @@ -2939,10 +2944,10 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz#719df7fb41766bc143369eaa0dd56d8dc87c9958" integrity sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg== -"@storybook/addon-actions@8.4.6": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-8.4.6.tgz#d2501d84831051469356a5ec70ceba4bfe47d148" - integrity sha512-vbplwjMj7UXbdzoFhQkqFHLQAPJX8OVGTM9Q+yjuWDHViaKKUlgRWp0jclT7aIDNJQU2a6wJbTimHgJeF16Vhg== +"@storybook/addon-actions@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-8.4.7.tgz#210c6bb5a7e17c3664c300b4b69b6243ec34b9cd" + integrity sha512-mjtD5JxcPuW74T6h7nqMxWTvDneFtokg88p6kQ5OnC1M259iAXb//yiSZgu/quunMHPCXSiqn4FNOSgASTSbsA== dependencies: "@storybook/global" "^5.0.0" "@types/uuid" "^9.0.1" @@ -2950,99 +2955,99 @@ polished "^4.2.2" uuid "^9.0.0" -"@storybook/addon-backgrounds@8.4.6": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-8.4.6.tgz#4f2f1e6eded69137882af90facd6106eb201f271" - integrity sha512-RSjJ3iElxlQXebZrz1s5LeoLpAXr9LAGifX7w0abMzN5sg6QSwNeUHko2eT3V57M3k1Fa/5Eelso/QBQifFEog== +"@storybook/addon-backgrounds@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-8.4.7.tgz#56856bdafc5a2ba18cc19422320883c9e8f66c1c" + integrity sha512-I4/aErqtFiazcoWyKafOAm3bLpxTj6eQuH/woSbk1Yx+EzN+Dbrgx1Updy8//bsNtKkcrXETITreqHC+a57DHQ== dependencies: "@storybook/global" "^5.0.0" memoizerific "^1.11.3" ts-dedent "^2.0.0" -"@storybook/addon-controls@8.4.6": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-8.4.6.tgz#e07736244966c66a3df43fd48004d3621d6b0fdd" - integrity sha512-70pEGWh0C2g8s0DYsISElOzsMbQS6p/K9iU5EqfotDF+hvEqstjsV/bTbR5f3OK4vR/7Gxamk7j8RVd14Nql6A== +"@storybook/addon-controls@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-8.4.7.tgz#0c2ace0c7056248577f08f90471f29e861b485be" + integrity sha512-377uo5IsJgXLnQLJixa47+11V+7Wn9KcDEw+96aGCBCfLbWNH8S08tJHHnSu+jXg9zoqCAC23MetntVp6LetHA== dependencies: "@storybook/global" "^5.0.0" dequal "^2.0.2" ts-dedent "^2.0.0" -"@storybook/addon-docs@8.4.6": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-8.4.6.tgz#8afa3849066f9b0ee0600b9808a8da81278957cf" - integrity sha512-olxz61W7PW/EsXrKhLrYbI3rn9GMBhY3KIOF/6tumbRkh0Siu/qe4EAImaV9NNwiC1R7+De/1OIVMY6o0EIZVw== +"@storybook/addon-docs@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-8.4.7.tgz#556515da1049f97023427301e11ecb52d0b9dbe7" + integrity sha512-NwWaiTDT5puCBSUOVuf6ME7Zsbwz7Y79WF5tMZBx/sLQ60vpmJVQsap6NSjvK1Ravhc21EsIXqemAcBjAWu80w== dependencies: "@mdx-js/react" "^3.0.0" - "@storybook/blocks" "8.4.6" - "@storybook/csf-plugin" "8.4.6" - "@storybook/react-dom-shim" "8.4.6" + "@storybook/blocks" "8.4.7" + "@storybook/csf-plugin" "8.4.7" + "@storybook/react-dom-shim" "8.4.7" react "^16.8.0 || ^17.0.0 || ^18.0.0" react-dom "^16.8.0 || ^17.0.0 || ^18.0.0" ts-dedent "^2.0.0" "@storybook/addon-essentials@^8.1.1": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-8.4.6.tgz#30de1b679646dc0c71fa5e7d16c2dd0db8e01818" - integrity sha512-TbFqyvWFUKw8LBpVcZuGQydzVB/3kSuHxDHi+Wj3Qas3cxBl7+w4/HjwomT2D2Tni1dZ1uPDOsAtNLmwp1POsg== - dependencies: - "@storybook/addon-actions" "8.4.6" - "@storybook/addon-backgrounds" "8.4.6" - "@storybook/addon-controls" "8.4.6" - "@storybook/addon-docs" "8.4.6" - "@storybook/addon-highlight" "8.4.6" - "@storybook/addon-measure" "8.4.6" - "@storybook/addon-outline" "8.4.6" - "@storybook/addon-toolbars" "8.4.6" - "@storybook/addon-viewport" "8.4.6" + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-8.4.7.tgz#381c74230d1b1a209d5fdc017d241c016b98affe" + integrity sha512-+BtZHCBrYtQKILtejKxh0CDRGIgTl9PumfBOKRaihYb4FX1IjSAxoV/oo/IfEjlkF5f87vouShWsRa8EUauFDw== + dependencies: + "@storybook/addon-actions" "8.4.7" + "@storybook/addon-backgrounds" "8.4.7" + "@storybook/addon-controls" "8.4.7" + "@storybook/addon-docs" "8.4.7" + "@storybook/addon-highlight" "8.4.7" + "@storybook/addon-measure" "8.4.7" + "@storybook/addon-outline" "8.4.7" + "@storybook/addon-toolbars" "8.4.7" + "@storybook/addon-viewport" "8.4.7" ts-dedent "^2.0.0" -"@storybook/addon-highlight@8.4.6": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/addon-highlight/-/addon-highlight-8.4.6.tgz#0c6f06a4ef9e265cc1986e74b1bdff6af6f22792" - integrity sha512-m8wedbqDMbwkP99dNHkHAiAUkx5E7FEEEyLPX1zfkhZWOGtTkavXHH235SGp50zD75LQ6eC/BvgegrzxSQa9Wg== +"@storybook/addon-highlight@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/addon-highlight/-/addon-highlight-8.4.7.tgz#06b9752977e38884007e9446f9a2b0c04c873229" + integrity sha512-whQIDBd3PfVwcUCrRXvCUHWClXe9mQ7XkTPCdPo4B/tZ6Z9c6zD8JUHT76ddyHivixFLowMnA8PxMU6kCMAiNw== dependencies: "@storybook/global" "^5.0.0" "@storybook/addon-interactions@^8.1.1": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/addon-interactions/-/addon-interactions-8.4.6.tgz#cd6e5aaae6badf0f0f1475d0f3a4f12cc095de13" - integrity sha512-sR2oUSYIGUoAdrHT+fM1zgykhad98bsJ11c79r7HfBMXEPWc1yRcjIMmz8Xz06FMROMfebqduYDf60V++/I0Jw== + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/addon-interactions/-/addon-interactions-8.4.7.tgz#d34545db5ea6f03a5499ad6742c3317fb9e02d55" + integrity sha512-fnufT3ym8ht3HHUIRVXAH47iOJW/QOb0VSM+j269gDuvyDcY03D1civCu1v+eZLGaXPKJ8vtjr0L8zKQ/4P0JQ== dependencies: "@storybook/global" "^5.0.0" - "@storybook/instrumenter" "8.4.6" - "@storybook/test" "8.4.6" + "@storybook/instrumenter" "8.4.7" + "@storybook/test" "8.4.7" polished "^4.2.2" ts-dedent "^2.2.0" "@storybook/addon-links@^8.1.1": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/addon-links/-/addon-links-8.4.6.tgz#8f26e7497ba6141825b5634cebea00723ddff707" - integrity sha512-1KoG9ytEWWwdF/dheu1O0dayQTMsHw++Qk8afqw7bwW1Cxz5LuAJH5ZscFWMiE5f4Xq1NgaJdeAUaIavyoOcdg== + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/addon-links/-/addon-links-8.4.7.tgz#c38b2b63c3b0308adacff4b0758464a0657154c4" + integrity sha512-L/1h4dMeMKF+MM0DanN24v5p3faNYbbtOApMgg7SlcBT/tgo3+cAjkgmNpYA8XtKnDezm+T2mTDhB8mmIRZpIQ== dependencies: "@storybook/csf" "^0.1.11" "@storybook/global" "^5.0.0" ts-dedent "^2.0.0" -"@storybook/addon-measure@8.4.6": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/addon-measure/-/addon-measure-8.4.6.tgz#ae53f5f0dc4490e81bf06549f0d5364c78487493" - integrity sha512-N2IRpr39g5KpexCAS1vIHJT+phc9Yilwm3PULds2rQ66VMTbkxobXJDdt0NS05g5n9/eDniroNQwdCeLg4tkpw== +"@storybook/addon-measure@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/addon-measure/-/addon-measure-8.4.7.tgz#9d556ba34b57c13ad8d00bd953b27ec405a64d23" + integrity sha512-QfvqYWDSI5F68mKvafEmZic3SMiK7zZM8VA0kTXx55hF/+vx61Mm0HccApUT96xCXIgmwQwDvn9gS4TkX81Dmw== dependencies: "@storybook/global" "^5.0.0" tiny-invariant "^1.3.1" "@storybook/addon-onboarding@^8.1.1": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/addon-onboarding/-/addon-onboarding-8.4.6.tgz#ec94b7540c6e4c24caac748be6b85a933f427c9a" - integrity sha512-fCV7ipRYXTfOtK1ybh+jwgZB9DsNibn9zWKthmnGAXHgpB2qmvuDlnax8mz54pmsodL4ZnccYutUrPbsgjfEZw== + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/addon-onboarding/-/addon-onboarding-8.4.7.tgz#212a5e27db1ee8440a2dd0d5c67ac29f0e6efda5" + integrity sha512-FdC2NV60VNYeMxf6DVe0qV9ucSBAzMh1//C0Qqwq8CcjthMbmKlVZ7DqbVsbIHKnFaSCaUC88eR5olAfMaauCQ== dependencies: react-confetti "^6.1.0" -"@storybook/addon-outline@8.4.6": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/addon-outline/-/addon-outline-8.4.6.tgz#2f83b901f69d639e991b1572f45a3cae38834c74" - integrity sha512-EhcWx8OpK85HxQulLWzpWUHEwQpDYuAiKzsFj9ivAbfeljkIWNTG04mierfaH1xX016uL9RtLJL/zwBS5ChnFg== +"@storybook/addon-outline@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/addon-outline/-/addon-outline-8.4.7.tgz#8a35fe519dd639bb287a370da2222e6ffdce4020" + integrity sha512-6LYRqUZxSodmAIl8icr585Oi8pmzbZ90aloZJIpve+dBAzo7ydYrSQxxoQEVltXbKf3VeVcrs64ouAYqjisMYA== dependencies: "@storybook/global" "^5.0.0" ts-dedent "^2.0.0" @@ -3054,15 +3059,15 @@ dependencies: "@storybook/node-logger" "^8.0.0-alpha.10" -"@storybook/addon-toolbars@8.4.6": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-8.4.6.tgz#1ae32cda6c01d7052ced339681632921b91641d9" - integrity sha512-+Xao/uGa8FnYsyUiREUkYXWNysm3Aba8tL/Bwd+HufHtdiKJGa9lrXaC7VLCqBUaEjwqM3aaPwqEWIROsthmPQ== +"@storybook/addon-toolbars@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-8.4.7.tgz#b898d4deaf6f5a58f3b70bd8d136cd4ec2844b79" + integrity sha512-OSfdv5UZs+NdGB+nZmbafGUWimiweJ/56gShlw8Neo/4jOJl1R3rnRqqY7MYx8E4GwoX+i3GF5C3iWFNQqlDcw== -"@storybook/addon-viewport@8.4.6": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-8.4.6.tgz#07e9d231b9857666024d4282348870a4ddfc82c3" - integrity sha512-BuQll5YzOCpMS7p5Rsw9wcmi8hTnEKyg6+qAbkZNfiZ2JhXCa1GFUqX725fF1whpYVQULtkQxU8r+vahoRn7Yg== +"@storybook/addon-viewport@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-8.4.7.tgz#e65c53608f52149c06347b395487960605fc4805" + integrity sha512-hvczh/jjuXXcOogih09a663sRDDSATXwbE866al1DXgbDFraYD/LxX/QDb38W9hdjU9+Qhx8VFIcNWoMQns5HQ== dependencies: memoizerific "^1.11.3" @@ -3074,21 +3079,21 @@ "@swc/core" "^1.7.3" swc-loader "^0.2.3" -"@storybook/blocks@8.4.6", "@storybook/blocks@^8.1.1": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/blocks/-/blocks-8.4.6.tgz#57e340e50fc3add1b08dd50dd16980b64e9c7c98" - integrity sha512-Gzbx8hM7ZQIHlQELcFIMbY1v+r1Po4mlinq0QVPtKS4lBcW4eZIsesbxOaL+uFNrxb583TLFzXo0DbRPzS46sg== +"@storybook/blocks@8.4.7", "@storybook/blocks@^8.1.1": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/blocks/-/blocks-8.4.7.tgz#ee17f59dd52d11c97c39b0f6b03957085a80ad95" + integrity sha512-+QH7+JwXXXIyP3fRCxz/7E2VZepAanXJM7G8nbR3wWsqWgrRp4Wra6MvybxAYCxU7aNfJX5c+RW84SNikFpcIA== dependencies: "@storybook/csf" "^0.1.11" "@storybook/icons" "^1.2.12" ts-dedent "^2.0.0" -"@storybook/builder-webpack5@8.4.6": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/builder-webpack5/-/builder-webpack5-8.4.6.tgz#4e650fb61d4d4e5161918c8f383ba8e4da2a7664" - integrity sha512-/ZInCFk2myJZinnAU05bATe+9iJn3+YRoxl+CUpYljxzsjoqb7iAwaNaMNolZCDOnMj24Kg2Pt87WtzAhu+ilw== +"@storybook/builder-webpack5@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/builder-webpack5/-/builder-webpack5-8.4.7.tgz#5bc15568716bbf4f45a88fc389e25fa2ce50a8c2" + integrity sha512-O8LpsQ+4g2x5kh7rI9+jEUdX8k1a5egBQU1lbudmHchqsV0IKiVqBD9LL5Gj3wpit4vB8coSW4ZWTFBw8FQb4Q== dependencies: - "@storybook/core-webpack" "8.4.6" + "@storybook/core-webpack" "8.4.7" "@types/node" "^22.0.0" "@types/semver" "^7.3.4" browser-assert "^1.2.1" @@ -3114,23 +3119,23 @@ webpack-hot-middleware "^2.25.1" webpack-virtual-modules "^0.6.0" -"@storybook/components@8.4.6": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/components/-/components-8.4.6.tgz#2c406a709189008172c8cf4f55d05254954bae4d" - integrity sha512-9tKSJJCyFT5RZMRGyozTBJkr9C9Yfk1nuOE9XbDEE1Z+3/IypKR9+iwc5mfNBStDNY+rxtYWNLKBb5GPR2yhzA== +"@storybook/components@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/components/-/components-8.4.7.tgz#09eeffa07aa672ad3966ca1764a43003731b1d30" + integrity sha512-uyJIcoyeMWKAvjrG9tJBUCKxr2WZk+PomgrgrUwejkIfXMO76i6jw9BwLa0NZjYdlthDv30r9FfbYZyeNPmF0g== -"@storybook/core-webpack@8.4.6": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/core-webpack/-/core-webpack-8.4.6.tgz#5cedabc15e00043a532ff6a61ec4041a653e3bda" - integrity sha512-5NE4pUy0iHKc8UKHm7A7SikxSMOO92udl7kcBUlQKwcAgrAk1rFpvfXGodDb48AVgfK+07qAOSjBm7GcZ7X1jw== +"@storybook/core-webpack@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/core-webpack/-/core-webpack-8.4.7.tgz#660d1cbd03a91fee27b65e6acc2f9269ed1fbfc8" + integrity sha512-Tj+CjQLpFyBJxhhMms+vbPT3+gTRAiQlrhY3L1IEVwBa3wtRMS0qjozH26d1hK4G6mUIEdwu13L54HMU/w33Sg== dependencies: "@types/node" "^22.0.0" ts-dedent "^2.0.0" -"@storybook/core@8.4.6": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/core/-/core-8.4.6.tgz#78a3155f428635be808c4ef2912eeb79140228c8" - integrity sha512-WeojVtHy0/t50tzw/15S+DLzKsj8BN9yWdo3vJMvm+nflLFvfq1XvD9WGOWeaFp8E/o3AP+4HprXG0r42KEJtA== +"@storybook/core@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/core/-/core-8.4.7.tgz#af9cbb3f26f0b6c98c679a134ce776c202570d66" + integrity sha512-7Z8Z0A+1YnhrrSXoKKwFFI4gnsLbWzr8fnDCU6+6HlDukFYh8GHRcZ9zKfqmy6U3hw2h8H5DrHsxWfyaYUUOoA== dependencies: "@storybook/csf" "^0.1.11" better-opn "^3.0.2" @@ -3144,10 +3149,10 @@ util "^0.12.5" ws "^8.2.3" -"@storybook/csf-plugin@8.4.6": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/csf-plugin/-/csf-plugin-8.4.6.tgz#861c64248e10827c8fdf264c23d149c2dde828cf" - integrity sha512-JDIT0czC4yMgKGNf39KTZr3zm5MusAZdn6LBrTfvWb7CrTCR4iVHa4lp2yb7EJk41vHsBec0QUYDDuiFH/vV0g== +"@storybook/csf-plugin@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/csf-plugin/-/csf-plugin-8.4.7.tgz#0117c872b05bf033eec089ab0224e0fab01da810" + integrity sha512-Fgogplu4HImgC+AYDcdGm1rmL6OR1rVdNX1Be9C/NEXwOCpbbBwi0BxTf/2ZxHRk9fCeaPEcOdP5S8QHfltc1g== dependencies: unplugin "^1.3.1" @@ -3164,35 +3169,35 @@ integrity sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ== "@storybook/icons@^1.2.12": - version "1.2.12" - resolved "https://registry.yarnpkg.com/@storybook/icons/-/icons-1.2.12.tgz#3e4c939113b67df7ab17b78f805dbb57f4acf0db" - integrity sha512-UxgyK5W3/UV4VrI3dl6ajGfHM4aOqMAkFLWe2KibeQudLf6NJpDrDMSHwZj+3iKC4jFU7dkKbbtH2h/al4sW3Q== + version "1.3.0" + resolved "https://registry.yarnpkg.com/@storybook/icons/-/icons-1.3.0.tgz#a5c1460fb15a7260e0b638ab86163f7347a0061e" + integrity sha512-Nz/UzeYQdUZUhacrPyfkiiysSjydyjgg/p0P9HxB4p/WaJUUjMAcaoaLgy3EXx61zZJ3iD36WPuDkZs5QYrA0A== -"@storybook/instrumenter@8.4.6": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/instrumenter/-/instrumenter-8.4.6.tgz#3e760d6aecf4041dd7aaeeb9dde5a2d365607462" - integrity sha512-snXjlgbp065A6KoK9zkjBYEIMCSlN5JefPKzt1FC0rbcbtahhD+iPpqISKhDSczwgOku/JVhVUDp/vU7AIf4mg== +"@storybook/instrumenter@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/instrumenter/-/instrumenter-8.4.7.tgz#5a37876fee8f828241a1e7fd76891c6effc1805a" + integrity sha512-k6NSD3jaRCCHAFtqXZ7tw8jAzD/yTEWXGya+REgZqq5RCkmJ+9S4Ytp/6OhQMPtPFX23gAuJJzTQVLcCr+gjRg== dependencies: "@storybook/global" "^5.0.0" "@vitest/utils" "^2.1.1" -"@storybook/manager-api@8.4.6": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/manager-api/-/manager-api-8.4.6.tgz#844a8b71de7eae44998a83b96666a05e6e49156c" - integrity sha512-TsXlQ5m5rTl2KNT9icPFyy822AqXrx1QplZBt/L7cFn7SpqQKDeSta21FH7MG0piAvzOweXebVSqKngJ6cCWWQ== +"@storybook/manager-api@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/manager-api/-/manager-api-8.4.7.tgz#4e13debf645c9300d7d6d49195e720d0c7ecd261" + integrity sha512-ELqemTviCxAsZ5tqUz39sDmQkvhVAvAgiplYy9Uf15kO0SP2+HKsCMzlrm2ue2FfkUNyqbDayCPPCB0Cdn/mpQ== "@storybook/node-logger@^8.0.0-alpha.10": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-8.4.6.tgz#04f3be51a21c4415cfe4ce496a2126f7e35b1b2d" - integrity sha512-MC2UTE+TmlJ7E3Oh2IzVfJLmitbvVgqHLfULWHi9UdvzUvDO6p3p44t+C3v7jRMy8UETtkbrtbUEYPR17gzFpA== + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-8.4.7.tgz#9bc5adc7ea820b41ba7602890ac5c5e090bc4419" + integrity sha512-bsNMy9RgN4jVw5MMHMf0T04dX8a1lLTWq4iZoZkfdbshDKn5Z16brzPVwhFg2IE0YTEZi9XyA1NJLPgUTCcp7g== -"@storybook/preset-react-webpack@8.4.6": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/preset-react-webpack/-/preset-react-webpack-8.4.6.tgz#6df6255af7063284b01f55840fb98343f27c62c6" - integrity sha512-4AdsRug6NaWras/bbcFx7KEnJY5GLt3REPlSqMtZwhdY3f7LF0W++Wyo/F2ly82f57welquGURyL+YxjQ9yFkA== +"@storybook/preset-react-webpack@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/preset-react-webpack/-/preset-react-webpack-8.4.7.tgz#7b303f464228035a919ea18a3cd6193f6776c2bc" + integrity sha512-geTSBKyrBagVihil5MF7LkVFynbfHhCinvnbCZZqXW7M1vgcxvatunUENB+iV8eWg/0EJ+8O7scZL+BAxQ/2qg== dependencies: - "@storybook/core-webpack" "8.4.6" - "@storybook/react" "8.4.6" + "@storybook/core-webpack" "8.4.7" + "@storybook/react" "8.4.7" "@storybook/react-docgen-typescript-plugin" "1.0.6--canary.9.0c3f3b7.0" "@types/node" "^22.0.0" "@types/semver" "^7.3.4" @@ -3204,10 +3209,10 @@ tsconfig-paths "^4.2.0" webpack "5" -"@storybook/preview-api@8.4.6": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/preview-api/-/preview-api-8.4.6.tgz#5c167e5c0fceed1010da27bcf08c81e2449cb538" - integrity sha512-LbD+lR1FGvWaJBXteVx5xdgs1x1D7tyidBg2CsW2ex+cP0iJ176JgjPfutZxlWOfQnhfRYNnJ3WKoCIfxFOTKA== +"@storybook/preview-api@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/preview-api/-/preview-api-8.4.7.tgz#85e01a97f4182b974581765d725f6c7a7d190013" + integrity sha512-0QVQwHw+OyZGHAJEXo6Knx+6/4er7n2rTDE5RYJ9F2E2Lg42E19pfdLlq2Jhoods2Xrclo3wj6GWR//Ahi39Eg== "@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0": version "1.0.6--canary.9.0c3f3b7.0" @@ -3222,120 +3227,120 @@ react-docgen-typescript "^2.2.2" tslib "^2.0.0" -"@storybook/react-dom-shim@8.4.6": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/react-dom-shim/-/react-dom-shim-8.4.6.tgz#526fa7a7c754cbb5e42f2ade4b167f210a4fd70e" - integrity sha512-f7RM8GO++fqMxbjNdEzeGS1P821jXuwRnAraejk5hyjB5SqetauFxMwoFYEYfJXPaLX2qIubnIJ78hdJ/IBaEA== +"@storybook/react-dom-shim@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/react-dom-shim/-/react-dom-shim-8.4.7.tgz#f0dd5bbf2fc185def72d9d08a11c8de22f152c2a" + integrity sha512-6bkG2jvKTmWrmVzCgwpTxwIugd7Lu+2btsLAqhQSzDyIj2/uhMNp8xIMr/NBDtLgq3nomt9gefNa9xxLwk/OMg== "@storybook/react-webpack5@^8.1.1": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/react-webpack5/-/react-webpack5-8.4.6.tgz#babd1c0fdc6f60f27d6c3737c7fbd1a52d2a1335" - integrity sha512-qUCOUoYW09voRhk0PzEZpZz6F5Ek9aHvVto8KW3lyYEuk6qujqUTNO6Y/X7hMraVt/C3l0+Ds4D5LEmxNBvd8g== + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/react-webpack5/-/react-webpack5-8.4.7.tgz#b0c48caffd658912bd10f4f9960edb229e990b4b" + integrity sha512-T9GLqlsP4It4El7cC8rSkBPRWvORAsTDULeWlO36RST2TrYnmBOUytsi22mk7cAAAVhhD6rTrs1YdqWRMpfa1w== dependencies: - "@storybook/builder-webpack5" "8.4.6" - "@storybook/preset-react-webpack" "8.4.6" - "@storybook/react" "8.4.6" + "@storybook/builder-webpack5" "8.4.7" + "@storybook/preset-react-webpack" "8.4.7" + "@storybook/react" "8.4.7" "@types/node" "^22.0.0" -"@storybook/react@8.4.6", "@storybook/react@^8.1.1": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/react/-/react-8.4.6.tgz#6e8f2b93bf044db123a9d49a17e867afab02e806" - integrity sha512-QAT23beoYNLhFGAXPimtuMErvpcI7eZbZ4AlLqW1fhiTZrRYw06cjC1bs9H3tODMcHH9LS5p3Wz9b29jtV2XGw== +"@storybook/react@8.4.7", "@storybook/react@^8.1.1": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/react/-/react-8.4.7.tgz#e2cf62b3c1d8e4bfe5eff82ced07ec473d4e4fd1" + integrity sha512-nQ0/7i2DkaCb7dy0NaT95llRVNYWQiPIVuhNfjr1mVhEP7XD090p0g7eqUmsx8vfdHh2BzWEo6CoBFRd3+EXxw== dependencies: - "@storybook/components" "8.4.6" + "@storybook/components" "8.4.7" "@storybook/global" "^5.0.0" - "@storybook/manager-api" "8.4.6" - "@storybook/preview-api" "8.4.6" - "@storybook/react-dom-shim" "8.4.6" - "@storybook/theming" "8.4.6" + "@storybook/manager-api" "8.4.7" + "@storybook/preview-api" "8.4.7" + "@storybook/react-dom-shim" "8.4.7" + "@storybook/theming" "8.4.7" -"@storybook/test@8.4.6", "@storybook/test@^8.1.1": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/test/-/test-8.4.6.tgz#7ea0ac47b8492e04be2a11d7c1c6be54399c9355" - integrity sha512-MeU1g65YgU66M2NtmEIL9gVeHk+en0k9Hp0wfxEO7NT/WLfaOD5RXLRDJVhbAlrH/6tLeWKIPNh/D26y27vO/g== +"@storybook/test@8.4.7", "@storybook/test@^8.1.1": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/test/-/test-8.4.7.tgz#7f58f2cdf3a6d810bf3ff4e0e2fee634040c678f" + integrity sha512-AhvJsu5zl3uG40itSQVuSy5WByp3UVhS6xAnme4FWRwgSxhvZjATJ3AZkkHWOYjnnk+P2/sbz/XuPli1FVCWoQ== dependencies: "@storybook/csf" "^0.1.11" "@storybook/global" "^5.0.0" - "@storybook/instrumenter" "8.4.6" + "@storybook/instrumenter" "8.4.7" "@testing-library/dom" "10.4.0" "@testing-library/jest-dom" "6.5.0" "@testing-library/user-event" "14.5.2" "@vitest/expect" "2.0.5" "@vitest/spy" "2.0.5" -"@storybook/theming@8.4.6": - version "8.4.6" - resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-8.4.6.tgz#40b03d72b014cc0bb6530aba0652b49e176b0c0e" - integrity sha512-q7vDPN/mgj7cXIVQ9R1/V75hrzNgKkm2G0LjMo57//9/djQ+7LxvBsR1iScbFIRSEqppvMiBFzkts+2uXidySA== - -"@swc/core-darwin-arm64@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.0.tgz#39fd894356f8e858535e96111d34602da0a730c5" - integrity sha512-wCeUpanqZyzvgqWRtXIyhcFK3CqukAlYyP+fJpY2gWc/+ekdrenNIfZMwY7tyTFDkXDYEKzvn3BN/zDYNJFowQ== - -"@swc/core-darwin-x64@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.10.0.tgz#d1b95c1db67ac328a96324b800843bc410d17f05" - integrity sha512-0CZPzqTynUBO+SHEl/qKsFSahp2Jv/P2ZRjFG0gwZY5qIcr1+B/v+o74/GyNMBGz9rft+F2WpU31gz2sJwyF4A== - -"@swc/core-linux-arm-gnueabihf@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.0.tgz#e10510bb028bc3836948cb7345312269cd22295d" - integrity sha512-oq+DdMu5uJOFPtRkeiITc4kxmd+QSmK+v+OBzlhdGkSgoH3yRWZP+H2ao0cBXo93ZgCr2LfjiER0CqSKhjGuNA== - -"@swc/core-linux-arm64-gnu@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.0.tgz#a4826c0b44db5b5a02826a0c47307f5969bcc353" - integrity sha512-Y6+PC8knchEViRxiCUj3j8wsGXaIhuvU+WqrFqV834eiItEMEI9+Vh3FovqJMBE3L7d4E4ZQtgImHCXjrHfxbw== - -"@swc/core-linux-arm64-musl@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.0.tgz#d4adab4a646be095e3c64226a0150ebe4b874c1a" - integrity sha512-EbrX9A5U4cECCQQfky7945AW9GYnTXtCUXElWTkTYmmyQK87yCyFfY8hmZ9qMFIwxPOH6I3I2JwMhzdi8Qoz7g== - -"@swc/core-linux-x64-gnu@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.0.tgz#278655c2b2abcb2e7ada031e75e6853777ebce4c" - integrity sha512-TaxpO6snTjjfLXFYh5EjZ78se69j2gDcqEM8yB9gguPYwkCHi2Ylfmh7iVaNADnDJFtjoAQp0L41bTV/Pfq9Cg== - -"@swc/core-linux-x64-musl@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.0.tgz#7df236de40a685c1723a904d6dead99eea36a30f" - integrity sha512-IEGvDd6aEEKEyZFZ8oCKuik05G5BS7qwG5hO5PEMzdGeh8JyFZXxsfFXbfeAqjue4UaUUrhnoX+Ze3M2jBVMHw== - -"@swc/core-win32-arm64-msvc@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.0.tgz#99278f8f02c79e03caeeb6d64941d0487e58d7e1" - integrity sha512-UkQ952GSpY+Z6XONj9GSW8xGSkF53jrCsuLj0nrcuw7Dvr1a816U/9WYZmmcYS8tnG2vHylhpm6csQkyS8lpCw== - -"@swc/core-win32-ia32-msvc@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.0.tgz#a5cf2cfa3e31e8e01a3692d7e053aaa788d3cf3e" - integrity sha512-a2QpIZmTiT885u/mUInpeN2W9ClCnqrV2LnMqJR1/Fgx1Afw/hAtiDZPtQ0SqS8yDJ2VR5gfNZo3gpxWMrqdVA== - -"@swc/core-win32-x64-msvc@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.0.tgz#ee1fdf8e6a627de33501b5a404465a7e676c8689" - integrity sha512-tZcCmMwf483nwsEBfUk5w9e046kMa1iSik4bP9Kwi2FGtOfHuDfIcwW4jek3hdcgF5SaBW1ktnK/lgQLDi5AtA== +"@storybook/theming@8.4.7": + version "8.4.7" + resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-8.4.7.tgz#c308f6a883999bd35e87826738ab8a76515932b5" + integrity sha512-99rgLEjf7iwfSEmdqlHkSG3AyLcK0sfExcr0jnc6rLiAkBhzuIsvcHjjUwkR210SOCgXqBPW0ZA6uhnuyppHLw== + +"@swc/core-darwin-arm64@1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.1.tgz#faaaab19b4a039ae67ef661c0144a6f20fe8a78e" + integrity sha512-NyELPp8EsVZtxH/mEqvzSyWpfPJ1lugpTQcSlMduZLj1EASLO4sC8wt8hmL1aizRlsbjCX+r0PyL+l0xQ64/6Q== + +"@swc/core-darwin-x64@1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.10.1.tgz#754600f453abd24471c202d48836f1161d798f49" + integrity sha512-L4BNt1fdQ5ZZhAk5qoDfUnXRabDOXKnXBxMDJ+PWLSxOGBbWE6aJTnu4zbGjJvtot0KM46m2LPAPY8ttknqaZA== + +"@swc/core-linux-arm-gnueabihf@1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.1.tgz#b0f43c482d0d1819b382a4eb4a0733ce2e386257" + integrity sha512-Y1u9OqCHgvVp2tYQAJ7hcU9qO5brDMIrA5R31rwWQIAKDkJKtv3IlTHF0hrbWk1wPR0ZdngkQSJZple7G+Grvw== + +"@swc/core-linux-arm64-gnu@1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.1.tgz#e02a9e22c25ba85ef00335742e549e06284cf33a" + integrity sha512-tNQHO/UKdtnqjc7o04iRXng1wTUXPgVd8Y6LI4qIbHVoVPwksZydISjMcilKNLKIwOoUQAkxyJ16SlOAeADzhQ== + +"@swc/core-linux-arm64-musl@1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.1.tgz#3a0530af8f8bd3717f2f1bd8a2f5183fc58d4cf1" + integrity sha512-x0L2Pd9weQ6n8dI1z1Isq00VHFvpBClwQJvrt3NHzmR+1wCT/gcYl1tp9P5xHh3ldM8Cn4UjWCw+7PaUgg8FcQ== + +"@swc/core-linux-x64-gnu@1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.1.tgz#5eb4d282b047a22896ab1d4627403be4c3e4fa6a" + integrity sha512-yyYEwQcObV3AUsC79rSzN9z6kiWxKAVJ6Ntwq2N9YoZqSPYph+4/Am5fM1xEQYf/kb99csj0FgOelomJSobxQA== + +"@swc/core-linux-x64-musl@1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.1.tgz#890f2eda3e67ccc6817cdd04eff91e6ad9e761c4" + integrity sha512-tcaS43Ydd7Fk7sW5ROpaf2Kq1zR+sI5K0RM+0qYLYYurvsJruj3GhBCaiN3gkzd8m/8wkqNqtVklWaQYSDsyqA== + +"@swc/core-win32-arm64-msvc@1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.1.tgz#4ea7b2a2fab47f801d31ea8b001a141efaa5e6bf" + integrity sha512-D3Qo1voA7AkbOzQ2UGuKNHfYGKL6eejN8VWOoQYtGHHQi1p5KK/Q7V1ku55oxXBsj79Ny5FRMqiRJpVGad7bjQ== + +"@swc/core-win32-ia32-msvc@1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.1.tgz#729102669ccdb72e69884cce58e3686ac63d6f36" + integrity sha512-WalYdFoU3454Og+sDKHM1MrjvxUGwA2oralknXkXL8S0I/8RkWZOB++p3pLaGbTvOO++T+6znFbQdR8KRaa7DA== + +"@swc/core-win32-x64-msvc@1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.1.tgz#7d665a7c69642861aed850ecb0cdf5d87197edda" + integrity sha512-JWobfQDbTnoqaIwPKQ3DVSywihVXlQMbDuwik/dDWlj33A8oEHcjPOGs4OqcA3RHv24i+lfCQpM3Mn4FAMfacA== "@swc/core@^1.7.3": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.10.0.tgz#9584465f7c5feaf34098466c7063c0044fa08bd8" - integrity sha512-+CuuTCmQFfzaNGg1JmcZvdUVITQXJk9sMnl1C2TiDLzOSVOJRwVD4dNo5dljX/qxpMAN+2BIYlwjlSkoGi6grg== + version "1.10.1" + resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.10.1.tgz#16b3b8284bafb0ecabb253925796883971e5a761" + integrity sha512-rQ4dS6GAdmtzKiCRt3LFVxl37FaY1cgL9kSUTnhQ2xc3fmHOd7jdJK/V4pSZMG1ruGTd0bsi34O2R0Olg9Zo/w== dependencies: "@swc/counter" "^0.1.3" "@swc/types" "^0.1.17" optionalDependencies: - "@swc/core-darwin-arm64" "1.10.0" - "@swc/core-darwin-x64" "1.10.0" - "@swc/core-linux-arm-gnueabihf" "1.10.0" - "@swc/core-linux-arm64-gnu" "1.10.0" - "@swc/core-linux-arm64-musl" "1.10.0" - "@swc/core-linux-x64-gnu" "1.10.0" - "@swc/core-linux-x64-musl" "1.10.0" - "@swc/core-win32-arm64-msvc" "1.10.0" - "@swc/core-win32-ia32-msvc" "1.10.0" - "@swc/core-win32-x64-msvc" "1.10.0" + "@swc/core-darwin-arm64" "1.10.1" + "@swc/core-darwin-x64" "1.10.1" + "@swc/core-linux-arm-gnueabihf" "1.10.1" + "@swc/core-linux-arm64-gnu" "1.10.1" + "@swc/core-linux-arm64-musl" "1.10.1" + "@swc/core-linux-x64-gnu" "1.10.1" + "@swc/core-linux-x64-musl" "1.10.1" + "@swc/core-win32-arm64-msvc" "1.10.1" + "@swc/core-win32-ia32-msvc" "1.10.1" + "@swc/core-win32-x64-msvc" "1.10.1" "@swc/counter@^0.1.3": version "0.1.3" @@ -3375,9 +3380,9 @@ postcss-selector-parser "6.0.10" "@tanstack/react-virtual@^3.0.0-beta.60": - version "3.10.9" - resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.10.9.tgz#40606b6dd8aba8e977f576d8f7df07f69ca63eea" - integrity sha512-OXO2uBjFqA4Ibr2O3y0YMnkrRWGVNqcvHQXmGvMu6IK8chZl3PrDxFXdGZ2iZkSrKh3/qUYoFqYe+Rx23RoU0g== + version "3.11.0" + resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.11.0.tgz#d847da68ade193155a9621fed56e4817ae24084f" + integrity sha512-liRl34SrQm54NZdf22d/H4a7GucPNCxBSJdWWZlUrF1E1oXcZ3/GfRRHFDUJXwEuTfjtyp0X6NnUK7bhIuDzoQ== dependencies: "@tanstack/virtual-core" "3.10.9" @@ -4096,9 +4101,9 @@ pg-types "^2.2.0" "@types/prop-types@*", "@types/prop-types@^15.0.0", "@types/prop-types@^15.7.12", "@types/prop-types@^15.7.2": - version "15.7.13" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.13.tgz#2af91918ee12d9d32914feb13f5326658461b451" - integrity sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA== + version "15.7.14" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.14.tgz#1433419d73b2a7ebfc6918dcefd2ec0d5cd698f2" + integrity sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ== "@types/qs@*": version "6.9.17" @@ -4119,11 +4124,11 @@ "@types/reactcss" "*" "@types/react-dom@^18.2.18": - version "18.3.1" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.1.tgz#1e4654c08a9cdcfb6594c780ac59b55aad42fe07" - integrity sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ== + version "18.3.2" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.2.tgz#b58a9520f5f317a00bbda0271502889b71c345f0" + integrity sha512-Fqp+rcvem9wEnGr3RY8dYNvSQ8PoLqjZ9HLgaPUOjJJD120uDyOxOjc/39M4Kddp9JQCxpGQbnhVQF0C0ncYVg== dependencies: - "@types/react" "*" + "@types/react" "^18" "@types/react-transition-group@^4.4.10": version "4.4.11" @@ -4132,10 +4137,10 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^18.3.11": - version "18.3.13" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.13.tgz#84c9690d9a271f548659760754ea8745701bfd82" - integrity sha512-ii/gswMmOievxAJed4PAHT949bpYjPKXvXo1v6cRB/kqc2ZR4n+SgyCyvyc5Fec5ez8VnUumI1Vk7j6fRyRogg== +"@types/react@*", "@types/react@^18", "@types/react@^18.3.11": + version "18.3.14" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.14.tgz#7ce43bbca0e15e1c4f67ad33ea3f83d75aa6756b" + integrity sha512-NzahNKvjNhVjuPBQ+2G7WlxstQ+47kXZNHlUvFakDViuIEfGY926GqhMueQFZ7woG+sPiQKlF36XfrIUVSUfFg== dependencies: "@types/prop-types" "*" csstype "^3.0.2" @@ -4384,9 +4389,9 @@ eslint-visitor-keys "^4.2.0" "@ungap/structured-clone@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" - integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + version "1.2.1" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.1.tgz#28fa185f67daaf7b7a1a8c1d445132c5d979f8bd" + integrity sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA== "@vitest/expect@2.0.5": version "2.0.5" @@ -4618,12 +4623,10 @@ agent-base@6: dependencies: debug "4" -agent-base@^7.0.2, agent-base@^7.1.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" - integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== - dependencies: - debug "^4.3.4" +agent-base@^7.1.0, agent-base@^7.1.2: + version "7.1.3" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1" + integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw== ajv-formats@^2.1.1: version "2.1.1" @@ -5190,16 +5193,23 @@ cac@^6.7.12: resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== -call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" - integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== +call-bind-apply-helpers@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz#32e5892e6361b29b0b545ba6f7763378daca2840" + integrity sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g== dependencies: - es-define-property "^1.0.0" es-errors "^1.3.0" function-bind "^1.1.2" + +call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7, call-bind@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" + integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== + dependencies: + call-bind-apply-helpers "^1.0.0" + es-define-property "^1.0.0" get-intrinsic "^1.2.4" - set-function-length "^1.2.1" + set-function-length "^1.2.2" callsites@^3.0.0: version "3.1.0" @@ -5220,9 +5230,9 @@ camelcase-css@^2.0.1: integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== caniuse-lite@^1.0.30001464, caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001669: - version "1.0.30001686" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001686.tgz#0e04b8d90de8753188e93c9989d56cb19d902670" - integrity sha512-Y7deg0Aergpa24M3qLC5xjNklnKnhsmSyR/V89dLZ1n0ucJIFNs7PgR2Yfa/Zf6W79SbBicgtGxZr2juHkEUIA== + version "1.0.30001687" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001687.tgz#d0ac634d043648498eedf7a3932836beba90ebae" + integrity sha512-0S/FDhf4ZiqrTUiQ39dKeUjYRjkv7lOZU1Dgif2rIqrTzX/1wV2hfKu9TOm1IHkdSijfLswxTFzl/cvir+SLSQ== capital-case@^1.0.4: version "1.0.4" @@ -5885,9 +5895,9 @@ debug@2.6.9: ms "2.0.0" debug@4, debug@^4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.3.7: - version "4.3.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" - integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== dependencies: ms "^2.1.3" @@ -6155,9 +6165,9 @@ domhandler@^5.0.2, domhandler@^5.0.3: domelementtype "^2.3.0" dompurify@*, dompurify@^3.0.11, dompurify@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.2.2.tgz#6c0518745e81686c74a684f5af1e5613e7cc0246" - integrity sha512-YMM+erhdZ2nkZ4fTNRTSI94mb7VG7uVF5vj5Zde7tImgnhZE3R6YW/IACGIHb2ux+QkEXMhe591N+5jWOmL4Zw== + version "3.2.3" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.2.3.tgz#05dd2175225324daabfca6603055a09b2382a4cd" + integrity sha512-U1U5Hzc2MO0oW3DF+G9qYN0aT7atAou4AgI0XjWz061nyBPbdxkfdhfy5uMgGn6+oLFCfn44ZGbdDqCzVmlOWA== optionalDependencies: "@types/trusted-types" "^2.0.7" @@ -6197,6 +6207,15 @@ dotenv@16.0.3: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== +dunder-proto@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.0.tgz#c2fce098b3c8f8899554905f4377b6d85dabaa80" + integrity sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A== + dependencies: + call-bind-apply-helpers "^1.0.0" + es-errors "^1.3.0" + gopd "^1.2.0" + eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" @@ -6208,9 +6227,9 @@ ee-first@1.1.1: integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.5.41: - version "1.5.70" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.70.tgz#8be2f92e54a3d069c0eefcf766a48bf0f78d93e0" - integrity sha512-P6FPqAWIZrC3sHDAwBitJBs7N7IF58m39XVny7DFseQXK2eiMn7nNQizFf63mWDDUnFvaqsM8FI0+ZZfLkdUGA== + version "1.5.71" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.71.tgz#d8b5dba1e55b320f2f4e9b1ca80738f53fcfec2b" + integrity sha512-dB68l59BI75W1BUGVTAEJy45CEVuEGy9qPVVQ8pnHyHMn36PLPPoE1mjLH+lo9rKulO3HC2OhbACI/8tCqJBcA== emoji-picker-react@^4.5.16: version "4.12.0" @@ -6342,12 +6361,10 @@ es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23 unbox-primitive "^1.0.2" which-typed-array "^1.1.15" -es-define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" - integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== - dependencies: - get-intrinsic "^1.2.4" +es-define-property@^1.0.0, es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== es-errors@^1.2.1, es-errors@^1.3.0: version "1.3.0" @@ -7208,15 +7225,18 @@ get-caller-file@^2.0.5: integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" - integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + version "1.2.5" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.5.tgz#dfe7dd1b30761b464fe51bf4bb00ac7c37b681e7" + integrity sha512-Y4+pKa7XeRUPWFNvOOYHkRYrfzW07oraURSvjDmRVOJ748OrVmeXtpE4+GCEHncjCjkTxPNRt8kEbxDhsn6VTg== dependencies: + call-bind-apply-helpers "^1.0.0" + dunder-proto "^1.0.0" + es-define-property "^1.0.1" es-errors "^1.3.0" function-bind "^1.1.2" - has-proto "^1.0.1" - has-symbols "^1.0.3" - hasown "^2.0.0" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" get-nonce@^1.0.0: version "1.0.1" @@ -7362,7 +7382,7 @@ globby@^14.0.0: slash "^5.1.0" unicorn-magic "^0.1.0" -gopd@^1.0.1, gopd@^1.1.0: +gopd@^1.0.1, gopd@^1.1.0, gopd@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== @@ -7404,14 +7424,14 @@ has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: dependencies: es-define-property "^1.0.0" -has-proto@^1.0.1, has-proto@^1.0.3: - version "1.1.0" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.1.0.tgz#deb10494cbbe8809bce168a3b961f42969f5ed43" - integrity sha512-QLdzI9IIO1Jg7f9GT1gXpPpXArAn6cS31R1eEZqz08Gc+uQ8/XiqHWt17Fiw+2p6oTTIq5GXEpQkAlA88YRl/Q== +has-proto@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.2.0.tgz#5de5a6eabd95fdffd9818b43055e8065e39fe9d5" + integrity sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ== dependencies: - call-bind "^1.0.7" + dunder-proto "^1.0.0" -has-symbols@^1.0.3: +has-symbols@^1.0.3, has-symbols@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== @@ -7561,11 +7581,11 @@ https-proxy-agent@^5.0.0: debug "4" https-proxy-agent@^7.0.5: - version "7.0.5" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2" - integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw== + version "7.0.6" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" + integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== dependencies: - agent-base "^7.0.2" + agent-base "^7.1.2" debug "4" human-signals@^2.1.0: @@ -9763,9 +9783,9 @@ postgres-range@^1.1.1: integrity sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w== posthog-js@^1.131.3: - version "1.194.3" - resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.194.3.tgz#4ae936a1e33f32ae960708e8df898f13593c84ce" - integrity sha512-/YFpBMqZzRpywa07QeoaIojdrUDijFajT4gZBSCFUBuZA5BN5xr5S1spsvtpT7E4RjkQSVgRvUngI4W19csgQw== + version "1.194.6" + resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.194.6.tgz#58fd5d2b1b4a125573ba6fcf546e0fb92142085d" + integrity sha512-5g5n7FjWLha/QWVTeWeMErGff21v4/V3wYCZ2z8vAbHaCyHkaDBEbuM756jMFBQMsq3HJcDX9mlxi2HhAHxq2A== dependencies: core-js "^3.38.1" fflate "^0.4.8" @@ -10486,17 +10506,18 @@ redlock@^4.2.0: bluebird "^3.7.2" reflect.getprototypeof@^1.0.4, reflect.getprototypeof@^1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.7.tgz#04311b33a1b713ca5eb7b5aed9950a86481858e5" - integrity sha512-bMvFGIUKlc/eSfXNX+aZ+EL95/EgZzuwA0OBPTbZZDEJw/0AkentjMuM1oiRfwHrshqk4RzdgiTg5CcDalXN5g== + version "1.0.8" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.8.tgz#c58afb17a4007b4d1118c07b92c23fca422c5d82" + integrity sha512-B5dj6usc5dkk8uFliwjwDHM8To5/QwdKz9JcBZ8Ic4G1f0YmeeJTtE/ZTdgRFPAfxZFiUaPhZ1Jcs4qeagItGQ== dependencies: - call-bind "^1.0.7" + call-bind "^1.0.8" define-properties "^1.2.1" + dunder-proto "^1.0.0" es-abstract "^1.23.5" es-errors "^1.3.0" get-intrinsic "^1.2.4" - gopd "^1.0.1" - which-builtin-type "^1.1.4" + gopd "^1.2.0" + which-builtin-type "^1.2.0" regenerate-unicode-properties@^10.2.0: version "10.2.0" @@ -10678,30 +10699,31 @@ rollup@3.29.5: fsevents "~2.3.2" rollup@^4.0.2: - version "4.28.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.28.0.tgz#eb8d28ed43ef60a18f21d0734d230ee79dd0de77" - integrity sha512-G9GOrmgWHBma4YfCcX8PjH0qhXSdH8B4HDE2o4/jaxj93S4DPCIDoLcXz99eWMji4hB29UFCEd7B2gwGJDR9cQ== + version "4.28.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.28.1.tgz#7718ba34d62b449dfc49adbfd2f312b4fe0df4de" + integrity sha512-61fXYl/qNVinKmGSTHAZ6Yy8I3YIJC/r2m9feHo6SwVAVcLT5MPwOUFe7EuURA/4m0NR8lXG4BBXuo/IZEsjMg== dependencies: "@types/estree" "1.0.6" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.28.0" - "@rollup/rollup-android-arm64" "4.28.0" - "@rollup/rollup-darwin-arm64" "4.28.0" - "@rollup/rollup-darwin-x64" "4.28.0" - "@rollup/rollup-freebsd-arm64" "4.28.0" - "@rollup/rollup-freebsd-x64" "4.28.0" - "@rollup/rollup-linux-arm-gnueabihf" "4.28.0" - "@rollup/rollup-linux-arm-musleabihf" "4.28.0" - "@rollup/rollup-linux-arm64-gnu" "4.28.0" - "@rollup/rollup-linux-arm64-musl" "4.28.0" - "@rollup/rollup-linux-powerpc64le-gnu" "4.28.0" - "@rollup/rollup-linux-riscv64-gnu" "4.28.0" - "@rollup/rollup-linux-s390x-gnu" "4.28.0" - "@rollup/rollup-linux-x64-gnu" "4.28.0" - "@rollup/rollup-linux-x64-musl" "4.28.0" - "@rollup/rollup-win32-arm64-msvc" "4.28.0" - "@rollup/rollup-win32-ia32-msvc" "4.28.0" - "@rollup/rollup-win32-x64-msvc" "4.28.0" + "@rollup/rollup-android-arm-eabi" "4.28.1" + "@rollup/rollup-android-arm64" "4.28.1" + "@rollup/rollup-darwin-arm64" "4.28.1" + "@rollup/rollup-darwin-x64" "4.28.1" + "@rollup/rollup-freebsd-arm64" "4.28.1" + "@rollup/rollup-freebsd-x64" "4.28.1" + "@rollup/rollup-linux-arm-gnueabihf" "4.28.1" + "@rollup/rollup-linux-arm-musleabihf" "4.28.1" + "@rollup/rollup-linux-arm64-gnu" "4.28.1" + "@rollup/rollup-linux-arm64-musl" "4.28.1" + "@rollup/rollup-linux-loongarch64-gnu" "4.28.1" + "@rollup/rollup-linux-powerpc64le-gnu" "4.28.1" + "@rollup/rollup-linux-riscv64-gnu" "4.28.1" + "@rollup/rollup-linux-s390x-gnu" "4.28.1" + "@rollup/rollup-linux-x64-gnu" "4.28.1" + "@rollup/rollup-linux-x64-musl" "4.28.1" + "@rollup/rollup-win32-arm64-msvc" "4.28.1" + "@rollup/rollup-win32-ia32-msvc" "4.28.1" + "@rollup/rollup-win32-x64-msvc" "4.28.1" fsevents "~2.3.2" rope-sequence@^1.3.0: @@ -10903,7 +10925,7 @@ serve-static@1.16.2: parseurl "~1.3.3" send "0.19.0" -set-function-length@^1.2.1: +set-function-length@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== @@ -11052,9 +11074,9 @@ sonic-boom@^4.0.1: atomic-sleep "^1.0.0" sonner@^1.4.41: - version "1.7.0" - resolved "https://registry.yarnpkg.com/sonner/-/sonner-1.7.0.tgz#f59a2a70e049a179b6fbd1bb1bf2619d5ced07c0" - integrity sha512-W6dH7m5MujEPyug3lpI2l3TC3Pp1+LTgK0Efg+IHDrBbtEjyCmCHHo6yfNBOsf1tFZ6zf+jceWwB38baC8yO9g== + version "1.7.1" + resolved "https://registry.yarnpkg.com/sonner/-/sonner-1.7.1.tgz#737110a3e6211d8d766442076f852ddde1725205" + integrity sha512-b6LHBfH32SoVasRFECrdY8p8s7hXPDn3OHUFbZZbiB1ctLS9Gdh6rpX2dVrpQA0kiL5jcRzDDldwwLkSKk3+QQ== source-map-js@^1.0.2, source-map-js@^1.2.1: version "1.2.1" @@ -11119,11 +11141,11 @@ statuses@2.0.1: integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== storybook@^8.1.1: - version "8.4.6" - resolved "https://registry.yarnpkg.com/storybook/-/storybook-8.4.6.tgz#6a25899fc4a58657e3a66c0b0d9066f3c201f8cc" - integrity sha512-J6juZSZT2u3PUW0QZYZZYxBq6zU5O0OrkSgkMXGMg/QrS9to9IHmt4FjEMEyACRbXo8POcB/fSXa3VpGe7bv3g== + version "8.4.7" + resolved "https://registry.yarnpkg.com/storybook/-/storybook-8.4.7.tgz#a3068787a58074cec1b4197eed1c4427ec644b3f" + integrity sha512-RP/nMJxiWyFc8EVMH5gp20ID032Wvk+Yr3lmKidoegto5Iy+2dVQnUoElZb2zpbVXNHWakGuAkfI0dY1Hfp/vw== dependencies: - "@storybook/core" "8.4.6" + "@storybook/core" "8.4.7" streamsearch@^1.1.0: version "1.1.0" @@ -11526,9 +11548,11 @@ terser@^5.10.0, terser@^5.26.0: source-map-support "~0.5.20" text-decoder@^1.1.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.2.1.tgz#e173f5121d97bfa3ff8723429ad5ba92e1ead67e" - integrity sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ== + version "1.2.2" + resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.2.2.tgz#63dd2401c43895cecb292e2407db184b50ad60ac" + integrity sha512-/MDslo7ZyWTA2vnk1j7XoDVfXsGk3tp+zFEJHJGm0UjIlQifonVFwlVbQDFh8KJzTBnT8ie115TYqir6bclddA== + dependencies: + b4a "^1.6.4" text-table@^0.2.0: version "0.2.0" @@ -11603,17 +11627,17 @@ tiptap-markdown@^0.8.9: markdown-it-task-lists "^2.1.1" prosemirror-markdown "^1.11.1" -tldts-core@^6.1.65: - version "6.1.65" - resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.65.tgz#4b238e9658469f82a61787ee9135a3f083de68fa" - integrity sha512-Uq5t0N0Oj4nQSbU8wFN1YYENvMthvwU13MQrMJRspYCGLSAZjAfoBOJki5IQpnBM/WFskxxC/gIOTwaedmHaSg== +tldts-core@^6.1.66: + version "6.1.66" + resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.66.tgz#78f823137876f80bf8986028e8839473c2d114af" + integrity sha512-s07jJruSwndD2X8bVjwioPfqpIc1pDTzszPe9pL1Skbh4bjytL85KNQ3tolqLbCvpQHawIsGfFi9dgerWjqW4g== tldts@^6.1.32: - version "6.1.65" - resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.65.tgz#a3b8ad62292c7465d79addba3ff4bdc5fa92e4f5" - integrity sha512-xU9gLTfAGsADQ2PcWee6Hg8RFAv0DnjMGVJmDnUmI8a9+nYmapMQix4afwrdaCtT+AqP4MaxEzu7cCrYmBPbzQ== + version "6.1.66" + resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.66.tgz#34cb048acf51964c8f66139284a4c863d8150bb2" + integrity sha512-l3ciXsYFel/jSRfESbyKYud1nOw7WfhrBEF9I3UiarYk/qEaOOwu3qXNECHw4fHGHGTEOuhf/VdKgoDX5M/dhQ== dependencies: - tldts-core "^6.1.65" + tldts-core "^6.1.66" to-regex-range@^5.0.1: version "5.0.1" @@ -12135,9 +12159,9 @@ use-sidecar@^1.1.2: tslib "^2.0.0" use-sync-external-store@^1, use-sync-external-store@^1.2.0, use-sync-external-store@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9" - integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw== + version "1.4.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz#adbc795d8eeb47029963016cefdf89dc799fcebc" + integrity sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw== util-deprecate@^1.0.1, util-deprecate@^1.0.2: version "1.0.2" @@ -12325,9 +12349,9 @@ webpack-virtual-modules@^0.6.0, webpack-virtual-modules@^0.6.2: integrity sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ== webpack@5: - version "5.97.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.97.0.tgz#1c5e3b9319f8c6decb19b142e776d90e629d5c40" - integrity sha512-CWT8v7ShSfj7tGs4TLRtaOLmOCPWhoKEvp+eA7FVx8Xrjb3XfT0aXdxDItnRZmE8sHcH+a8ayDrJCOjXKxVFfQ== + version "5.97.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.97.1.tgz#972a8320a438b56ff0f1d94ade9e82eac155fa58" + integrity sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg== dependencies: "@types/eslint-scope" "^3.7.7" "@types/estree" "^1.0.6" @@ -12401,7 +12425,7 @@ which-boxed-primitive@^1.0.2: is-string "^1.1.0" is-symbol "^1.1.0" -which-builtin-type@^1.1.4: +which-builtin-type@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.2.0.tgz#58042ac9602d78a6d117c7e811349df1268ba63c" integrity sha512-I+qLGQ/vucCby4tf5HsLmGueEla4ZhwTBSqaooS+Y0BuxN4Cp+okmGuV+8mXZ84KDI9BA+oklo+RzKg0ONdSUA== From d04619477b7212a92cbab78cae84230ff3ee8a52 Mon Sep 17 00:00:00 2001 From: Vamsi Krishna <46787868+mathalav55@users.noreply.github.com> Date: Mon, 9 Dec 2024 18:06:56 +0530 Subject: [PATCH 08/34] [WEB-2382]chore: notifications code improvement (#6172) * chore: adjusted increment/decrement for unread count * chore: improved param handling for unread notification count function --- .../notification-card/options/snooze/modal.tsx | 1 - .../workspace-notifications.store.ts | 18 ++++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/web/core/components/workspace-notifications/sidebar/notification-card/options/snooze/modal.tsx b/web/core/components/workspace-notifications/sidebar/notification-card/options/snooze/modal.tsx index 36302aa5f0c..e7bed668816 100644 --- a/web/core/components/workspace-notifications/sidebar/notification-card/options/snooze/modal.tsx +++ b/web/core/components/workspace-notifications/sidebar/notification-card/options/snooze/modal.tsx @@ -5,7 +5,6 @@ import { useParams } from "next/navigation"; import { useForm, Controller } from "react-hook-form"; import { X } from "lucide-react"; import { Transition, Dialog } from "@headlessui/react"; -import { TNotification } from "@plane/types"; import { Button, CustomSelect } from "@plane/ui"; // components import { DateDropdown } from "@/components/dropdowns"; diff --git a/web/core/store/notifications/workspace-notifications.store.ts b/web/core/store/notifications/workspace-notifications.store.ts index b3ba8820353..a0de7bc0f60 100644 --- a/web/core/store/notifications/workspace-notifications.store.ts +++ b/web/core/store/notifications/workspace-notifications.store.ts @@ -50,7 +50,7 @@ export interface IWorkspaceNotificationStore { // actions setCurrentNotificationTab: (tab: TNotificationTab) => void; setCurrentSelectedNotificationId: (notificationId: string | undefined) => void; - setUnreadNotificationsCount: (type: "increment" | "decrement") => void; + setUnreadNotificationsCount: (type: "increment" | "decrement", newCount?: number) => void; getUnreadNotificationsCount: (workspaceSlug: string) => Promise; getNotifications: ( workspaceSlug: string, @@ -285,16 +285,22 @@ export class WorkspaceNotificationStore implements IWorkspaceNotificationStore { * @param { "increment" | "decrement" } type * @returns { void } */ - setUnreadNotificationsCount = (type: "increment" | "decrement"): void => { + setUnreadNotificationsCount = (type: "increment" | "decrement", newCount: number = 1): void => { + const validCount = Math.max(0, Math.abs(newCount)); + switch (this.currentNotificationTab) { case ENotificationTab.ALL: - update(this.unreadNotificationsCount, "total_unread_notifications_count", (count: 0) => - type === "increment" ? count + 1 : count - 1 + update( + this.unreadNotificationsCount, + "total_unread_notifications_count", + (count: number) => +Math.max(0, type === "increment" ? count + validCount : count - validCount) ); break; case ENotificationTab.MENTIONS: - update(this.unreadNotificationsCount, "mention_unread_notifications_count", (count: 0) => - type === "increment" ? count + 1 : count - 1 + update( + this.unreadNotificationsCount, + "mention_unread_notifications_count", + (count: number) => +Math.max(0, type === "increment" ? count + validCount : count - validCount) ); break; default: From ff8bbed6f93e49ce0a77d024b480a61e181fa41e Mon Sep 17 00:00:00 2001 From: Bavisetti Narayan <72156168+NarayanBavisetti@users.noreply.github.com> Date: Mon, 9 Dec 2024 20:29:30 +0530 Subject: [PATCH 09/34] chore: changed the soft deletion logic (#6171) --- apiserver/plane/app/views/issue/base.py | 2 - apiserver/plane/bgtasks/deletion_task.py | 117 ++++++++++++++++++----- apiserver/plane/db/models/__init__.py | 10 -- 3 files changed, 92 insertions(+), 37 deletions(-) diff --git a/apiserver/plane/app/views/issue/base.py b/apiserver/plane/app/views/issue/base.py index 7c15f91da9d..d0c614368fd 100644 --- a/apiserver/plane/app/views/issue/base.py +++ b/apiserver/plane/app/views/issue/base.py @@ -15,8 +15,6 @@ UUIDField, Value, Subquery, - Case, - When, ) from django.db.models.functions import Coalesce from django.utils import timezone diff --git a/apiserver/plane/bgtasks/deletion_task.py b/apiserver/plane/bgtasks/deletion_task.py index b350c2299f5..30ff7e8bd3d 100644 --- a/apiserver/plane/bgtasks/deletion_task.py +++ b/apiserver/plane/bgtasks/deletion_task.py @@ -3,7 +3,8 @@ from django.apps import apps from django.conf import settings from django.db import models -from django.core.exceptions import ObjectDoesNotExist +from django.db.models.fields.related import OneToOneRel + # Third party imports from celery import shared_task @@ -11,32 +12,98 @@ @shared_task def soft_delete_related_objects(app_label, model_name, instance_pk, using=None): + """ + Soft delete related objects for a given model instance + """ + # Get the model class using app registry model_class = apps.get_model(app_label, model_name) - instance = model_class.all_objects.get(pk=instance_pk) - related_fields = instance._meta.get_fields() - for field in related_fields: - if field.one_to_many or field.one_to_one: + + # Get the instance using all_objects to ensure we can get even if it's already soft deleted + try: + instance = model_class.all_objects.get(pk=instance_pk) + except model_class.DoesNotExist: + return + + # Get all related fields that are reverse relationships + all_related = [ + f + for f in instance._meta.get_fields() + if (f.one_to_many or f.one_to_one) and f.auto_created and not f.concrete + ] + + # Handle each related field + for relation in all_related: + related_name = relation.get_accessor_name() + + # Skip if the relation doesn't exist + if not hasattr(instance, related_name): + continue + + # Get the on_delete behavior name + on_delete_name = ( + relation.on_delete.__name__ + if hasattr(relation.on_delete, "__name__") + else "" + ) + + if on_delete_name == "DO_NOTHING": + continue + + elif on_delete_name == "SET_NULL": + # Handle SET_NULL relationships + if isinstance(relation, OneToOneRel): + # For OneToOne relationships + related_obj = getattr(instance, related_name, None) + if related_obj and isinstance(related_obj, models.Model): + setattr(related_obj, relation.remote_field.name, None) + related_obj.save(update_fields=[relation.remote_field.name]) + else: + # For other relationships + related_queryset = getattr(instance, related_name).all() + related_queryset.update(**{relation.remote_field.name: None}) + + else: + # Handle CASCADE and other delete behaviors try: - # Check if the field has CASCADE on delete - if ( - hasattr(field, "remote_field") - and hasattr(field.remote_field, "on_delete") - and field.remote_field.on_delete == models.CASCADE - ): - if field.one_to_many: - related_objects = getattr(instance, field.name).all() - elif field.one_to_one: - related_object = getattr(instance, field.name) - related_objects = ( - [related_object] if related_object is not None else [] - ) - - for obj in related_objects: - if obj: - obj.deleted_at = timezone.now() - obj.save(using=using) - except ObjectDoesNotExist: - pass + if relation.one_to_one: + # Handle OneToOne relationships + related_obj = getattr(instance, related_name, None) + if related_obj: + if hasattr(related_obj, "deleted_at"): + if not related_obj.deleted_at: + related_obj.deleted_at = timezone.now() + related_obj.save() + # Recursively handle related objects + soft_delete_related_objects( + related_obj._meta.app_label, + related_obj._meta.model_name, + related_obj.pk, + using, + ) + else: + # Handle other relationships + related_queryset = getattr(instance, related_name).all() + for related_obj in related_queryset: + if hasattr(related_obj, "deleted_at"): + if not related_obj.deleted_at: + related_obj.deleted_at = timezone.now() + related_obj.save() + # Recursively handle related objects + soft_delete_related_objects( + related_obj._meta.app_label, + related_obj._meta.model_name, + related_obj.pk, + using, + ) + except Exception as e: + # Log the error or handle as needed + print(f"Error handling relation {related_name}: {str(e)}") + continue + + # Finally, soft delete the instance itself if it hasn't been deleted yet + if hasattr(instance, "deleted_at") and not instance.deleted_at: + instance.deleted_at = timezone.now() + instance.save() # @shared_task diff --git a/apiserver/plane/db/models/__init__.py b/apiserver/plane/db/models/__init__.py index 36810956c27..e3a9df2542a 100644 --- a/apiserver/plane/db/models/__init__.py +++ b/apiserver/plane/db/models/__init__.py @@ -53,7 +53,6 @@ ProjectMemberInvite, ProjectPublicMember, ) -from .deploy_board import DeployBoard from .session import Session from .social_connection import SocialLoginConnection from .state import State @@ -69,23 +68,14 @@ WorkspaceUserProperties, ) -from .importer import Importer -from .page import Page, PageLog, PageLabel -from .estimate import Estimate, EstimatePoint -from .intake import Intake, IntakeIssue -from .analytic import AnalyticView -from .notification import Notification, UserNotificationPreference, EmailNotificationLog -from .exporter import ExporterHistory -from .webhook import Webhook, WebhookLog -from .dashboard import Dashboard, DashboardWidget, Widget from .favorite import UserFavorite From 205395e07914a030efca5ce6b848c18895f86ab7 Mon Sep 17 00:00:00 2001 From: Vihar Kurama Date: Tue, 10 Dec 2024 01:02:34 +0530 Subject: [PATCH 10/34] fix: changed checkboxes to toggles on notifications settings page (#6175) --- .../notification/email-notification-form.tsx | 95 +++++++++++-------- 1 file changed, 58 insertions(+), 37 deletions(-) diff --git a/web/core/components/profile/notification/email-notification-form.tsx b/web/core/components/profile/notification/email-notification-form.tsx index d3e9049e72a..02bbe19f1b9 100644 --- a/web/core/components/profile/notification/email-notification-form.tsx +++ b/web/core/components/profile/notification/email-notification-form.tsx @@ -4,7 +4,7 @@ import React, { FC, useEffect } from "react"; import { Controller, useForm } from "react-hook-form"; import { IUserEmailNotificationSettings } from "@plane/types"; // ui -import { Button, Checkbox, TOAST_TYPE, setToast } from "@plane/ui"; +import { ToggleSwitch, TOAST_TYPE, setToast } from "@plane/ui"; // services import { UserService } from "@/services/user.service"; // types @@ -20,35 +20,32 @@ export const EmailNotificationForm: FC = (props) => const { data } = props; // form data const { - handleSubmit, control, reset, - formState: { isSubmitting, dirtyFields }, } = useForm({ defaultValues: { ...data, }, }); - const onSubmit = async (formData: IUserEmailNotificationSettings) => { - // Get the dirty fields from the form data and create a payload - let payload = {}; - Object.keys(dirtyFields).forEach((key) => { - payload = { - ...payload, - [key]: formData[key as keyof IUserEmailNotificationSettings], - }; - }); - await userService - .updateCurrentUserEmailNotificationSettings(payload) - .then(() => - setToast({ - title: "Success!", - type: TOAST_TYPE.SUCCESS, - message: "Email Notification Settings updated successfully", - }) - ) - .catch((err) => console.error(err)); + const handleSettingChange = async (key: keyof IUserEmailNotificationSettings, value: boolean) => { + try { + await userService.updateCurrentUserEmailNotificationSettings({ + [key]: value, + }); + setToast({ + title: "Success!", + type: TOAST_TYPE.SUCCESS, + message: "Email notification setting updated successfully", + }); + } catch (err) { + console.error(err); + setToast({ + title: "Error!", + type: TOAST_TYPE.ERROR, + message: "Failed to update email notification setting", + }); + } }; useEffect(() => { @@ -64,7 +61,7 @@ export const EmailNotificationForm: FC = (props) =>
Property changes
- Notify me when issue’s properties like assignees, priority, estimates or anything else changes. + Notify me when issue's properties like assignees, priority, estimates or anything else changes.
@@ -72,7 +69,14 @@ export const EmailNotificationForm: FC = (props) => control={control} name="property_change" render={({ field: { value, onChange } }) => ( - onChange(!value)} containerClassName="mx-2" /> + { + onChange(newValue); + handleSettingChange("property_change", newValue); + }} + size="sm" + /> )} />
@@ -89,12 +93,13 @@ export const EmailNotificationForm: FC = (props) => control={control} name="state_change" render={({ field: { value, onChange } }) => ( - { - onChange(!value); + { + onChange(newValue); + handleSettingChange("state_change", newValue); }} - containerClassName="mx-2" + size="sm" /> )} /> @@ -110,7 +115,14 @@ export const EmailNotificationForm: FC = (props) => control={control} name="issue_completed" render={({ field: { value, onChange } }) => ( - onChange(!value)} containerClassName="mx-2" /> + { + onChange(newValue); + handleSettingChange("issue_completed", newValue); + }} + size="sm" + /> )} /> @@ -127,7 +139,14 @@ export const EmailNotificationForm: FC = (props) => control={control} name="comment" render={({ field: { value, onChange } }) => ( - onChange(!value)} containerClassName="mx-2" /> + { + onChange(newValue); + handleSettingChange("comment", newValue); + }} + size="sm" + /> )} /> @@ -144,17 +163,19 @@ export const EmailNotificationForm: FC = (props) => control={control} name="mention" render={({ field: { value, onChange } }) => ( - onChange(!value)} containerClassName="mx-2" /> + { + onChange(newValue); + handleSettingChange("mention", newValue); + }} + size="sm" + /> )} /> -
- -
); }; 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 11/34] 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 e080225aaf2..74e6534d67a 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 6518cff0066..e6b0730dbd4 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 12/34] 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 c81966de4f2..3ec8c6340ac 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 13/34] 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 ff9bf6a67dd..e2fe4cf331d 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 4b44b3898cb..6d350d83c9a 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 18124e39635..e098564e114 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 0700636092a..f14aa4ac793 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 ce02a4946ba..c1fe71a306a 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 19c361629b9..8f7295a0ede 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 8487dbb47db..66557c74ed3 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 ad5fe24e5c2..b45723305cf 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 5c31544b3d2..a7fac6401b9 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 9ce0fd077dc..6fc823e902c 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 24387736619..3237a184b49 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 600f0d72658..2cfcc3643c1 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 368c14e74ea..a0e0ecca141 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 2c05ef43f7e..941f8419fa5 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 93fdbb5922f..cd0a5ddcca5 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 14/34] 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 97a2b2d4105..b0fa447885a 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 15/34] [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 7d960015b9b..0e0e15af178 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 7d71948d838..e3d73036361 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 c8711b96a4c..18c4afa968e 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 2682c9114fb..8bc361a7c90 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 35c61263df0..fa17060d593 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 d4000aa9e7f..8c086a5a81a 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 dff2755fdcb..74c0bf38cd8 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 16/34] 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 e6a1a887e0c..c96a2572502 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 17/34] 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 | 94 ++------ web/core/store/module.store.ts | 20 +- 7 files changed, 170 insertions(+), 273 deletions(-) diff --git a/packages/types/src/issues.d.ts b/packages/types/src/issues.d.ts index eff81f857b2..9bbfa36b1f0 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 d1144995c8a..f1107c8fb7c 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 ba1dce5c1c4..e2d52e7ca59 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 fca8d68eb25..01a81cc61be 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 21f40a5bc14..9cb6fa8f5b1 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, + }, + ]; + } - if (!projectIds) return; + // 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 dd90cfe1d5e..4e56e56c11d 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,36 +327,27 @@ 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 - */ - getActiveCycleById = computedFn((cycleId: string): ICycle | null => - this.activeCycleIdMap?.[cycleId] && this.cycleMap?.[cycleId] ? this.cycleMap?.[cycleId] : null - ); - - /** - * @description returns list of cycle ids of the project id passed as argument + * @description returns list of cycle details of the project id passed as argument * @param projectId */ - getProjectCycleIds = computedFn((projectId: string): string[] | 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]); - const cycleIds = cycles.map((c) => c.id); - return cycleIds || null; + return cycles || null; }); /** - * @description validates cycle dates - * @param workspaceSlug + * @description returns list of cycle ids of the project id passed as argument * @param projectId - * @param payload - * @returns */ - validateDate = async (workspaceSlug: string, projectId: string, payload: CycleDateCheckData) => - await this.cycleService.cycleDateCheck(workspaceSlug, projectId, payload); + getProjectCycleIds = computedFn((projectId: string): string[] | null => { + const cycles = this.getProjectCycleDetails(projectId); + if (!cycles) return null; + const cycleIds = cycles.map((c) => c.id); + return cycleIds || null; + }); /** * @description gets the plot type for the cycle store @@ -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 787b8b091e1..06a08b92c1d 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 @@ -211,14 +212,23 @@ export class ModulesStore implements IModuleStore { getModuleNameById = computedFn((moduleId: string) => this.moduleMap?.[moduleId]?.name); /** - * @description returns list of module ids of the project id passed as argument + * @description returns list of module details of the project id passed as argument * @param projectId */ - getProjectModuleIds = computedFn((projectId: string) => { + 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) => { + 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 18/34] [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 6fc2c1b5249..8806b2d081e 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 2ae16528aed..49931802e84 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 8aea968ecaa..6773e3a64b7 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); - } - //handle remove from folder if dropped outside of the folder - if (parentId && parentId !== sourceData.parentId && sourceData.isChild) { - handleRemoveFromFavoritesFolder(sourceData.id); + handleMoveToFolder(sourceData.id, parentId); /**parent 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 3f4f636d345..a588f2af3bb 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 19/34] 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 148cf1f26cc..d8d0b59dd31 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 20/34] 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 00000000000..6e072ab8fb8 --- /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 10e51970062..9c66c629a78 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 00000000000..a4fac4b91ef --- /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 00000000000..84a7dddc643 --- /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 00000000000..324af8d4834 --- /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 00000000000..64d22493e1d --- /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 00000000000..fccfcbaa4dd --- /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 00000000000..47c9280cd41 --- /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 c38266c400c..4e59234cde9 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 e72e92e0b9a..570cb02fa78 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 7b399387b1a..cdf6a09ec61 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 4cede34347d..9b0ff52b572 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 33e4e7e92ae..621f5f8082f 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 00000000000..fabf334b9cd --- /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 00000000000..b191f9f2df2 --- /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 00000000000..7a451832159 --- /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 21/34] 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 00000000000..7a451832159 --- /dev/null +++ b/web/ee/helpers/command-palette.ts @@ -0,0 +1 @@ +export * from "ce/helpers/command-palette"; From 9ad8b43408986523af0a73462561f2d4ad1936d4 Mon Sep 17 00:00:00 2001 From: Bavisetti Narayan <72156168+NarayanBavisetti@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:11:12 +0530 Subject: [PATCH 22/34] chore: handled the cycle date time using project timezone (#6187) * chore: handled the cycle date time using project timezone * chore: reverted the frontend commit --- apiserver/plane/app/serializers/cycle.py | 12 +++ apiserver/plane/app/views/cycle/base.py | 55 +++++++++- apiserver/plane/app/views/issue/base.py | 2 +- apiserver/plane/app/views/issue/sub_issue.py | 2 +- apiserver/plane/app/views/module/archive.py | 2 +- apiserver/plane/app/views/module/base.py | 2 +- apiserver/plane/utils/timezone_converter.py | 100 ++++++++++++++++++ .../plane/utils/user_timezone_converter.py | 26 ----- web/core/components/project/form.tsx | 38 +++---- 9 files changed, 185 insertions(+), 54 deletions(-) create mode 100644 apiserver/plane/utils/timezone_converter.py delete mode 100644 apiserver/plane/utils/user_timezone_converter.py diff --git a/apiserver/plane/app/serializers/cycle.py b/apiserver/plane/app/serializers/cycle.py index bf08de4fef9..171494f03d5 100644 --- a/apiserver/plane/app/serializers/cycle.py +++ b/apiserver/plane/app/serializers/cycle.py @@ -5,6 +5,7 @@ from .base import BaseSerializer from .issue import IssueStateSerializer from plane.db.models import Cycle, CycleIssue, CycleUserProperties +from plane.utils.timezone_converter import convert_to_utc class CycleWriteSerializer(BaseSerializer): @@ -15,6 +16,17 @@ def validate(self, data): and data.get("start_date", None) > data.get("end_date", None) ): raise serializers.ValidationError("Start date cannot exceed end date") + if ( + data.get("start_date", None) is not None + and data.get("end_date", None) is not None + ): + project_id = self.initial_data.get("project_id") or self.instance.project_id + data["start_date"] = convert_to_utc( + str(data.get("start_date").date()), project_id + ) + data["end_date"] = convert_to_utc( + str(data.get("end_date", None).date()), project_id, is_end_date=True + ) return data class Meta: diff --git a/apiserver/plane/app/views/cycle/base.py b/apiserver/plane/app/views/cycle/base.py index 61ea9eed461..1addc5becd1 100644 --- a/apiserver/plane/app/views/cycle/base.py +++ b/apiserver/plane/app/views/cycle/base.py @@ -1,5 +1,7 @@ # Python imports import json +import pytz + # Django imports from django.contrib.postgres.aggregates import ArrayAgg @@ -52,6 +54,11 @@ # Module imports from .. import BaseAPIView, BaseViewSet from plane.bgtasks.webhook_task import model_activity +from plane.utils.timezone_converter import ( + convert_utc_to_project_timezone, + convert_to_utc, + user_timezone_converter, +) class CycleViewSet(BaseViewSet): @@ -67,6 +74,19 @@ def get_queryset(self): project_id=self.kwargs.get("project_id"), workspace__slug=self.kwargs.get("slug"), ) + + project = Project.objects.get(id=self.kwargs.get("project_id")) + + # Fetch project for the specific record or pass project_id dynamically + project_timezone = project.timezone + + # Convert the current time (timezone.now()) to the project's timezone + local_tz = pytz.timezone(project_timezone) + current_time_in_project_tz = timezone.now().astimezone(local_tz) + + # Convert project local time back to UTC for comparison (start_date is stored in UTC) + current_time_in_utc = current_time_in_project_tz.astimezone(pytz.utc) + return self.filter_queryset( super() .get_queryset() @@ -119,12 +139,15 @@ def get_queryset(self): .annotate( status=Case( When( - Q(start_date__lte=timezone.now()) - & Q(end_date__gte=timezone.now()), + Q(start_date__lte=current_time_in_utc) + & Q(end_date__gte=current_time_in_utc), then=Value("CURRENT"), ), - When(start_date__gt=timezone.now(), then=Value("UPCOMING")), - When(end_date__lt=timezone.now(), then=Value("COMPLETED")), + When( + start_date__gt=current_time_in_utc, + then=Value("UPCOMING"), + ), + When(end_date__lt=current_time_in_utc, then=Value("COMPLETED")), When( Q(start_date__isnull=True) & Q(end_date__isnull=True), then=Value("DRAFT"), @@ -160,10 +183,22 @@ def list(self, request, slug, project_id): # Update the order by queryset = queryset.order_by("-is_favorite", "-created_at") + project = Project.objects.get(id=self.kwargs.get("project_id")) + + # Fetch project for the specific record or pass project_id dynamically + project_timezone = project.timezone + + # Convert the current time (timezone.now()) to the project's timezone + local_tz = pytz.timezone(project_timezone) + current_time_in_project_tz = timezone.now().astimezone(local_tz) + + # Convert project local time back to UTC for comparison (start_date is stored in UTC) + current_time_in_utc = current_time_in_project_tz.astimezone(pytz.utc) + # Current Cycle if cycle_view == "current": queryset = queryset.filter( - start_date__lte=timezone.now(), end_date__gte=timezone.now() + start_date__lte=current_time_in_utc, end_date__gte=current_time_in_utc ) data = queryset.values( @@ -191,6 +226,8 @@ def list(self, request, slug, project_id): "version", "created_by", ) + datetime_fields = ["start_date", "end_date"] + data = user_timezone_converter(data, datetime_fields, project_timezone) if data: return Response(data, status=status.HTTP_200_OK) @@ -221,6 +258,8 @@ def list(self, request, slug, project_id): "version", "created_by", ) + datetime_fields = ["start_date", "end_date"] + data = user_timezone_converter(data, datetime_fields, project_timezone) return Response(data, status=status.HTTP_200_OK) @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) @@ -365,6 +404,7 @@ def partial_update(self, request, slug, project_id, pk): @allow_permission([ROLE.ADMIN, ROLE.MEMBER]) def retrieve(self, request, slug, project_id, pk): + project = Project.objects.get(id=project_id) queryset = self.get_queryset().filter(archived_at__isnull=True).filter(pk=pk) data = ( self.get_queryset() @@ -417,6 +457,8 @@ def retrieve(self, request, slug, project_id, pk): ) queryset = queryset.first() + datetime_fields = ["start_date", "end_date"] + data = user_timezone_converter(data, datetime_fields, project.timezone) recent_visited_task.delay( slug=slug, @@ -492,6 +534,9 @@ def post(self, request, slug, project_id): status=status.HTTP_400_BAD_REQUEST, ) + start_date = convert_to_utc(str(start_date), project_id) + end_date = convert_to_utc(str(end_date), project_id, is_end_date=True) + # Check if any cycle intersects in the given interval cycles = Cycle.objects.filter( Q(workspace__slug=slug) diff --git a/apiserver/plane/app/views/issue/base.py b/apiserver/plane/app/views/issue/base.py index d0c614368fd..e087a36597d 100644 --- a/apiserver/plane/app/views/issue/base.py +++ b/apiserver/plane/app/views/issue/base.py @@ -54,7 +54,7 @@ from plane.utils.order_queryset import order_issue_queryset from plane.utils.paginator import GroupedOffsetPaginator, SubGroupedOffsetPaginator from .. import BaseAPIView, BaseViewSet -from plane.utils.user_timezone_converter import user_timezone_converter +from plane.utils.timezone_converter import user_timezone_converter from plane.bgtasks.recent_visited_task import recent_visited_task from plane.utils.global_paginator import paginate from plane.bgtasks.webhook_task import model_activity diff --git a/apiserver/plane/app/views/issue/sub_issue.py b/apiserver/plane/app/views/issue/sub_issue.py index e461917fb38..19e2522d2c1 100644 --- a/apiserver/plane/app/views/issue/sub_issue.py +++ b/apiserver/plane/app/views/issue/sub_issue.py @@ -20,7 +20,7 @@ from plane.app.permissions import ProjectEntityPermission from plane.db.models import Issue, IssueLink, FileAsset, CycleIssue from plane.bgtasks.issue_activities_task import issue_activity -from plane.utils.user_timezone_converter import user_timezone_converter +from plane.utils.timezone_converter import user_timezone_converter from collections import defaultdict diff --git a/apiserver/plane/app/views/module/archive.py b/apiserver/plane/app/views/module/archive.py index 82c1d47eb4e..d5c632f966d 100644 --- a/apiserver/plane/app/views/module/archive.py +++ b/apiserver/plane/app/views/module/archive.py @@ -28,7 +28,7 @@ from plane.app.serializers import ModuleDetailSerializer from plane.db.models import Issue, Module, ModuleLink, UserFavorite, Project from plane.utils.analytics_plot import burndown_plot -from plane.utils.user_timezone_converter import user_timezone_converter +from plane.utils.timezone_converter import user_timezone_converter # Module imports diff --git a/apiserver/plane/app/views/module/base.py b/apiserver/plane/app/views/module/base.py index 8f9839b71f1..3e3a4c2db72 100644 --- a/apiserver/plane/app/views/module/base.py +++ b/apiserver/plane/app/views/module/base.py @@ -56,7 +56,7 @@ Project, ) from plane.utils.analytics_plot import burndown_plot -from plane.utils.user_timezone_converter import user_timezone_converter +from plane.utils.timezone_converter import user_timezone_converter from plane.bgtasks.webhook_task import model_activity from .. import BaseAPIView, BaseViewSet from plane.bgtasks.recent_visited_task import recent_visited_task diff --git a/apiserver/plane/utils/timezone_converter.py b/apiserver/plane/utils/timezone_converter.py new file mode 100644 index 00000000000..dc8e20b8c7c --- /dev/null +++ b/apiserver/plane/utils/timezone_converter.py @@ -0,0 +1,100 @@ +import pytz +from plane.db.models import Project +from datetime import datetime, time +from datetime import timedelta + +def user_timezone_converter(queryset, datetime_fields, user_timezone): + # Create a timezone object for the user's timezone + user_tz = pytz.timezone(user_timezone) + + # Check if queryset is a dictionary (single item) or a list of dictionaries + if isinstance(queryset, dict): + queryset_values = [queryset] + else: + queryset_values = list(queryset) + + # Iterate over the dictionaries in the list + for item in queryset_values: + # Iterate over the datetime fields + for field in datetime_fields: + # Convert the datetime field to the user's timezone + if field in item and item[field]: + item[field] = item[field].astimezone(user_tz) + + # If queryset was a single item, return a single item + if isinstance(queryset, dict): + return queryset_values[0] + else: + return queryset_values + + +def convert_to_utc(date, project_id, is_end_date=False): + """ + Converts a start date string to the project's local timezone at 12:00 AM + and then converts it to UTC for storage. + + Args: + date (str): The date string in "YYYY-MM-DD" format. + project_id (int): The project's ID to fetch the associated timezone. + + Returns: + datetime: The UTC datetime. + """ + # Retrieve the project's timezone using the project ID + project = Project.objects.get(id=project_id) + project_timezone = project.timezone + if not date or not project_timezone: + raise ValueError("Both date and timezone must be provided.") + + # Parse the string into a date object + start_date = datetime.strptime(date, "%Y-%m-%d").date() + + # Get the project's timezone + local_tz = pytz.timezone(project_timezone) + + # Combine the date with 12:00 AM time + local_datetime = datetime.combine(start_date, time.min) + + # Localize the datetime to the project's timezone + localized_datetime = local_tz.localize(local_datetime) + + # If it's an end date, subtract one minute + if is_end_date: + localized_datetime -= timedelta(minutes=1) + + # Convert the localized datetime to UTC + utc_datetime = localized_datetime.astimezone(pytz.utc) + + # Return the UTC datetime for storage + return utc_datetime + + +def convert_utc_to_project_timezone(utc_datetime, project_id): + """ + Converts a UTC datetime (stored in the database) to the project's local timezone. + + Args: + utc_datetime (datetime): The UTC datetime to be converted. + project_id (int): The project's ID to fetch the associated timezone. + + Returns: + datetime: The datetime in the project's local timezone. + """ + # Retrieve the project's timezone using the project ID + project = Project.objects.get(id=project_id) + project_timezone = project.timezone + if not project_timezone: + raise ValueError("Project timezone must be provided.") + + # Get the timezone object for the project's timezone + local_tz = pytz.timezone(project_timezone) + + # Convert the UTC datetime to the project's local timezone + if utc_datetime.tzinfo is None: + # Localize UTC datetime if it's naive (i.e., without timezone info) + utc_datetime = pytz.utc.localize(utc_datetime) + + # Convert to the project's local timezone + local_datetime = utc_datetime.astimezone(local_tz) + + return local_datetime diff --git a/apiserver/plane/utils/user_timezone_converter.py b/apiserver/plane/utils/user_timezone_converter.py deleted file mode 100644 index 550abfe997d..00000000000 --- a/apiserver/plane/utils/user_timezone_converter.py +++ /dev/null @@ -1,26 +0,0 @@ -import pytz - - -def user_timezone_converter(queryset, datetime_fields, user_timezone): - # Create a timezone object for the user's timezone - user_tz = pytz.timezone(user_timezone) - - # Check if queryset is a dictionary (single item) or a list of dictionaries - if isinstance(queryset, dict): - queryset_values = [queryset] - else: - queryset_values = list(queryset) - - # Iterate over the dictionaries in the list - for item in queryset_values: - # Iterate over the datetime fields - for field in datetime_fields: - # Convert the datetime field to the user's timezone - if field in item and item[field]: - item[field] = item[field].astimezone(user_tz) - - # If queryset was a single item, return a single item - if isinstance(queryset, dict): - return queryset_values[0] - else: - return queryset_values diff --git a/web/core/components/project/form.tsx b/web/core/components/project/form.tsx index 86da4a2f797..855c52aaf7f 100644 --- a/web/core/components/project/form.tsx +++ b/web/core/components/project/form.tsx @@ -16,7 +16,7 @@ import { CustomEmojiIconPicker, EmojiIconPickerTypes, Tooltip, - // CustomSearchSelect, + CustomSearchSelect, } from "@plane/ui"; // components import { Logo } from "@/components/common"; @@ -25,7 +25,7 @@ import { ImagePickerPopover } from "@/components/core"; import { PROJECT_UPDATED } from "@/constants/event-tracker"; import { NETWORK_CHOICES } from "@/constants/project"; // helpers -// import { TTimezone, TIME_ZONES } from "@/constants/timezones"; +import { TTimezone, TIME_ZONES } from "@/constants/timezones"; import { renderFormattedDate } from "@/helpers/date-time.helper"; import { convertHexEmojiToDecimal } from "@/helpers/emoji.helper"; import { getFileURL } from "@/helpers/file.helper"; @@ -68,20 +68,20 @@ export const ProjectDetailsForm: FC = (props) => { }); // derived values const currentNetwork = NETWORK_CHOICES.find((n) => n.key === project?.network); - // const getTimeZoneLabel = (timezone: TTimezone | undefined) => { - // if (!timezone) return undefined; - // return ( - //
- // {timezone.gmtOffset} - // {timezone.name} - //
- // ); - // }; - // const timeZoneOptions = TIME_ZONES.map((timeZone) => ({ - // value: timeZone.value, - // query: timeZone.name + " " + timeZone.gmtOffset + " " + timeZone.value, - // content: getTimeZoneLabel(timeZone), - // })); + const getTimeZoneLabel = (timezone: TTimezone | undefined) => { + if (!timezone) return undefined; + return ( +
+ {timezone.gmtOffset} + {timezone.name} +
+ ); + }; + const timeZoneOptions = TIME_ZONES.map((timeZone) => ({ + value: timeZone.value, + query: timeZone.name + " " + timeZone.gmtOffset + " " + timeZone.value, + content: getTimeZoneLabel(timeZone), + })); const coverImage = watch("cover_image_url"); useEffect(() => { @@ -146,7 +146,7 @@ export const ProjectDetailsForm: FC = (props) => { description: formData.description, logo_props: formData.logo_props, - // timezone: formData.timezone, + timezone: formData.timezone, }; // if unsplash or a pre-defined image is uploaded, delete the old uploaded asset if (formData.cover_image_url?.startsWith("http")) { @@ -386,7 +386,7 @@ export const ProjectDetailsForm: FC = (props) => { }} />
- {/*
+

Project Timezone

= (props) => { )} /> {errors.timezone && {errors.timezone.message}} -
*/} +
<> From 54f828cbfaf4175313edce13b28233ad2e209448 Mon Sep 17 00:00:00 2001 From: Prateek Shourya Date: Thu, 12 Dec 2024 21:40:57 +0530 Subject: [PATCH 23/34] refactor: enhance components modularity and introduce new UI componenets (#6192) * feat: add navigation dropdown component * chore: enhance title/ description loader and componenet modularity * chore: issue store filter update * chore: added few icons to ui package * chore: improvements for tabs componenet * chore: enhance sidebar modularity * chore: update issue and router store to add support for additional issue layouts * chore: enhanced cycle componenets modularity * feat: added project grouping header for cycles list * chore: enhanced project dropdown componenet by adding multiple selection functionality * chore: enhanced rich text editor modularity by taking members ids as props for mentions * chore: added functionality to filter disabled layouts in issue-layout dropdown * chore: added support to pass project ids as props in project card list * feat: multi select project modal * chore: seperate out project componenet for reusability * chore: command pallete store improvements * fix: build errors --- packages/constants/src/issue.ts | 1 + packages/types/src/common.d.ts | 2 + packages/types/src/enums.ts | 1 + packages/types/src/issues.d.ts | 3 +- packages/types/src/view-props.d.ts | 3 + packages/ui/src/breadcrumbs/index.ts | 1 + .../src/breadcrumbs/navigation-dropdown.tsx | 96 ++++++++++ packages/ui/src/icons/index.ts | 3 + packages/ui/src/icons/lead-icon.tsx | 26 +++ packages/ui/src/icons/teams.tsx | 19 ++ packages/ui/src/tabs/tabs.tsx | 32 +++- space/core/store/helpers/base-issues.store.ts | 2 +- .../cycles/(detail)/[cycleId]/page.tsx | 7 +- .../[workspaceSlug]/(projects)/sidebar.tsx | 17 +- .../components/cycles/active-cycle/root.tsx | 117 ++++++------ web/ce/components/issues/filters/index.ts | 1 + .../issues/filters/team-project.tsx | 12 ++ .../components/issues/issue-layouts/utils.tsx | 4 + .../workspace/sidebar/teams-sidebar-list.tsx | 1 + web/ce/constants/dashboard.ts | 58 +++++- web/ce/constants/issues.ts | 8 +- web/ce/helpers/dashboard.helper.ts | 5 +- web/ce/helpers/issue-action-helper.ts | 15 ++ web/ce/store/command-palette.store.ts | 20 +- web/ce/store/issue/team-views/filter.store.ts | 12 ++ web/ce/store/issue/team-views/index.ts | 2 + web/ce/store/issue/team-views/issue.store.ts | 13 ++ web/ce/store/issue/team/filter.store.ts | 12 ++ web/ce/store/issue/team/index.ts | 2 + web/ce/store/issue/team/issue.store.ts | 13 ++ web/ce/types/dashboard.ts | 2 + .../cycles/analytics-sidebar/root.tsx | 27 ++- .../components/cycles/cycle-peek-overview.tsx | 24 ++- web/core/components/cycles/form.tsx | 1 + .../cycles/list/cycle-list-item-action.tsx | 13 +- .../list/cycle-list-project-group-header.tsx | 44 +++++ .../cycles/list/cycles-list-item.tsx | 6 +- web/core/components/cycles/list/index.ts | 1 + web/core/components/dropdowns/layout.tsx | 12 +- web/core/components/dropdowns/project.tsx | 66 +++++-- .../rich-text-editor/rich-text-editor.tsx | 15 +- .../inbox/content/inbox-issue-header.tsx | 7 +- .../content/inbox-issue-mobile-header.tsx | 7 +- .../components/inbox/content/issue-root.tsx | 6 +- web/core/components/inbox/content/root.tsx | 3 +- .../modals/create-modal/issue-description.tsx | 8 +- .../components/issues/description-input.tsx | 13 +- .../issues/issue-detail/main-content.tsx | 7 +- .../components/issues/issue-layouts/utils.tsx | 6 +- .../components/description-editor.tsx | 8 +- .../issue-modal/components/project-select.tsx | 1 + .../components/issues/issue-update-status.tsx | 8 +- .../issues/peek-overview/header.tsx | 8 +- web/core/components/modules/form.tsx | 1 + web/core/components/project/card-list.tsx | 18 +- web/core/components/project/header.tsx | 80 +------- web/core/components/project/index.ts | 2 + .../components/project/multi-select-modal.tsx | 175 ++++++++++++++++++ .../components/project/search-projects.tsx | 78 ++++++++ .../workspace/sidebar/workspace-menu.tsx | 21 ++- web/core/constants/dashboard.ts | 48 ----- web/core/constants/issue.ts | 7 + web/core/hooks/store/use-issues.ts | 27 ++- web/core/hooks/use-group-dragndrop.ts | 4 +- web/core/hooks/use-issue-layout-store.ts | 6 +- web/core/hooks/use-issues-actions.tsx | 9 +- web/core/hooks/use-local-storage.tsx | 1 + web/core/store/base-command-palette.store.ts | 12 +- .../store/issue/helpers/base-issues.store.ts | 2 + .../helpers/issue-filter-helper.store.ts | 2 + web/core/store/issue/root.store.ts | 38 +++- web/core/store/pages/page.ts | 12 +- web/core/store/router.store.ts | 10 + web/ee/components/issues/filters/index.ts | 1 + .../issues/filters/team-project.tsx | 1 + .../components/issues/issue-layouts/utils.tsx | 1 + .../workspace/sidebar/teams-sidebar-list.tsx | 1 + web/ee/constants/dashboard.ts | 1 + web/ee/helpers/issue-action-helper.ts | 1 + web/ee/store/issue/team-views/index.ts | 1 + web/ee/store/issue/team/index.ts | 1 + web/helpers/issue.helper.ts | 2 +- 82 files changed, 1044 insertions(+), 320 deletions(-) create mode 100644 packages/ui/src/breadcrumbs/navigation-dropdown.tsx create mode 100644 packages/ui/src/icons/lead-icon.tsx create mode 100644 packages/ui/src/icons/teams.tsx create mode 100644 web/ce/components/issues/filters/team-project.tsx create mode 100644 web/ce/components/issues/issue-layouts/utils.tsx create mode 100644 web/ce/components/workspace/sidebar/teams-sidebar-list.tsx create mode 100644 web/ce/helpers/issue-action-helper.ts create mode 100644 web/ce/store/issue/team-views/filter.store.ts create mode 100644 web/ce/store/issue/team-views/index.ts create mode 100644 web/ce/store/issue/team-views/issue.store.ts create mode 100644 web/ce/store/issue/team/filter.store.ts create mode 100644 web/ce/store/issue/team/index.ts create mode 100644 web/ce/store/issue/team/issue.store.ts create mode 100644 web/core/components/cycles/list/cycle-list-project-group-header.tsx create mode 100644 web/core/components/project/multi-select-modal.tsx create mode 100644 web/core/components/project/search-projects.tsx create mode 100644 web/ee/components/issues/filters/team-project.tsx create mode 100644 web/ee/components/issues/issue-layouts/utils.tsx create mode 100644 web/ee/components/workspace/sidebar/teams-sidebar-list.tsx create mode 100644 web/ee/constants/dashboard.ts create mode 100644 web/ee/helpers/issue-action-helper.ts create mode 100644 web/ee/store/issue/team-views/index.ts create mode 100644 web/ee/store/issue/team/index.ts diff --git a/packages/constants/src/issue.ts b/packages/constants/src/issue.ts index 5db398c7634..9f6a1a2e2e9 100644 --- a/packages/constants/src/issue.ts +++ b/packages/constants/src/issue.ts @@ -11,6 +11,7 @@ export enum EIssueGroupByToServerOptions { "target_date" = "target_date", "project" = "project_id", "created_by" = "created_by", + "team_project" = "project_id", } export enum EIssueGroupBYServerToProperty { diff --git a/packages/types/src/common.d.ts b/packages/types/src/common.d.ts index 5fe31ad0006..7e755fcc28e 100644 --- a/packages/types/src/common.d.ts +++ b/packages/types/src/common.d.ts @@ -22,3 +22,5 @@ export type TLogoProps = { background_color?: string; }; }; + +export type TNameDescriptionLoader = "submitting" | "submitted" | "saved"; diff --git a/packages/types/src/enums.ts b/packages/types/src/enums.ts index df6a462b02e..e37e2f4a5d5 100644 --- a/packages/types/src/enums.ts +++ b/packages/types/src/enums.ts @@ -59,4 +59,5 @@ export enum EFileAssetType { USER_AVATAR = "USER_AVATAR", USER_COVER = "USER_COVER", WORKSPACE_LOGO = "WORKSPACE_LOGO", + TEAM_SPACE_DESCRIPTION = "TEAM_SPACE_DESCRIPTION", } diff --git a/packages/types/src/issues.d.ts b/packages/types/src/issues.d.ts index 9bbfa36b1f0..b6d32bdf816 100644 --- a/packages/types/src/issues.d.ts +++ b/packages/types/src/issues.d.ts @@ -211,7 +211,8 @@ export type GroupByColumnTypes = | "priority" | "labels" | "assignees" - | "created_by"; + | "created_by" + | "team_project"; export interface IGroupByColumn { id: string; diff --git a/packages/types/src/view-props.d.ts b/packages/types/src/view-props.d.ts index 57baa4cfdd1..aa1c75cdbdc 100644 --- a/packages/types/src/view-props.d.ts +++ b/packages/types/src/view-props.d.ts @@ -18,6 +18,7 @@ export type TIssueGroupByOptions = | "cycle" | "module" | "target_date" + | "team_project" | null; export type TIssueOrderByOptions = @@ -69,6 +70,7 @@ export type TIssueParams = | "start_date" | "target_date" | "project" + | "team_project" | "group_by" | "sub_group_by" | "order_by" @@ -92,6 +94,7 @@ export interface IIssueFilterOptions { cycle?: string[] | null; module?: string[] | null; project?: string[] | null; + team_project?: string[] | null; start_date?: string[] | null; state?: string[] | null; state_group?: string[] | null; diff --git a/packages/ui/src/breadcrumbs/index.ts b/packages/ui/src/breadcrumbs/index.ts index 669f5575772..05a8bdbf1b6 100644 --- a/packages/ui/src/breadcrumbs/index.ts +++ b/packages/ui/src/breadcrumbs/index.ts @@ -1 +1,2 @@ export * from "./breadcrumbs"; +export * from "./navigation-dropdown"; diff --git a/packages/ui/src/breadcrumbs/navigation-dropdown.tsx b/packages/ui/src/breadcrumbs/navigation-dropdown.tsx new file mode 100644 index 00000000000..a716ca65e19 --- /dev/null +++ b/packages/ui/src/breadcrumbs/navigation-dropdown.tsx @@ -0,0 +1,96 @@ +"use client"; + +import * as React from "react"; +import { CheckIcon, ChevronDownIcon } from "lucide-react"; +// ui +import { CustomMenu, TContextMenuItem } from "../dropdowns"; +// helpers +import { cn } from "../../helpers"; + +type TBreadcrumbNavigationDropdownProps = { + selectedItemKey: string; + navigationItems: TContextMenuItem[]; + navigationDisabled?: boolean; +}; + +export const BreadcrumbNavigationDropdown = (props: TBreadcrumbNavigationDropdownProps) => { + const { selectedItemKey, navigationItems, navigationDisabled = false } = props; + // derived values + const selectedItem = navigationItems.find((item) => item.key === selectedItemKey); + const selectedItemIcon = selectedItem?.icon ? ( + + ) : undefined; + + // if no selected item, return null + if (!selectedItem) return null; + + const NavigationButton = ({ className }: { className?: string }) => ( +
  • + {selectedItemIcon && ( +
    {selectedItemIcon}
    + )} +
    {selectedItem.title}
    +
  • + ); + + if (navigationDisabled) { + return ; + } + + return ( + + + +
    + } + placement="bottom-start" + closeOnSelect + > + {navigationItems.map((item) => { + if (item.shouldRender === false) return null; + return ( + { + e.preventDefault(); + e.stopPropagation(); + if (item.key === selectedItemKey) return; + item.action(); + }} + className={cn( + "flex items-center gap-2", + { + "text-custom-text-400": item.disabled, + }, + item.className + )} + disabled={item.disabled} + > + {item.icon && } +
    +
    {item.title}
    + {item.description && ( +

    + {item.description} +

    + )} +
    + {item.key === selectedItemKey && } +
    + ); + })} + + ); +}; diff --git a/packages/ui/src/icons/index.ts b/packages/ui/src/icons/index.ts index f8a2b1c849b..573efd99fb7 100644 --- a/packages/ui/src/icons/index.ts +++ b/packages/ui/src/icons/index.ts @@ -16,6 +16,7 @@ export * from "./epic-icon"; export * from "./full-screen-panel-icon"; export * from "./github-icon"; export * from "./gitlab-icon"; +export * from "./info-fill-icon"; export * from "./info-icon"; export * from "./layer-stack"; export * from "./layers-icon"; @@ -38,3 +39,5 @@ export * from "./done-icon"; export * from "./pending-icon"; export * from "./pi-chat"; export * from "./workspace-icon"; +export * from "./teams"; +export * from "./lead-icon"; diff --git a/packages/ui/src/icons/lead-icon.tsx b/packages/ui/src/icons/lead-icon.tsx new file mode 100644 index 00000000000..75575d35ec5 --- /dev/null +++ b/packages/ui/src/icons/lead-icon.tsx @@ -0,0 +1,26 @@ +import * as React from "react"; + +import { ISvgIcons } from "./type"; + +export const LeadIcon: React.FC = ({ className = "text-current", ...rest }) => ( + + + + + + + + + + + + +); diff --git a/packages/ui/src/icons/teams.tsx b/packages/ui/src/icons/teams.tsx new file mode 100644 index 00000000000..b730555989e --- /dev/null +++ b/packages/ui/src/icons/teams.tsx @@ -0,0 +1,19 @@ +import * as React from "react"; + +import { ISvgIcons } from "./type"; + +export const TeamsIcon: React.FC = ({ className = "text-current", ...rest }) => ( + + + + +); diff --git a/packages/ui/src/tabs/tabs.tsx b/packages/ui/src/tabs/tabs.tsx index a323d9721fd..92bc3ad7276 100644 --- a/packages/ui/src/tabs/tabs.tsx +++ b/packages/ui/src/tabs/tabs.tsx @@ -1,4 +1,4 @@ -import React, { FC, Fragment } from "react"; +import React, { FC, Fragment, useEffect, useState } from "react"; import { Tab } from "@headlessui/react"; import { LucideProps } from "lucide-react"; // helpers @@ -11,11 +11,12 @@ type TabItem = { label?: React.ReactNode; content: React.ReactNode; disabled?: boolean; + onClick?: () => void; }; type TTabsProps = { tabs: TabItem[]; - storageKey: string; + storageKey?: string; actions?: React.ReactNode; defaultTab?: string; containerClassName?: string; @@ -23,6 +24,8 @@ type TTabsProps = { tabListClassName?: string; tabClassName?: string; tabPanelClassName?: string; + size?: "sm" | "md" | "lg"; + storeInLocalStorage?: boolean; }; export const Tabs: FC = (props: TTabsProps) => { @@ -36,15 +39,28 @@ export const Tabs: FC = (props: TTabsProps) => { tabListClassName = "", tabClassName = "", tabPanelClassName = "", + size = "md", + storeInLocalStorage = true, } = props; // local storage - const { storedValue, setValue } = useLocalStorage(`tab-${storageKey}`, defaultTab); + const { storedValue, setValue } = useLocalStorage( + storeInLocalStorage && storageKey ? `tab-${storageKey}` : `tab-${tabs[0]?.key}`, + defaultTab + ); + // state + const [selectedTab, setSelectedTab] = useState(storedValue ?? defaultTab); + + useEffect(() => { + if (storeInLocalStorage) { + setValue(selectedTab); + } + }, [selectedTab, setValue, storeInLocalStorage, storageKey]); const currentTabIndex = (tabKey: string): number => tabs.findIndex((tab) => tab.key === tabKey); return (
    - +
    = (props: TTabsProps) => { : tab.disabled ? "text-custom-text-400 cursor-not-allowed" : "text-custom-text-400 hover:text-custom-text-300 hover:bg-custom-background-80/60", + { + "text-xs": size === "sm", + "text-sm": size === "md", + "text-base": size === "lg", + }, tabClassName ) } key={tab.key} onClick={() => { - if (!tab.disabled) setValue(tab.key); + if (!tab.disabled) setSelectedTab(tab.key); + tab.onClick?.(); }} disabled={tab.disabled} > diff --git a/space/core/store/helpers/base-issues.store.ts b/space/core/store/helpers/base-issues.store.ts index 004aa06c630..7abfa324a8d 100644 --- a/space/core/store/helpers/base-issues.store.ts +++ b/space/core/store/helpers/base-issues.store.ts @@ -26,7 +26,7 @@ import { CoreRootStore } from "../root.store"; // constants // helpers -export type TIssueDisplayFilterOptions = Exclude | "target_date"; +export type TIssueDisplayFilterOptions = Exclude | "target_date"; export enum EIssueGroupedAction { ADD = "ADD", diff --git a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(detail)/[cycleId]/page.tsx b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(detail)/[cycleId]/page.tsx index a1f7071a449..7a29f055306 100644 --- a/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(detail)/[cycleId]/page.tsx +++ b/web/app/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/cycles/(detail)/[cycleId]/page.tsx @@ -77,7 +77,12 @@ const CycleDetailPage = observer(() => { "0px 1px 4px 0px rgba(0, 0, 0, 0.06), 0px 2px 4px 0px rgba(16, 24, 40, 0.06), 0px 1px 8px -1px rgba(16, 24, 40, 0.06)", }} > - +
    )}
    diff --git a/web/app/[workspaceSlug]/(projects)/sidebar.tsx b/web/app/[workspaceSlug]/(projects)/sidebar.tsx index ac55fdec81e..11d23e35f05 100644 --- a/web/app/[workspaceSlug]/(projects)/sidebar.tsx +++ b/web/app/[workspaceSlug]/(projects)/sidebar.tsx @@ -21,6 +21,7 @@ import { useFavorite } from "@/hooks/store/use-favorite"; import useSize from "@/hooks/use-window-size"; // plane web components import { SidebarAppSwitcher } from "@/plane-web/components/sidebar"; +import { SidebarTeamsList } from "@/plane-web/components/workspace/sidebar/teams-sidebar-list"; import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions"; export const AppSidebar: FC = observer(() => { @@ -47,7 +48,7 @@ export const AppSidebar: FC = observer(() => { }); useEffect(() => { - if (windowSize[0] < 768) !sidebarCollapsed && toggleSidebar(); + if (windowSize[0] < 768 && !sidebarCollapsed) toggleSidebar(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [windowSize]); @@ -73,9 +74,12 @@ export const AppSidebar: FC = observer(() => { "px-4": !sidebarCollapsed, })} > + {/* Workspace switcher and settings */}
    - + {/* App switcher */} + {canPerformWorkspaceMemberActions && } + {/* Quick actions */}

    { "vertical-scrollbar px-4": !sidebarCollapsed, })} > + {/* User Menu */} - + {/* Workspace Menu */}
    + {/* Favorites Menu */} {canPerformWorkspaceMemberActions && !isFavoriteEmpty && } - + {/* Teams List */} + + {/* Projects List */}
    + {/* Help Section */} diff --git a/web/ce/components/cycles/active-cycle/root.tsx b/web/ce/components/cycles/active-cycle/root.tsx index a173cfda03a..5ebddc63f23 100644 --- a/web/ce/components/cycles/active-cycle/root.tsx +++ b/web/ce/components/cycles/active-cycle/root.tsx @@ -1,5 +1,6 @@ "use client"; +import { useMemo } from "react"; import { observer } from "mobx-react"; import { Disclosure } from "@headlessui/react"; // ui @@ -22,68 +23,80 @@ import { ActiveCycleIssueDetails } from "@/store/issue/cycle"; interface IActiveCycleDetails { workspaceSlug: string; projectId: string; + cycleId?: string; + showHeader?: boolean; } export const ActiveCycleRoot: React.FC = observer((props) => { - const { workspaceSlug, projectId } = props; - const { currentProjectActiveCycle, currentProjectActiveCycleId } = useCycle(); + const { workspaceSlug, projectId, cycleId: propsCycleId, showHeader = true } = props; + const { currentProjectActiveCycleId } = useCycle(); + // derived values + const cycleId = propsCycleId ?? currentProjectActiveCycleId; + // fetch cycle details const { handleFiltersUpdate, cycle: activeCycle, cycleIssueDetails, - } = useCyclesDetails({ workspaceSlug, projectId, cycleId: currentProjectActiveCycleId }); + } = useCyclesDetails({ workspaceSlug, projectId, cycleId }); + + const ActiveCyclesComponent = useMemo( + () => ( + <> + {!cycleId || !activeCycle ? ( + + ) : ( +
    + {cycleId && ( + + )} + +
    + + + +
    +
    +
    + )} + + ), + [cycleId, activeCycle, workspaceSlug, projectId, handleFiltersUpdate, cycleIssueDetails] + ); return ( <> - - {({ open }) => ( - <> - - - - - {!currentProjectActiveCycle ? ( - - ) : ( -
    - {currentProjectActiveCycleId && ( - - )} - -
    - - - -
    -
    -
    - )} -
    - - )} -
    + {showHeader ? ( + + {({ open }) => ( + <> + + + + {ActiveCyclesComponent} + + )} + + ) : ( + <>{ActiveCyclesComponent} + )} ); }); diff --git a/web/ce/components/issues/filters/index.ts b/web/ce/components/issues/filters/index.ts index 2cd80e3a7e5..f0f36b6c97e 100644 --- a/web/ce/components/issues/filters/index.ts +++ b/web/ce/components/issues/filters/index.ts @@ -1,2 +1,3 @@ export * from "./applied-filters"; export * from "./issue-types"; +export * from "./team-project"; diff --git a/web/ce/components/issues/filters/team-project.tsx b/web/ce/components/issues/filters/team-project.tsx new file mode 100644 index 00000000000..4f4787fef8b --- /dev/null +++ b/web/ce/components/issues/filters/team-project.tsx @@ -0,0 +1,12 @@ +"use client"; + +import React from "react"; +import { observer } from "mobx-react"; + +type Props = { + appliedFilters: string[] | null; + handleUpdate: (val: string) => void; + searchQuery: string; +}; + +export const FilterTeamProjects: React.FC = observer(() => null); diff --git a/web/ce/components/issues/issue-layouts/utils.tsx b/web/ce/components/issues/issue-layouts/utils.tsx new file mode 100644 index 00000000000..48dca43bd92 --- /dev/null +++ b/web/ce/components/issues/issue-layouts/utils.tsx @@ -0,0 +1,4 @@ +// types +import { IGroupByColumn } from "@plane/types"; + +export const getTeamProjectColumns = (): IGroupByColumn[] | undefined => undefined; diff --git a/web/ce/components/workspace/sidebar/teams-sidebar-list.tsx b/web/ce/components/workspace/sidebar/teams-sidebar-list.tsx new file mode 100644 index 00000000000..92cbdfc5f5f --- /dev/null +++ b/web/ce/components/workspace/sidebar/teams-sidebar-list.tsx @@ -0,0 +1 @@ +export const SidebarTeamsList = () => null; diff --git a/web/ce/constants/dashboard.ts b/web/ce/constants/dashboard.ts index 8872982fc5b..0df2719a772 100644 --- a/web/ce/constants/dashboard.ts +++ b/web/ce/constants/dashboard.ts @@ -1,17 +1,19 @@ "use client"; // icons -import { Home, Inbox, PenSquare } from "lucide-react"; +import { Briefcase, Home, Inbox, Layers, PenSquare, BarChart2 } from "lucide-react"; // ui -import { UserActivityIcon } from "@plane/ui"; +import { UserActivityIcon, ContrastIcon } from "@plane/ui"; import { Props } from "@/components/icons/types"; +// constants import { TLinkOptions } from "@/constants/dashboard"; +// plane web constants import { EUserPermissions } from "@/plane-web/constants/user-permissions"; // plane web types -import { TSidebarUserMenuItemKeys } from "@/plane-web/types/dashboard"; +import { TSidebarUserMenuItemKeys, TSidebarWorkspaceMenuItemKeys } from "@/plane-web/types/dashboard"; -export type TSidebarUserMenuItems = { - key: TSidebarUserMenuItemKeys; +export type TSidebarMenuItems = { + key: T; label: string; href: string; access: EUserPermissions[]; @@ -19,6 +21,8 @@ export type TSidebarUserMenuItems = { Icon: React.FC; }; +export type TSidebarUserMenuItems = TSidebarMenuItems; + export const SIDEBAR_USER_MENU_ITEMS: TSidebarUserMenuItems[] = [ { key: "home", @@ -54,3 +58,47 @@ export const SIDEBAR_USER_MENU_ITEMS: TSidebarUserMenuItems[] = [ Icon: PenSquare, }, ]; + +export type TSidebarWorkspaceMenuItems = TSidebarMenuItems; + +export const SIDEBAR_WORKSPACE_MENU: Partial> = { + projects: { + key: "projects", + label: "Projects", + href: `/projects`, + access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST], + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/projects/`, + Icon: Briefcase, + }, + "all-issues": { + key: "all-issues", + label: "Views", + href: `/workspace-views/all-issues`, + access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST], + highlight: (pathname: string, baseUrl: string) => pathname.includes(`${baseUrl}/workspace-views/`), + Icon: Layers, + }, + "active-cycles": { + key: "active-cycles", + label: "Cycles", + href: `/active-cycles`, + access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER], + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/active-cycles/`, + Icon: ContrastIcon, + }, + analytics: { + key: "analytics", + label: "Analytics", + href: `/analytics`, + access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER], + highlight: (pathname: string, baseUrl: string) => pathname.includes(`${baseUrl}/analytics/`), + Icon: BarChart2, + }, +}; + +export const SIDEBAR_WORKSPACE_MENU_ITEMS: TSidebarWorkspaceMenuItems[] = [ + SIDEBAR_WORKSPACE_MENU?.projects, + SIDEBAR_WORKSPACE_MENU?.["all-issues"], + SIDEBAR_WORKSPACE_MENU?.["active-cycles"], + SIDEBAR_WORKSPACE_MENU?.analytics, +].filter((item): item is TSidebarWorkspaceMenuItems => item !== undefined); diff --git a/web/ce/constants/issues.ts b/web/ce/constants/issues.ts index dc6ffbcb8c2..99b8ef90de1 100644 --- a/web/ce/constants/issues.ts +++ b/web/ce/constants/issues.ts @@ -1,4 +1,6 @@ import { TIssueActivityComment } from "@plane/types"; +// constants +import { ILayoutDisplayFiltersOptions } from "@/constants/issue"; export enum EActivityFilterType { ACTIVITY = "ACTIVITY", @@ -19,7 +21,7 @@ export const ACTIVITY_FILTER_TYPE_OPTIONS: Record void; @@ -32,3 +34,7 @@ export const filterActivityOnSelectedFilters = ( activity.filter((activity) => filter.includes(activity.activity_type as TActivityFilters)); export const ENABLE_ISSUE_DEPENDENCIES = false; + +export const ADDITIONAL_ISSUE_DISPLAY_FILTERS_BY_LAYOUT: { + [pageType: string]: { [layoutType: string]: ILayoutDisplayFiltersOptions }; +} = {}; diff --git a/web/ce/helpers/dashboard.helper.ts b/web/ce/helpers/dashboard.helper.ts index b2fba63adb0..c96c818a1f0 100644 --- a/web/ce/helpers/dashboard.helper.ts +++ b/web/ce/helpers/dashboard.helper.ts @@ -1,5 +1,8 @@ // plane web types -import { TSidebarUserMenuItemKeys } from "@/plane-web/types/dashboard"; +import { TSidebarUserMenuItemKeys, TSidebarWorkspaceMenuItemKeys } from "@/plane-web/types/dashboard"; // eslint-disable-next-line @typescript-eslint/no-unused-vars export const isUserFeatureEnabled = (featureKey: TSidebarUserMenuItemKeys) => true; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export const isWorkspaceFeatureEnabled = (featureKey: TSidebarWorkspaceMenuItemKeys, workspaceSlug: string) => true; diff --git a/web/ce/helpers/issue-action-helper.ts b/web/ce/helpers/issue-action-helper.ts new file mode 100644 index 00000000000..b1644e2aa26 --- /dev/null +++ b/web/ce/helpers/issue-action-helper.ts @@ -0,0 +1,15 @@ +import { IssueActions } from "@/hooks/use-issues-actions"; + +export const useTeamIssueActions: () => IssueActions = () => ({ + fetchIssues: () => Promise.resolve(undefined), + fetchNextIssues: () => Promise.resolve(undefined), + removeIssue: () => Promise.resolve(undefined), + updateFilters: () => Promise.resolve(undefined), +}); + +export const useTeamViewIssueActions: () => IssueActions = () => ({ + fetchIssues: () => Promise.resolve(undefined), + fetchNextIssues: () => Promise.resolve(undefined), + removeIssue: () => Promise.resolve(undefined), + updateFilters: () => Promise.resolve(undefined), +}); diff --git a/web/ce/store/command-palette.store.ts b/web/ce/store/command-palette.store.ts index 47c9280cd41..1b6fabf1875 100644 --- a/web/ce/store/command-palette.store.ts +++ b/web/ce/store/command-palette.store.ts @@ -1,12 +1,26 @@ -import { makeObservable } from "mobx"; +import { computed, makeObservable } from "mobx"; // types / constants import { BaseCommandPaletteStore, IBaseCommandPaletteStore } from "@/store/base-command-palette.store"; -export type ICommandPaletteStore = IBaseCommandPaletteStore; +export interface ICommandPaletteStore extends IBaseCommandPaletteStore { + // computed + isAnyModalOpen: boolean; +} export class CommandPaletteStore extends BaseCommandPaletteStore implements ICommandPaletteStore { constructor() { super(); - makeObservable(this, {}); + makeObservable(this, { + // computed + isAnyModalOpen: computed, + }); + } + + /** + * Checks whether any modal is open or not in the base command palette. + * @returns boolean + */ + get isAnyModalOpen(): boolean { + return Boolean(super.getCoreModalsState()); } } diff --git a/web/ce/store/issue/team-views/filter.store.ts b/web/ce/store/issue/team-views/filter.store.ts new file mode 100644 index 00000000000..9c33f94051c --- /dev/null +++ b/web/ce/store/issue/team-views/filter.store.ts @@ -0,0 +1,12 @@ +import { IProjectViewIssuesFilter, ProjectViewIssuesFilter } from "@/store/issue/project-views"; +import { IIssueRootStore } from "@/store/issue/root.store"; + +// @ts-nocheck - This class will never be used, extending similar class to avoid type errors +export type ITeamViewIssuesFilter = IProjectViewIssuesFilter; + +// @ts-nocheck - This class will never be used, extending similar class to avoid type errors +export class TeamViewIssuesFilter extends ProjectViewIssuesFilter implements IProjectViewIssuesFilter { + constructor(_rootStore: IIssueRootStore) { + super(_rootStore); + } +} diff --git a/web/ce/store/issue/team-views/index.ts b/web/ce/store/issue/team-views/index.ts new file mode 100644 index 00000000000..0fe6c946b0c --- /dev/null +++ b/web/ce/store/issue/team-views/index.ts @@ -0,0 +1,2 @@ +export * from "./filter.store"; +export * from "./issue.store"; diff --git a/web/ce/store/issue/team-views/issue.store.ts b/web/ce/store/issue/team-views/issue.store.ts new file mode 100644 index 00000000000..328370f853d --- /dev/null +++ b/web/ce/store/issue/team-views/issue.store.ts @@ -0,0 +1,13 @@ +import { IProjectViewIssues, ProjectViewIssues } from "@/store/issue/project-views"; +import { IIssueRootStore } from "@/store/issue/root.store"; +import { ITeamViewIssuesFilter } from "./filter.store"; + +// @ts-nocheck - This class will never be used, extending similar class to avoid type errors +export type ITeamViewIssues = IProjectViewIssues; + +// @ts-nocheck - This class will never be used, extending similar class to avoid type errors +export class TeamViewIssues extends ProjectViewIssues implements IProjectViewIssues { + constructor(_rootStore: IIssueRootStore, teamViewFilterStore: ITeamViewIssuesFilter) { + super(_rootStore, teamViewFilterStore); + } +} diff --git a/web/ce/store/issue/team/filter.store.ts b/web/ce/store/issue/team/filter.store.ts new file mode 100644 index 00000000000..42b2d5dd248 --- /dev/null +++ b/web/ce/store/issue/team/filter.store.ts @@ -0,0 +1,12 @@ +import { IProjectIssuesFilter, ProjectIssuesFilter } from "@/store/issue/project"; +import { IIssueRootStore } from "@/store/issue/root.store"; + +// @ts-nocheck - This class will never be used, extending similar class to avoid type errors +export type ITeamIssuesFilter = IProjectIssuesFilter; + +// @ts-nocheck - This class will never be used, extending similar class to avoid type errors +export class TeamIssuesFilter extends ProjectIssuesFilter implements IProjectIssuesFilter { + constructor(_rootStore: IIssueRootStore) { + super(_rootStore); + } +} diff --git a/web/ce/store/issue/team/index.ts b/web/ce/store/issue/team/index.ts new file mode 100644 index 00000000000..0fe6c946b0c --- /dev/null +++ b/web/ce/store/issue/team/index.ts @@ -0,0 +1,2 @@ +export * from "./filter.store"; +export * from "./issue.store"; diff --git a/web/ce/store/issue/team/issue.store.ts b/web/ce/store/issue/team/issue.store.ts new file mode 100644 index 00000000000..2e397943640 --- /dev/null +++ b/web/ce/store/issue/team/issue.store.ts @@ -0,0 +1,13 @@ +import { IProjectIssues, ProjectIssues } from "@/store/issue/project"; +import { IIssueRootStore } from "@/store/issue/root.store"; +import { ITeamIssuesFilter } from "./filter.store"; + +// @ts-nocheck - This class will never be used, extending similar class to avoid type errors +export type ITeamIssues = IProjectIssues; + +// @ts-nocheck - This class will never be used, extending similar class to avoid type errors +export class TeamIssues extends ProjectIssues implements IProjectIssues { + constructor(_rootStore: IIssueRootStore, teamIssueFilterStore: ITeamIssuesFilter) { + super(_rootStore, teamIssueFilterStore); + } +} diff --git a/web/ce/types/dashboard.ts b/web/ce/types/dashboard.ts index d615ac4afce..de35f60c6a7 100644 --- a/web/ce/types/dashboard.ts +++ b/web/ce/types/dashboard.ts @@ -1 +1,3 @@ export type TSidebarUserMenuItemKeys = "home" | "your-work" | "notifications" | "drafts"; + +export type TSidebarWorkspaceMenuItemKeys = "projects" | "all-issues" | "active-cycles" | "analytics"; diff --git a/web/core/components/cycles/analytics-sidebar/root.tsx b/web/core/components/cycles/analytics-sidebar/root.tsx index fd8c984a690..b709c0e6317 100644 --- a/web/core/components/cycles/analytics-sidebar/root.tsx +++ b/web/core/components/cycles/analytics-sidebar/root.tsx @@ -2,7 +2,6 @@ import React from "react"; import { observer } from "mobx-react"; -import { useParams } from "next/navigation"; // ui import { Loader } from "@plane/ui"; // components @@ -13,19 +12,19 @@ import useCyclesDetails from "../active-cycle/use-cycles-details"; type Props = { handleClose: () => void; isArchived?: boolean; - cycleId?: string; + cycleId: string; + projectId: string; + workspaceSlug: string; }; export const CycleDetailsSidebar: React.FC = observer((props) => { - const { handleClose, isArchived } = props; - // router - const { workspaceSlug, projectId, cycleId } = useParams(); + const { handleClose, isArchived, projectId, workspaceSlug, cycleId } = props; // store hooks const { cycle: cycleDetails } = useCyclesDetails({ - workspaceSlug: workspaceSlug.toString(), - projectId: projectId.toString(), - cycleId: cycleId?.toString() || props.cycleId, + workspaceSlug, + projectId, + cycleId, }); if (!cycleDetails) @@ -47,21 +46,17 @@ export const CycleDetailsSidebar: React.FC = observer((props) => {
    - +
    {workspaceSlug && projectId && cycleDetails?.id && ( - + )}
    ); diff --git a/web/core/components/cycles/cycle-peek-overview.tsx b/web/core/components/cycles/cycle-peek-overview.tsx index 759569cfa9f..187425b8d72 100644 --- a/web/core/components/cycles/cycle-peek-overview.tsx +++ b/web/core/components/cycles/cycle-peek-overview.tsx @@ -9,12 +9,13 @@ import { useAppRouter } from "@/hooks/use-app-router"; import { CycleDetailsSidebar } from "./"; type Props = { - projectId: string; + projectId?: string; workspaceSlug: string; isArchived?: boolean; }; -export const CyclePeekOverview: React.FC = observer(({ projectId, workspaceSlug, isArchived = false }) => { +export const CyclePeekOverview: React.FC = observer((props) => { + const { projectId: propsProjectId, workspaceSlug, isArchived } = props; // router const router = useAppRouter(); const pathname = usePathname(); @@ -23,22 +24,25 @@ export const CyclePeekOverview: React.FC = observer(({ projectId, workspa // refs const ref = React.useRef(null); // store hooks - const { fetchCycleDetails, fetchArchivedCycleDetails } = useCycle(); + const { getCycleById, fetchCycleDetails, fetchArchivedCycleDetails } = useCycle(); + // derived values + const cycleDetails = peekCycle ? getCycleById(peekCycle.toString()) : undefined; + const projectId = propsProjectId || cycleDetails?.project_id; const handleClose = () => { const query = generateQueryParams(searchParams, ["peekCycle"]); - router.push(`${pathname}?${query}`); + router.push(`${pathname}?${query}`, {}, { showProgressBar: false }); }; useEffect(() => { - if (!peekCycle) return; + if (!peekCycle || !projectId) return; if (isArchived) fetchArchivedCycleDetails(workspaceSlug, projectId, peekCycle.toString()); else fetchCycleDetails(workspaceSlug, projectId, peekCycle.toString()); }, [fetchArchivedCycleDetails, fetchCycleDetails, isArchived, peekCycle, projectId, workspaceSlug]); return ( <> - {peekCycle && ( + {peekCycle && projectId && (
    = observer(({ projectId, workspa "0px 1px 4px 0px rgba(0, 0, 0, 0.06), 0px 2px 4px 0px rgba(16, 24, 40, 0.06), 0px 1px 8px -1px rgba(16, 24, 40, 0.06)", }} > - +
    )} diff --git a/web/core/components/cycles/form.tsx b/web/core/components/cycles/form.tsx index 660d33cdb80..7651c5d4433 100644 --- a/web/core/components/cycles/form.tsx +++ b/web/core/components/cycles/form.tsx @@ -75,6 +75,7 @@ export const CycleForm: React.FC = (props) => { onChange(val); setActiveProject(val); }} + multiple={false} buttonVariant="border-with-text" renderCondition={(project) => shouldRenderProject(project)} tabIndex={getIndex("cover_image")} diff --git a/web/core/components/cycles/list/cycle-list-item-action.tsx b/web/core/components/cycles/list/cycle-list-item-action.tsx index 989e0436e36..73dca345d7c 100644 --- a/web/core/components/cycles/list/cycle-list-item-action.tsx +++ b/web/core/components/cycles/list/cycle-list-item-action.tsx @@ -14,10 +14,9 @@ import { CycleQuickActions } from "@/components/cycles"; import { DateRangeDropdown } from "@/components/dropdowns"; import { ButtonAvatars } from "@/components/dropdowns/member/avatar"; // constants -import { CYCLE_STATUS } from "@/constants/cycle"; import { CYCLE_FAVORITED, CYCLE_UNFAVORITED } from "@/constants/event-tracker"; // helpers -import { findHowManyDaysLeft, getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper"; +import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper"; import { getFileURL } from "@/helpers/file.helper"; // hooks import { generateQueryParams } from "@/helpers/router.helper"; @@ -69,11 +68,11 @@ export const CycleListItemAction: FC = observer((props) => { const cycleStatus = cycleDetails.status ? (cycleDetails.status.toLocaleLowerCase() as TCycleGroups) : "draft"; const isEditingAllowed = allowPermissions( [EUserPermissions.ADMIN, EUserPermissions.MEMBER], - EUserPermissionsLevel.PROJECT + EUserPermissionsLevel.PROJECT, + workspaceSlug, + projectId ); const renderIcon = Boolean(cycleDetails.start_date) || Boolean(cycleDetails.end_date); - const currentCycle = CYCLE_STATUS.find((status) => status.value === cycleStatus); - const daysLeft = findHowManyDaysLeft(cycleDetails.end_date) ?? 0; // handlers const handleAddToFavorites = (e: MouseEvent) => { @@ -201,9 +200,9 @@ export const CycleListItemAction: FC = observer((props) => { const query = generateQueryParams(searchParams, ["peekCycle"]); if (searchParams.has("peekCycle") && searchParams.get("peekCycle") === cycleId) { - router.push(`${pathname}?${query}`); + router.push(`${pathname}?${query}`, {}, { showProgressBar: false }); } else { - router.push(`${pathname}?${query && `${query}&`}peekCycle=${cycleId}`); + router.push(`${pathname}?${query && `${query}&`}peekCycle=${cycleId}`, {}, { showProgressBar: false }); } }; diff --git a/web/core/components/cycles/list/cycle-list-project-group-header.tsx b/web/core/components/cycles/list/cycle-list-project-group-header.tsx new file mode 100644 index 00000000000..d663eca0d86 --- /dev/null +++ b/web/core/components/cycles/list/cycle-list-project-group-header.tsx @@ -0,0 +1,44 @@ +"use client"; + +import React, { FC } from "react"; +import { observer } from "mobx-react"; +import { ChevronRight } from "lucide-react"; +// icons +import { Row, Logo } from "@plane/ui"; +// helpers +import { cn } from "@/helpers/common.helper"; +import { useProject } from "@/hooks/store/use-project"; + +type Props = { + projectId: string; + count?: number; + showCount?: boolean; + isExpanded?: boolean; +}; + +export const CycleListProjectGroupHeader: FC = observer((props) => { + const { projectId, count, showCount = false, isExpanded = false } = props; + // store hooks + const { getProjectById } = useProject(); + // derived values + const project = getProjectById(projectId); + + if (!project) return null; + return ( + + +
    + +
    +
    +
    {project.name}
    + {showCount &&
    {`${count ?? "0"}`}
    } +
    +
    + ); +}); diff --git a/web/core/components/cycles/list/cycles-list-item.tsx b/web/core/components/cycles/list/cycles-list-item.tsx index 8d531216a4b..5954a0a7f33 100644 --- a/web/core/components/cycles/list/cycles-list-item.tsx +++ b/web/core/components/cycles/list/cycles-list-item.tsx @@ -4,7 +4,7 @@ import { FC, MouseEvent, useRef } from "react"; import { observer } from "mobx-react"; import { usePathname, useSearchParams } from "next/navigation"; // icons -import { Check, Info } from "lucide-react"; +import { Check } from "lucide-react"; // types import type { TCycleGroups } from "@plane/types"; // ui @@ -72,9 +72,9 @@ export const CyclesListItem: FC = observer((props) => { const query = generateQueryParams(searchParams, ["peekCycle"]); if (searchParams.has("peekCycle") && searchParams.get("peekCycle") === cycleId) { - router.push(`${pathname}?${query}`); + router.push(`${pathname}?${query}`, {}, { showProgressBar: false }); } else { - router.push(`${pathname}?${query && `${query}&`}peekCycle=${cycleId}`); + router.push(`${pathname}?${query && `${query}&`}peekCycle=${cycleId}`, {}, { showProgressBar: false }); } }; diff --git a/web/core/components/cycles/list/index.ts b/web/core/components/cycles/list/index.ts index 4eebc577943..25419a0560a 100644 --- a/web/core/components/cycles/list/index.ts +++ b/web/core/components/cycles/list/index.ts @@ -3,3 +3,4 @@ export * from "./cycles-list-map"; export * from "./root"; export * from "./cycle-list-item-action"; export * from "./cycle-list-group-header"; +export * from "./cycle-list-project-group-header"; diff --git a/web/core/components/dropdowns/layout.tsx b/web/core/components/dropdowns/layout.tsx index 2557e57a2a2..7864d1849df 100644 --- a/web/core/components/dropdowns/layout.tsx +++ b/web/core/components/dropdowns/layout.tsx @@ -10,18 +10,24 @@ import { EIssueLayoutTypes, ISSUE_LAYOUT_MAP } from "@/constants/issue"; type TLayoutDropDown = { onChange: (value: EIssueLayoutTypes) => void; value: EIssueLayoutTypes; + disabledLayouts?: EIssueLayoutTypes[]; }; export const LayoutDropDown = observer((props: TLayoutDropDown) => { - const { onChange, value = EIssueLayoutTypes.LIST } = props; + const { onChange, value = EIssueLayoutTypes.LIST, disabledLayouts = [] } = props; + // derived values + const availableLayouts = useMemo( + () => Object.values(ISSUE_LAYOUT_MAP).filter((layout) => !disabledLayouts.includes(layout.key)), + [disabledLayouts] + ); const options = useMemo( () => - Object.values(ISSUE_LAYOUT_MAP).map((issueLayout) => ({ + availableLayouts.map((issueLayout) => ({ data: issueLayout.key, value: issueLayout.key, })), - [] + [availableLayouts] ); const buttonContent = useCallback((isOpen: boolean, buttonValue: string | string[] | undefined) => { diff --git a/web/core/components/dropdowns/project.tsx b/web/core/components/dropdowns/project.tsx index 3f973cd1618..f94014eb8b8 100644 --- a/web/core/components/dropdowns/project.tsx +++ b/web/core/components/dropdowns/project.tsx @@ -1,4 +1,4 @@ -import { Fragment, ReactNode, useRef, useState } from "react"; +import { ReactNode, useRef, useState } from "react"; import { observer } from "mobx-react"; import { usePopper } from "react-popper"; import { Check, ChevronDown, Search } from "lucide-react"; @@ -25,12 +25,21 @@ type Props = TDropdownProps & { button?: ReactNode; dropdownArrow?: boolean; dropdownArrowClassName?: string; - onChange: (val: string) => void; onClose?: () => void; renderCondition?: (project: TProject) => boolean; - value: string | null; renderByDefault?: boolean; -}; +} & ( + | { + multiple: false; + onChange: (val: string) => void; + value: string | null; + } + | { + multiple: true; + onChange: (val: string[]) => void; + value: string[]; + } + ); export const ProjectDropdown: React.FC = observer((props) => { const { @@ -43,6 +52,7 @@ export const ProjectDropdown: React.FC = observer((props) => { dropdownArrow = false, dropdownArrowClassName = "", hideIcon = false, + multiple, onChange, onClose, placeholder = "Project", @@ -99,8 +109,6 @@ export const ProjectDropdown: React.FC = observer((props) => { const filteredOptions = query === "" ? options : options?.filter((o) => o?.query.toLowerCase().includes(query.toLowerCase())); - const selectedProject = value ? getProjectById(value) : null; - const { handleClose, handleKeyDown, handleOnClick, searchInputKeyDown } = useDropdown({ dropdownRef, inputRef, @@ -111,9 +119,40 @@ export const ProjectDropdown: React.FC = observer((props) => { setQuery, }); - const dropdownOnChange = (val: string) => { + const dropdownOnChange = (val: string & string[]) => { onChange(val); - handleClose(); + if (!multiple) handleClose(); + }; + + const getDisplayName = (value: string | string[] | null, placeholder: string = "") => { + if (Array.isArray(value)) { + const firstProject = getProjectById(value[0]); + return value.length ? (value.length === 1 ? firstProject?.name : `${value.length} projects`) : placeholder; + } else { + return value ? (getProjectById(value)?.name ?? placeholder) : placeholder; + } + }; + + const getProjectIcon = (value: string | string[] | null) => { + const renderIcon = (projectDetails: TProject) => ( + + + + ); + + if (Array.isArray(value)) { + return ( +
    + {value.map((projectId) => { + const projectDetails = getProjectById(projectId); + return projectDetails ? renderIcon(projectDetails) : null; + })} +
    + ); + } else { + const projectDetails = getProjectById(value); + return projectDetails ? renderIcon(projectDetails) : null; + } }; const comboButton = ( @@ -147,18 +186,14 @@ export const ProjectDropdown: React.FC = observer((props) => { className={buttonClassName} isActive={isOpen} tooltipHeading="Project" - tooltipContent={selectedProject?.name ?? placeholder} + tooltipContent={value?.length ? `${value.length} project${value.length !== 1 ? "s" : ""}` : placeholder} showTooltip={showTooltip} variant={buttonVariant} renderToolTipByDefault={renderByDefault} > - {!hideIcon && selectedProject && ( - - - - )} + {!hideIcon && getProjectIcon(value)} {BUTTON_VARIANTS_WITH_TEXT.includes(buttonVariant) && ( - {selectedProject?.name ?? placeholder} + {getDisplayName(value, placeholder)} )} {dropdownArrow && (