fix: cmdk integration (#567)

* fix: issues not showing on cmd k

* fix: text overflows on longer issue title

* fix: add loading state whenever there is a network call

* fix: minor ux changes

* feat: replace loading with spinner
This commit is contained in:
Saheb Giri 2023-03-29 00:51:47 +05:30 committed by GitHub
parent 628591854d
commit dd3bca9a32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -27,7 +27,7 @@ import {
PeopleGroupIcon,
SettingIcon,
ViewListIcon,
PencilScribbleIcon
PencilScribbleIcon,
} from "components/icons";
// headless ui
import { Dialog, Transition } from "@headlessui/react";
@ -37,6 +37,7 @@ import { Command } from "cmdk";
import useTheme from "hooks/use-theme";
import useToast from "hooks/use-toast";
import useUser from "hooks/use-user";
import useDebounce from "hooks/use-debounce";
// components
import {
ShortcutsModal,
@ -50,6 +51,7 @@ import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
import { CreateUpdateModuleModal } from "components/modules";
import { CreateProjectModal } from "components/project";
import { CreateUpdateViewModal } from "components/views";
import { Spinner } from "components/ui";
// helpers
import {
capitalizeFirstLetter,
@ -58,12 +60,11 @@ import {
} from "helpers/string.helper";
// services
import issuesService from "services/issues.service";
import workspaceService from "services/workspace.service";
// types
import { IIssue, IWorkspaceSearchResults } from "types";
// fetch keys
import { ISSUE_DETAILS, PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys";
import useDebounce from "hooks/use-debounce";
import workspaceService from "services/workspace.service";
export const CommandPalette: React.FC = () => {
const [isPaletteOpen, setIsPaletteOpen] = useState(false);
@ -88,7 +89,9 @@ export const CommandPalette: React.FC = () => {
page: [],
},
});
const [isPendingAPIRequest, setIsPendingAPIRequest] = useState(false);
const [resultsCount, setResultsCount] = useState(0);
const [isLoading, setIsLoading] = useState(false);
const [isSearching, setIsSearching] = useState(false);
const debouncedSearchTerm = useDebounce(searchTerm, 500);
const [placeholder, setPlaceholder] = React.useState("Type a command or search...");
const [pages, setPages] = React.useState<string[]>([]);
@ -220,18 +223,39 @@ export const CommandPalette: React.FC = () => {
() => {
if (!workspaceSlug || !projectId) return;
// this is done prevent api request when user is clearing input
setIsLoading(true);
// this is done prevent subsequent api request
// or searchTerm has not been updated within last 500ms.
if (debouncedSearchTerm) {
setIsPendingAPIRequest(true);
setIsSearching(true);
workspaceService
.searchWorkspace(workspaceSlug as string, projectId as string, debouncedSearchTerm)
.then((results) => {
setIsPendingAPIRequest(false);
setResults(results);
const count = Object.keys(results.results).reduce(
(accumulator, key) => (results.results as any)[key].length + accumulator,
0
);
setResultsCount(count);
})
.finally(() => {
setIsLoading(false);
setIsSearching(false);
});
} else {
setIsPendingAPIRequest(false);
setResults({
results: {
workspace: [],
project: [],
issue: [],
cycle: [],
module: [],
issue_view: [],
page: [],
},
});
setIsLoading(false);
setIsSearching(false);
}
},
[debouncedSearchTerm, workspaceSlug, projectId] // Only call effect if debounced search term changes
@ -369,11 +393,11 @@ export const CommandPalette: React.FC = () => {
}}
>
{issueId && issueDetails && (
<div className="p-3">
<span className="rounded-md bg-slate-100 p-1 px-2 text-xs font-medium text-slate-500">
<div className="flex p-3">
<p className="overflow-hidden truncate rounded-md bg-slate-100 p-1 px-2 text-xs font-medium text-slate-500">
{issueDetails.project_detail?.identifier}-{issueDetails.sequence_id}{" "}
{issueDetails?.name}
</span>
</p>
</div>
)}
<div className="relative">
@ -392,9 +416,20 @@ export const CommandPalette: React.FC = () => {
/>
</div>
<Command.List className="max-h-96 overflow-scroll p-2">
<Command.Empty className="my-4 text-center text-gray-500">
No results found.
</Command.Empty>
{!isLoading &&
resultsCount === 0 &&
searchTerm !== "" &&
debouncedSearchTerm !== "" && (
<div className="my-4 text-center text-gray-500">No results found.</div>
)}
{(isLoading || isSearching) && (
<Command.Loading>
<div className="flex h-full w-full items-center justify-center py-8">
<Spinner />
</div>
</Command.Loading>
)}
{debouncedSearchTerm !== "" && (
<>
@ -419,7 +454,8 @@ export const CommandPalette: React.FC = () => {
Icon = AssignmentClipboardIcon;
} else if (key === "issue") {
path = `/${item.workspace__slug}/projects/${item.project_id}/issues/${item.id}`;
value = `${item.project__identifier}-${item.sequence_id} item.name`;
// user can search id-num idnum or issue name
value = `${item.project__identifier}-${item.sequence_id} ${item.project__identifier}${item.sequence_id} ${item.name}`;
Icon = LayerDiagonalIcon;
} else if (key === "issue_view") {
path = `/${item.workspace__slug}/projects/${item.project_id}/views/${item.id}`;
@ -446,9 +482,9 @@ export const CommandPalette: React.FC = () => {
className="focus:bg-slate-200 focus:outline-none"
tabIndex={0}
>
<div className="flex items-center gap-2 text-slate-700">
<div className="flex items-center gap-2 overflow-hidden text-slate-700">
<Icon className="h-4 w-4" />
{item.name}
<p className="block flex-1 truncate">{item.name}</p>
</div>
</Command.Item>
);