From 109d8463a5a1c3a620aaea6fe883990979eeb10b Mon Sep 17 00:00:00 2001 From: Vamsi krishna Date: Tue, 17 Dec 2024 13:59:47 +0530 Subject: [PATCH 1/3] enhancement:added functionality to add features directly from dropdown --- .../issues/issue-detail/label/root.tsx | 17 +------ .../label/select/label-select.tsx | 48 +++++++++++++++---- .../issues/issue-detail/label/select/root.tsx | 1 + .../issue-layouts/properties/labels.tsx | 47 ++++++++++++++---- 4 files changed, 81 insertions(+), 32 deletions(-) diff --git a/web/core/components/issues/issue-detail/label/root.tsx b/web/core/components/issues/issue-detail/label/root.tsx index f71e9ba3ce2..f31defafd44 100644 --- a/web/core/components/issues/issue-detail/label/root.tsx +++ b/web/core/components/issues/issue-detail/label/root.tsx @@ -7,13 +7,12 @@ import { IIssueLabel, TIssue, TIssueServiceType } from "@plane/types"; // components import { TOAST_TYPE, setToast } from "@plane/ui"; // hooks -import { useIssueDetail, useLabel, useProjectInbox, useUserPermissions } from "@/hooks/store"; +import { useIssueDetail, useLabel, useProjectInbox } from "@/hooks/store"; // ui // types -import { LabelList, LabelCreate, IssueLabelSelectRoot } from "./"; +import { LabelList, IssueLabelSelectRoot } from "./"; // TODO: Fix this import statement, as core should not import from ee // eslint-disable-next-line import/order -import { EUserPermissions, EUserPermissionsLevel } from "ee/constants/user-permissions"; export type TIssueLabel = { workspaceSlug: string; @@ -47,9 +46,7 @@ export const IssueLabel: FC = observer((props) => { issue: { getIssueById }, } = useIssueDetail(issueServiceType); const { getIssueInboxByIssueId } = useProjectInbox(); - const { allowPermissions } = useUserPermissions(); - const canCreateLabel = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT); const issue = isInboxIssue ? getIssueInboxByIssueId(issueId)?.issue : getIssueById(issueId); const labelOperations: TLabelOperations = useMemo( @@ -113,16 +110,6 @@ export const IssueLabel: FC = observer((props) => { labelOperations={labelOperations} /> )} - - {!disabled && canCreateLabel && ( - - )} ); }); diff --git a/web/core/components/issues/issue-detail/label/select/label-select.tsx b/web/core/components/issues/issue-detail/label/select/label-select.tsx index 7fd700398c1..9aa9c58e09e 100644 --- a/web/core/components/issues/issue-detail/label/select/label-select.tsx +++ b/web/core/components/issues/issue-detail/label/select/label-select.tsx @@ -1,33 +1,40 @@ import { Fragment, useState } from "react"; +import { EUserPermissions, EUserPermissionsLevel } from "ee/constants/user-permissions"; import { observer } from "mobx-react"; import { usePopper } from "react-popper"; -import { Check, Search, Tag } from "lucide-react"; +import { Check, Loader, Search, Tag } from "lucide-react"; import { Combobox } from "@headlessui/react"; // helpers +import { IIssueLabel } from "@plane/types"; +import { getRandomLabelColor } from "@/constants/label"; import { getTabIndex } from "@/helpers/tab-indices.helper"; // hooks -import { useLabel } from "@/hooks/store"; +import { useLabel, useUserPermissions } from "@/hooks/store"; import { usePlatformOS } from "@/hooks/use-platform-os"; -// components - +//constants export interface IIssueLabelSelect { workspaceSlug: string; projectId: string; issueId: string; values: string[]; onSelect: (_labelIds: string[]) => void; + onAddLabel: (workspaceSlug: string, projectId: string, data: Partial) => Promise; } export const IssueLabelSelect: React.FC = observer((props) => { - const { workspaceSlug, projectId, issueId, values, onSelect } = props; + const { workspaceSlug, projectId, issueId, values, onSelect, onAddLabel } = props; // store hooks const { isMobile } = usePlatformOS(); const { fetchProjectLabels, getProjectLabels } = useLabel(); + const { allowPermissions } = useUserPermissions(); // states const [referenceElement, setReferenceElement] = useState(null); const [popperElement, setPopperElement] = useState(null); const [isLoading, setIsLoading] = useState(false); const [query, setQuery] = useState(""); + const [submitting, setSubmitting] = useState(false); + + const canCreateLabel = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT); const projectLabels = getProjectLabels(projectId); @@ -83,11 +90,25 @@ export const IssueLabelSelect: React.FC = observer((props) => ); - const searchInputKeyDown = (e: React.KeyboardEvent) => { + const searchInputKeyDown = async (e: React.KeyboardEvent) => { if (query !== "" && e.key === "Escape") { e.stopPropagation(); setQuery(""); } + + if (query !== "" && e.key === "Enter") { + e.stopPropagation(); + e.preventDefault(); + await handleAddLabel(query); + } + }; + + const handleAddLabel = async (labelName: string) => { + setSubmitting(true); + const label = await onAddLabel(workspaceSlug, projectId, { name: labelName, color: getRandomLabelColor() }); + onSelect([...values, label.id]); + setQuery(""); + setSubmitting(false); }; if (!issueId || !values) return <>; @@ -159,10 +180,19 @@ export const IssueLabelSelect: React.FC = observer((props) => )} )) + ) : submitting ? ( + + ) : canCreateLabel ? ( +

