[WEB-3357 | WEB-3363 | WEB-3370] chore: command-k enhancement and fixes (#6600)

* fix: command-k work item actions

* chore: command k work item context indicator improvement and default vale for workspace toggle updated

* chore: code refactor
This commit is contained in:
Anmol Singh Bhatia 2025-02-14 19:04:08 +05:30 committed by GitHub
parent bf1f12378e
commit 82eea3e802
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 79 additions and 92 deletions

View file

@ -4,14 +4,13 @@ import { Command } from "cmdk";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import { LinkIcon, Signal, Trash2, UserMinus2, UserPlus2, Users } from "lucide-react";
import { EIssuesStoreType } from "@plane/constants";
import { TIssue } from "@plane/types";
// hooks
import { DoubleCircleIcon, TOAST_TYPE, setToast } from "@plane/ui";
// helpers
import { copyTextToClipboard } from "@/helpers/string.helper";
// hooks
import { useCommandPalette, useIssues, useUser } from "@/hooks/store";
import { useCommandPalette, useIssueDetail, useUser } from "@/hooks/store";
type Props = {
closePalette: () => void;
@ -27,9 +26,7 @@ export const CommandPaletteIssueActions: React.FC<Props> = observer((props) => {
// router
const { workspaceSlug, projectId, issueId } = useParams();
// hooks
const {
issues: { updateIssue },
} = useIssues(EIssuesStoreType.PROJECT);
const { updateIssue } = useIssueDetail();
const { toggleCommandPaletteModal, toggleDeleteIssueModal } = useCommandPalette();
const { data: currentUser } = useUser();
@ -65,16 +62,10 @@ export const CommandPaletteIssueActions: React.FC<Props> = observer((props) => {
const url = new URL(window.location.href);
copyTextToClipboard(url.href)
.then(() => {
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Copied to clipboard",
});
setToast({ type: TOAST_TYPE.SUCCESS, title: "Copied to clipboard" });
})
.catch(() => {
setToast({
type: TOAST_TYPE.ERROR,
title: "Some error occurred",
});
setToast({ type: TOAST_TYPE.ERROR, title: "Some error occurred" });
});
};

View file

@ -4,8 +4,6 @@ import { Command } from "cmdk";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import { Check } from "lucide-react";
// plane constants
import { EIssuesStoreType } from "@plane/constants";
// plane types
import { TIssue } from "@plane/types";
// plane ui
@ -13,21 +11,16 @@ import { Avatar } from "@plane/ui";
// helpers
import { getFileURL } from "@/helpers/file.helper";
// hooks
import { useIssues, useMember } from "@/hooks/store";
import { useIssueDetail, useMember } from "@/hooks/store";
type Props = {
closePalette: () => void;
issue: TIssue;
};
type Props = { closePalette: () => void; issue: TIssue };
export const ChangeIssueAssignee: React.FC<Props> = observer((props) => {
const { closePalette, issue } = props;
// router params
const { workspaceSlug, projectId } = useParams();
// store
const {
issues: { updateIssue },
} = useIssues(EIssuesStoreType.PROJECT);
const { updateIssue } = useIssueDetail();
const {
project: { projectMemberIds, getProjectMemberDetails },
} = useMember();

View file

@ -5,28 +5,23 @@ import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import { Check } from "lucide-react";
// plane constants
import { EIssuesStoreType, ISSUE_PRIORITIES } from "@plane/constants";
import { ISSUE_PRIORITIES } from "@plane/constants";
// plane types
import { TIssue, TIssuePriorities } from "@plane/types";
// mobx store
import { PriorityIcon } from "@plane/ui";
import { useIssues } from "@/hooks/store";
import { useIssueDetail } from "@/hooks/store";
// ui
// types
// constants
type Props = {
closePalette: () => void;
issue: TIssue;
};
type Props = { closePalette: () => void; issue: TIssue };
export const ChangeIssuePriority: React.FC<Props> = observer((props) => {
const { closePalette, issue } = props;
// router params
const { workspaceSlug, projectId } = useParams();
const {
issues: { updateIssue },
} = useIssues(EIssuesStoreType.PROJECT);
const { updateIssue } = useIssueDetail();
const submitChanges = async (formData: Partial<TIssue>) => {
if (!workspaceSlug || !projectId || !issue) return;

View file

@ -5,27 +5,21 @@ import { observer } from "mobx-react";
import { useParams } from "next/navigation";
// hooks
import { Check } from "lucide-react";
import { EIssuesStoreType } from "@plane/constants";
import { TIssue } from "@plane/types";
import { Spinner, StateGroupIcon } from "@plane/ui";
import { useProjectState, useIssues } from "@/hooks/store";
import { useProjectState, useIssueDetail } from "@/hooks/store";
// ui
// icons
// types
type Props = {
closePalette: () => void;
issue: TIssue;
};
type Props = { closePalette: () => void; issue: TIssue };
export const ChangeIssueState: React.FC<Props> = observer((props) => {
const { closePalette, issue } = props;
// router params
const { workspaceSlug, projectId } = useParams();
// store hooks
const {
issues: { updateIssue },
} = useIssues(EIssuesStoreType.PROJECT);
const { updateIssue } = useIssueDetail();
const { projectStates } = useProjectState();
const submitChanges = async (formData: Partial<TIssue>) => {

View file

@ -5,13 +5,14 @@ import { Command } from "cmdk";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import useSWR from "swr";
import { CommandIcon, FolderPlus, Search, Settings } from "lucide-react";
import { CommandIcon, FolderPlus, Search, Settings, X } from "lucide-react";
import { Dialog, Transition } from "@headlessui/react";
// plane imports
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { IWorkspaceSearchResults } from "@plane/types";
import { LayersIcon, Loader, ToggleSwitch } from "@plane/ui";
import { cn } from "@plane/utils";
// components
import {
ChangeIssueAssignee,
@ -56,18 +57,11 @@ export const CommandModal: React.FC = observer(() => {
const [isSearching, setIsSearching] = useState(false);
const [searchTerm, setSearchTerm] = useState("");
const [results, setResults] = useState<IWorkspaceSearchResults>({
results: {
workspace: [],
project: [],
issue: [],
cycle: [],
module: [],
issue_view: [],
page: [],
},
results: { workspace: [], project: [], issue: [], cycle: [], module: [], issue_view: [], page: [] },
});
const [isWorkspaceLevel, setIsWorkspaceLevel] = useState(true);
const [isWorkspaceLevel, setIsWorkspaceLevel] = useState(false);
const [pages, setPages] = useState<string[]>([]);
const [searchInIssue, setSearchInIssue] = useState(false);
// plane hooks
const { t } = useTranslation();
// hooks
@ -96,6 +90,20 @@ export const CommandModal: React.FC = observer(() => {
: null
);
useEffect(() => {
if (issueDetails && isCommandPaletteOpen) {
setSearchInIssue(true);
}
}, [issueDetails, isCommandPaletteOpen]);
useEffect(() => {
if (!projectId && !isWorkspaceLevel) {
setIsWorkspaceLevel(true);
} else {
setIsWorkspaceLevel(false);
}
}, [projectId]);
const closePalette = () => {
toggleCommandPaletteModal(false);
};
@ -133,15 +141,7 @@ export const CommandModal: React.FC = observer(() => {
});
} else {
setResults({
results: {
workspace: [],
project: [],
issue: [],
cycle: [],
module: [],
issue_view: [],
page: [],
},
results: { workspace: [], project: [], issue: [], cycle: [], module: [], issue_view: [], page: [] },
});
setIsLoading(false);
setIsSearching(false);
@ -152,7 +152,16 @@ export const CommandModal: React.FC = observer(() => {
return (
<Transition.Root show={isCommandPaletteOpen} afterLeave={() => setSearchTerm("")} as={React.Fragment}>
<Dialog as="div" className="relative z-30" onClose={() => closePalette()}>
<Dialog
as="div"
className="relative z-30"
onClose={() => {
closePalette();
if (searchInIssue) {
setSearchInIssue(true);
}
}}
>
<Transition.Child
as={React.Fragment}
enter="ease-out duration-300"
@ -213,10 +222,7 @@ export const CommandModal: React.FC = observer(() => {
nextItem.setAttribute("aria-selected", "true");
selectedItem?.setAttribute("aria-selected", "false");
nextItem.focus();
nextItem.scrollIntoView({
behavior: "smooth",
block: "nearest",
});
nextItem.scrollIntoView({ behavior: "smooth", block: "nearest" });
}
}
@ -237,32 +243,40 @@ export const CommandModal: React.FC = observer(() => {
}
}}
>
<div
className={`flex gap-4 pb-0 sm:items-center ${
issueDetails ? "flex-col justify-between sm:flex-row" : "justify-end"
}`}
>
{issueDetails && (
<div className="flex gap-2 items-center overflow-hidden truncate rounded-md bg-custom-background-80 p-2 text-xs font-medium text-custom-text-200">
{issueDetails.project_id && (
<IssueIdentifier
issueId={issueDetails.id}
projectId={issueDetails.project_id}
textContainerClassName="text-xs font-medium text-custom-text-200"
/>
)}
{issueDetails.name}
</div>
)}
</div>
<div className="relative">
<Search
className="pointer-events-none absolute left-4 top-1/2 h-4 w-4 -translate-y-1/2 text-custom-text-200"
aria-hidden="true"
strokeWidth={2}
/>
<div className="relative flex items-center px-4 border-0 border-b border-custom-border-200">
<div className="flex items-center gap-2 flex-shrink-0">
<Search
className="h-4 w-4 text-custom-text-200 flex-shrink-0"
aria-hidden="true"
strokeWidth={2}
/>
{searchInIssue && issueDetails && (
<>
<span className="flex items-center text-sm">Update in:</span>
<span className="flex items-center gap-1 rounded px-1.5 py-1 text-sm bg-custom-primary-100/10 ">
{issueDetails.project_id && (
<IssueIdentifier
issueId={issueDetails.id}
projectId={issueDetails.project_id}
textContainerClassName="text-sm text-custom-primary-200"
/>
)}
<X
size={12}
strokeWidth={2}
className="flex-shrink-0 cursor-pointer"
onClick={() => {
setSearchInIssue(false);
}}
/>
</span>
</>
)}
</div>
<Command.Input
className="w-full border-0 border-b border-custom-border-200 bg-transparent p-4 pl-11 text-sm text-custom-text-100 outline-none placeholder:text-custom-text-400 focus:ring-0"
className={cn(
"w-full bg-transparent p-4 text-sm text-custom-text-100 outline-none placeholder:text-custom-text-400 focus:ring-0"
)}
placeholder={placeholder}
value={searchTerm}
onValueChange={(e) => setSearchTerm(e)}
@ -308,7 +322,7 @@ export const CommandModal: React.FC = observer(() => {
{!page && (
<>
{/* issue actions */}
{issueId && (
{issueId && issueDetails && searchInIssue && (
<CommandPaletteIssueActions
closePalette={closePalette}
issueDetails={issueDetails}