Skip to content

Commit

Permalink
[WEB-2678]feat: added functionality to add labels directly from dropd…
Browse files Browse the repository at this point in the history
…own (#6211)

* enhancement:added functionality to add features directly from dropdown

* fix: fixed import order

* fix: fixed lint errors
  • Loading branch information
mathalav55 authored Dec 17, 2024
1 parent 4507802 commit 8e6d885
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 33 deletions.
17 changes: 2 additions & 15 deletions web/core/components/issues/issue-detail/label/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -47,9 +46,7 @@ export const IssueLabel: FC<TIssueLabel> = 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(
Expand Down Expand Up @@ -113,16 +110,6 @@ export const IssueLabel: FC<TIssueLabel> = observer((props) => {
labelOperations={labelOperations}
/>
)}

{!disabled && canCreateLabel && (
<LabelCreate
workspaceSlug={workspaceSlug}
projectId={projectId}
issueId={issueId}
values={issue?.label_ids || []}
labelOperations={labelOperations}
/>
)}
</div>
);
});
Original file line number Diff line number Diff line change
@@ -1,33 +1,40 @@
import { Fragment, useState } from "react";
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

import { EUserPermissions, EUserPermissionsLevel } from "ee/constants/user-permissions";
//constants
export interface IIssueLabelSelect {
workspaceSlug: string;
projectId: string;
issueId: string;
values: string[];
onSelect: (_labelIds: string[]) => void;
onAddLabel: (workspaceSlug: string, projectId: string, data: Partial<IIssueLabel>) => Promise<any>;
}

export const IssueLabelSelect: React.FC<IIssueLabelSelect> = 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<HTMLButtonElement | null>(null);
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [query, setQuery] = useState("");
const [submitting, setSubmitting] = useState<boolean>(false);

const canCreateLabel = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.PROJECT);

const projectLabels = getProjectLabels(projectId);

Expand Down Expand Up @@ -83,11 +90,25 @@ export const IssueLabelSelect: React.FC<IIssueLabelSelect> = observer((props) =>
</div>
);

const searchInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
const searchInputKeyDown = async (e: React.KeyboardEvent<HTMLInputElement>) => {
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 <></>;
Expand Down Expand Up @@ -159,10 +180,19 @@ export const IssueLabelSelect: React.FC<IIssueLabelSelect> = observer((props) =>
)}
</Combobox.Option>
))
) : submitting ? (
<Loader className="spin h-3.5 w-3.5" />
) : canCreateLabel ? (
<p
onClick={() => {
handleAddLabel(query);
}}
className="text-left text-custom-text-200 cursor-pointer"
>
+ Add <span className="text-custom-text-100">&quot;{query}&quot;</span> to labels
</p>
) : (
<span className="flex items-center gap-2 p-1">
<p className="text-left text-custom-text-200 ">No matching results</p>
</span>
<p className="text-left text-custom-text-200 ">No matching results.</p>
)}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const IssueLabelSelectRoot: FC<TIssueLabelSelectRoot> = (props) => {
issueId={issueId}
values={values}
onSelect={handleLabel}
onAddLabel={labelOperations.createLabel}
/>
);
};
2 changes: 1 addition & 1 deletion web/core/components/issues/issue-layouts/kanban/block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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;
Expand Down
47 changes: 39 additions & 8 deletions web/core/components/issues/issue-layouts/properties/labels.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
"use client";

import { Fragment, useEffect, useRef, useState } from "react";
import { useEffect, useRef, useState } from "react";
import { Placement } from "@popperjs/core";
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";
Expand All @@ -14,9 +14,12 @@ 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";
import { EUserPermissions, EUserPermissionsLevel } from "ee/constants/user-permissions";
// constants

export interface IIssuePropertyLabels {
projectId: string | null;
Expand Down Expand Up @@ -62,6 +65,7 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
// states
const [query, setQuery] = useState("");
const [isOpen, setIsOpen] = useState(false);
const [submitting, setSubmitting] = useState<boolean>(false);
// refs
const dropdownRef = useRef<HTMLDivElement | null>(null);
const inputRef = useRef<HTMLInputElement | null>(null);
Expand All @@ -70,9 +74,12 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(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)
Expand Down Expand Up @@ -102,11 +109,17 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro

useOutsideClickDetector(dropdownRef, handleClose);

const searchInputKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
const searchInputKeyDown = async (e: React.KeyboardEvent<HTMLInputElement>) => {
if (query !== "" && e.key === "Escape") {
e.stopPropagation();
setQuery("");
}

if (query !== "" && e.key === "Enter") {
e.stopPropagation();
e.preventDefault();
await handleAddLabel(query);
}
};

useEffect(() => {
Expand Down Expand Up @@ -249,6 +262,15 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
</button>
);

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 (
<ComboDropDown
as="div"
Expand Down Expand Up @@ -314,10 +336,19 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
)}
</Combobox.Option>
))
) : submitting ? (
<Loader className="spin h-3.5 w-3.5" />
) : canCreateLabel ? (
<p
onClick={() => {
handleAddLabel(query);
}}
className="text-left text-custom-text-200 cursor-pointer"
>
+ Add <span className="text-custom-text-100">&quot;{query}&quot;</span> to labels
</p>
) : (
<span className="flex items-center gap-2 p-1">
<p className="text-left text-custom-text-200 ">No matching results</p>
</span>
<p className="text-left text-custom-text-200 ">No matching results.</p>
)}
</div>
</div>
Expand Down

0 comments on commit 8e6d885

Please sign in to comment.