{ + handleAddLabel(query); + }} + className="text-left text-custom-text-200 cursor-pointer" + > + + Add "{query}" to labels +

) : ( - -

No matching results

-
+

No matching results.

)} diff --git a/web/core/components/issues/issue-detail/label/select/root.tsx b/web/core/components/issues/issue-detail/label/select/root.tsx index 00f96522b1c..a57a58742c2 100644 --- a/web/core/components/issues/issue-detail/label/select/root.tsx +++ b/web/core/components/issues/issue-detail/label/select/root.tsx @@ -26,6 +26,7 @@ export const IssueLabelSelectRoot: FC = (props) => { issueId={issueId} values={values} onSelect={handleLabel} + onAddLabel={labelOperations.createLabel} /> ); }; diff --git a/web/core/components/issues/issue-layouts/properties/labels.tsx b/web/core/components/issues/issue-layouts/properties/labels.tsx index 823137cb88f..053e1d8a7da 100644 --- a/web/core/components/issues/issue-layouts/properties/labels.tsx +++ b/web/core/components/issues/issue-layouts/properties/labels.tsx @@ -1,11 +1,12 @@ "use client"; -import { Fragment, useEffect, useRef, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { Placement } from "@popperjs/core"; +import { EUserPermissions, EUserPermissionsLevel } from "ee/constants/user-permissions"; import { observer } from "mobx-react"; import { useParams } from "next/navigation"; import { usePopper } from "react-popper"; -import { Check, ChevronDown, Search, Tags } from "lucide-react"; +import { Check, ChevronDown, Loader, Search, Tags } from "lucide-react"; import { Combobox } from "@headlessui/react"; // plane helpers import { useOutsideClickDetector } from "@plane/hooks"; @@ -14,9 +15,11 @@ import { IIssueLabel } from "@plane/types"; // ui import { ComboDropDown, Tooltip } from "@plane/ui"; // hooks -import { useLabel } from "@/hooks/store"; +import { getRandomLabelColor } from "@/constants/label"; +import { useLabel, useUserPermissions } from "@/hooks/store"; import { useDropdownKeyDown } from "@/hooks/use-dropdown-key-down"; import { usePlatformOS } from "@/hooks/use-platform-os"; +// constants export interface IIssuePropertyLabels { projectId: string | null; @@ -62,6 +65,7 @@ export const IssuePropertyLabels: React.FC = observer((pro // states const [query, setQuery] = useState(""); const [isOpen, setIsOpen] = useState(false); + const [submitting, setSubmitting] = useState(false); // refs const dropdownRef = useRef(null); const inputRef = useRef(null); @@ -70,9 +74,12 @@ export const IssuePropertyLabels: React.FC = observer((pro const [popperElement, setPopperElement] = useState(null); const [isLoading, setIsLoading] = useState(false); // store hooks - const { fetchProjectLabels, getProjectLabels } = useLabel(); + const { fetchProjectLabels, getProjectLabels, createLabel } = useLabel(); const { isMobile } = usePlatformOS(); const storeLabels = getProjectLabels(projectId); + const { allowPermissions } = useUserPermissions(); + + const canCreateLabel = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT); const onOpen = () => { if (!storeLabels && workspaceSlug && projectId) @@ -102,11 +109,17 @@ export const IssuePropertyLabels: React.FC = observer((pro useOutsideClickDetector(dropdownRef, handleClose); - const searchInputKeyDown = (e: React.KeyboardEvent) => { + const searchInputKeyDown = async (e: React.KeyboardEvent) => { if (query !== "" && e.key === "Escape") { e.stopPropagation(); setQuery(""); } + + if (query !== "" && e.key === "Enter") { + e.stopPropagation(); + e.preventDefault(); + await handleAddLabel(query); + } }; useEffect(() => { @@ -249,6 +262,15 @@ export const IssuePropertyLabels: React.FC = observer((pro ); + const handleAddLabel = async (labelName: string) => { + if (!projectId) return; + setSubmitting(true); + const label = await createLabel(workspaceSlug, projectId, { name: labelName, color: getRandomLabelColor() }); + onChange([...value, label.id]); + setQuery(""); + setSubmitting(false); + }; + return ( = observer((pro )} )) + ) : submitting ? ( + + ) : canCreateLabel ? ( +

{ + handleAddLabel(query); + }} + className="text-left text-custom-text-200 cursor-pointer" + > + + Add "{query}" to labels +

) : ( - -

No matching results

-
+

No matching results.

)} From c10783bb472263bc32fa39e5ec69563622109023 Mon Sep 17 00:00:00 2001 From: Vamsi krishna Date: Tue, 17 Dec 2024 14:01:52 +0530 Subject: [PATCH 2/3] fix: fixed import order --- web/core/components/issues/issue-layouts/kanban/block.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/core/components/issues/issue-layouts/kanban/block.tsx b/web/core/components/issues/issue-layouts/kanban/block.tsx index d8f4307e855..5fbee1c0a73 100644 --- a/web/core/components/issues/issue-layouts/kanban/block.tsx +++ b/web/core/components/issues/issue-layouts/kanban/block.tsx @@ -6,6 +6,7 @@ import { draggable, dropTargetForElements } from "@atlaskit/pragmatic-drag-and-d import { observer } from "mobx-react"; import { useParams } from "next/navigation"; // plane helpers +import { EIssueServiceType } from "@plane/constants"; import { useOutsideClickDetector } from "@plane/hooks"; // types import { TIssue, IIssueDisplayProperties, IIssueMap } from "@plane/types"; @@ -26,7 +27,6 @@ import { IssueIdentifier } from "@/plane-web/components/issues"; import { TRenderQuickActions } from "../list/list-view-types"; import { IssueProperties } from "../properties/all-properties"; import { getIssueBlockId } from "../utils"; -import { EIssueServiceType } from "@plane/constants"; interface IssueBlockProps { issueId: string; From a5d1e0bf97b7cdbed71fa1ee10dc14fb523f4744 Mon Sep 17 00:00:00 2001 From: Vamsi krishna Date: Tue, 17 Dec 2024 14:15:23 +0530 Subject: [PATCH 3/3] fix: fixed lint errors --- .../issues/issue-detail/label/select/label-select.tsx | 2 +- web/core/components/issues/issue-layouts/properties/labels.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/core/components/issues/issue-detail/label/select/label-select.tsx b/web/core/components/issues/issue-detail/label/select/label-select.tsx index 9aa9c58e09e..39c76180937 100644 --- a/web/core/components/issues/issue-detail/label/select/label-select.tsx +++ b/web/core/components/issues/issue-detail/label/select/label-select.tsx @@ -1,5 +1,4 @@ import { Fragment, useState } from "react"; -import { EUserPermissions, EUserPermissionsLevel } from "ee/constants/user-permissions"; import { observer } from "mobx-react"; import { usePopper } from "react-popper"; import { Check, Loader, Search, Tag } from "lucide-react"; @@ -11,6 +10,7 @@ import { getTabIndex } from "@/helpers/tab-indices.helper"; // hooks import { useLabel, useUserPermissions } from "@/hooks/store"; import { usePlatformOS } from "@/hooks/use-platform-os"; +import { EUserPermissions, EUserPermissionsLevel } from "ee/constants/user-permissions"; //constants export interface IIssueLabelSelect { workspaceSlug: string; diff --git a/web/core/components/issues/issue-layouts/properties/labels.tsx b/web/core/components/issues/issue-layouts/properties/labels.tsx index 053e1d8a7da..56f9b651bef 100644 --- a/web/core/components/issues/issue-layouts/properties/labels.tsx +++ b/web/core/components/issues/issue-layouts/properties/labels.tsx @@ -2,7 +2,6 @@ import { useEffect, useRef, useState } from "react"; import { Placement } from "@popperjs/core"; -import { EUserPermissions, EUserPermissionsLevel } from "ee/constants/user-permissions"; import { observer } from "mobx-react"; import { useParams } from "next/navigation"; import { usePopper } from "react-popper"; @@ -19,6 +18,7 @@ import { getRandomLabelColor } from "@/constants/label"; import { useLabel, useUserPermissions } from "@/hooks/store"; import { useDropdownKeyDown } from "@/hooks/use-dropdown-key-down"; import { usePlatformOS } from "@/hooks/use-platform-os"; +import { EUserPermissions, EUserPermissionsLevel } from "ee/constants/user-permissions"; // constants export interface IIssuePropertyLabels {