Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions components/command-palette/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ import { useRouter } from "next/router";
import { Combobox, Dialog, Transition } from "@headlessui/react";
// hooks
import useUser from "lib/hooks/useUser";
import useTheme from "lib/hooks/useTheme";
import useToast from "lib/hooks/useToast";
// icons
import { MagnifyingGlassIcon } from "@heroicons/react/20/solid";
import { DocumentPlusIcon, FolderPlusIcon, FolderIcon } from "@heroicons/react/24/outline";
// commons
import { classNames } from "constants/common";
import { classNames, copyTextToClipboard } from "constants/common";
// components
import ShortcutsModal from "components/command-palette/shortcuts";
import CreateProjectModal from "components/project/CreateProjectModal";
import CreateUpdateIssuesModal from "components/project/issues/CreateUpdateIssueModal";
import CreateUpdateCycleModal from "components/project/cycles/CreateUpdateCyclesModal";
// hooks
import useTheme from "lib/hooks/useTheme";
// types
import { IIssue } from "types";
type ItemType = {
Expand All @@ -40,6 +40,8 @@ const CommandPalette: React.FC = () => {

const { toggleCollapsed } = useTheme();

const { setToastAlert } = useToast();

const filteredIssues: IIssue[] =
query === ""
? issues?.results ?? []
Expand Down Expand Up @@ -72,7 +74,7 @@ const CommandPalette: React.FC = () => {

const handleKeyDown = useCallback(
(e: KeyboardEvent) => {
if (e.key === "/") {
if (e.ctrlKey && e.key === "/") {
e.preventDefault();
setIsPaletteOpen(true);
} else if (e.ctrlKey && e.key === "i") {
Expand All @@ -90,9 +92,28 @@ const CommandPalette: React.FC = () => {
} else if (e.ctrlKey && e.key === "q") {
e.preventDefault();
setIsCreateCycleModalOpen(true);
} else if (e.ctrlKey && e.altKey && e.key === "c") {
e.preventDefault();

if (!router.query.issueId) return;

const url = new URL(window.location.href);
copyTextToClipboard(url.href)
.then(() => {
setToastAlert({
type: "success",
title: "Copied to clipboard",
});
})
.catch(() => {
setToastAlert({
type: "error",
title: "Some error occurred",
});
});
}
},
[toggleCollapsed]
[toggleCollapsed, setToastAlert, router]
);

useEffect(() => {
Expand Down
6 changes: 5 additions & 1 deletion components/command-palette/shortcuts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
{
title: "Navigation",
shortcuts: [
{ key: "/", description: "To open navigator" },
{ key: "Ctrl + /", description: "To open navigator" },
{ key: "↑", description: "Move up" },
{ key: "↓", description: "Move down" },
{ key: "←", description: "Move left" },
Expand All @@ -75,6 +75,10 @@ const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
{ key: "Ctrl + i", description: "To open create issue modal" },
{ key: "Ctrl + q", description: "To open create cycle modal" },
{ key: "Ctrl + h", description: "To open shortcuts guide" },
{
key: "Ctrl + alt + c",
description: "To copy issue url when on issue detail page.",
},
],
},
].map(({ title, shortcuts }) => (
Expand Down
1 change: 0 additions & 1 deletion components/project/CreateProjectModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ const CreateProjectModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
const checkIdentifier = (slug: string, value: string) => {
projectServices.checkProjectIdentifierAvailability(slug, value).then((response) => {
console.log(response);

if (response.exists) setError("identifier", { message: "Identifier already exists" });
});
};
Expand Down
6 changes: 3 additions & 3 deletions components/project/cycles/CycleView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,11 @@ const SprintView: React.FC<Props> = ({
<span
className="text-black rounded px-2 py-0.5 text-sm border"
style={{
backgroundColor: `${issue.issue_details.state_detail.color}20`,
borderColor: issue.issue_details.state_detail.color,
backgroundColor: `${issue.issue_details.state_detail?.color}20`,
borderColor: issue.issue_details.state_detail?.color,
}}
>
{issue.issue_details.state_detail.name}
{issue.issue_details.state_detail?.name}
</span>
<div className="relative">
<Menu>
Expand Down
98 changes: 13 additions & 85 deletions components/project/issues/CreateUpdateIssueModal/SelectAssignee.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
import React, { useContext } from "react";
import React from "react";
// swr
import useSWR from "swr";
// react hook form
import { Controller } from "react-hook-form";
// headless ui
import { Listbox, Transition } from "@headlessui/react";
// service
import projectServices from "lib/services/project.service";
// hooks
import useUser from "lib/hooks/useUser";
// fetch keys
import { PROJECT_MEMBERS } from "constants/fetch-keys";
// icons
import { CheckIcon } from "@heroicons/react/20/solid";

// types
import type { Control } from "react-hook-form";
import type { IIssue, WorkspaceMember } from "types";
import { UserIcon } from "@heroicons/react/24/outline";

import { SearchListbox } from "ui";

type Props = {
control: Control<IIssue, any>;
};
Expand All @@ -38,86 +35,17 @@ const SelectAssignee: React.FC<Props> = ({ control }) => {
control={control}
name="assignees_list"
render={({ field: { value, onChange } }) => (
<Listbox
<SearchListbox
title="Assignees"
optionsFontsize="sm"
options={people?.map((person) => {
return { value: person.member.id, display: person.member.first_name };
})}
multiple={true}
value={value}
onChange={(data: any) => {
const valueCopy = [...(value ?? [])];
if (valueCopy.some((i) => i === data)) onChange(valueCopy.filter((i) => i !== data));
else onChange([...valueCopy, data]);
}}
>
{({ open }) => (
<>
<div className="relative">
<Listbox.Button className="flex items-center gap-1 hover:bg-gray-100 relative border rounded-md shadow-sm px-2 py-1 cursor-pointer focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm duration-300">
<UserIcon className="h-3 w-3" />
<span className="block truncate">
{value && value.length > 0
? value
.map(
(id) =>
people
?.find((i) => i.member.id === id)
?.member.email.substring(0, 4) + "..."
)
.join(", ")
: "Assignees"}
</span>
</Listbox.Button>

<Transition
show={open}
as={React.Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="absolute mt-1 bg-white shadow-lg max-h-28 rounded-md py-1 text-xs ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none">
<div className="p-1">
{people?.map((person) => (
<Listbox.Option
key={person.member.id}
className={({ active }) =>
`${
active ? "text-white bg-theme" : "text-gray-900"
} cursor-pointer select-none relative p-2 rounded-md`
}
value={person.member.id}
>
{({ selected, active }) => (
<>
<span
className={`${
selected || (value ?? []).some((i) => i === person.member.id)
? "font-semibold"
: "font-normal"
} block truncate`}
>
{person.member.email}
</span>

{selected ? (
<span
className={`absolute inset-y-0 right-0 flex items-center pr-4 ${
active || (value ?? []).some((i) => i === person.member.id)
? "text-white"
: "text-indigo-600"
}`}
>
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>
) : null}
</>
)}
</Listbox.Option>
))}
</div>
</Listbox.Options>
</Transition>
</div>
</>
)}
</Listbox>
onChange={onChange}
icon={<UserIcon className="h-4 w-4 text-gray-400" />}
/>
)}
></Controller>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import React from "react";
// react hook form
import { Controller } from "react-hook-form";
// headless ui
import { Listbox, Transition } from "@headlessui/react";
// hooks
import useUser from "lib/hooks/useUser";
// icons
import { CheckIcon } from "@heroicons/react/20/solid";
// types
import type { IIssue } from "types";
import type { Control } from "react-hook-form";
Expand All @@ -16,12 +12,14 @@ type Props = {
control: Control<IIssue, any>;
};

import { SearchListbox } from "ui";

const SelectParent: React.FC<Props> = ({ control }) => {
const { issues: projectIssues } = useUser();

const getSelectedIssueKey = (issueId: string | undefined) => {
const identifier = projectIssues?.results?.find((i) => i.id.toString() === issueId?.toString())
?.project_detail.identifier;
?.project_detail?.identifier;

const sequenceId = projectIssues?.results?.find(
(i) => i.id.toString() === issueId?.toString()
Expand All @@ -36,53 +34,29 @@ const SelectParent: React.FC<Props> = ({ control }) => {
control={control}
name="parent"
render={({ field: { value, onChange } }) => (
<Listbox as="div" value={value} onChange={onChange}>
{({ open }) => (
<>
<div className="relative">
<Listbox.Button className="flex items-center gap-1 hover:bg-gray-100 relative border rounded-md shadow-sm px-2 py-1 cursor-pointer focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm duration-300">
<UserIcon className="h-3 w-3 flex-shrink-0" />
<span className="block truncate">{getSelectedIssueKey(value?.toString())}</span>
</Listbox.Button>

<Transition
show={open}
as={React.Fragment}
leave="transition ease-in duration-100"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<Listbox.Options className="absolute mt-1 bg-white shadow-lg max-h-28 max-w-[15rem] rounded-md py-1 text-xs ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none">
<div className="p-1">
{projectIssues?.results?.map((issue) => (
<Listbox.Option
key={issue.id}
value={issue.id}
className={({ active }) =>
`relative cursor-pointer select-none p-2 rounded-md ${
active ? "bg-theme text-white" : "text-gray-900"
}`
}
>
{({ active, selected }) => (
<>
<span className={`block truncate ${selected && "font-medium"}`}>
<span className="font-medium">
{issue.project_detail.identifier}-{issue.sequence_id}
</span>{" "}
{issue.name}
</span>
</>
)}
</Listbox.Option>
))}
</div>
</Listbox.Options>
</Transition>
</div>
</>
)}
</Listbox>
<SearchListbox
title="Parent issue"
optionsFontsize="sm"
options={projectIssues?.results?.map((issue) => {
return {
value: issue.id,
display: issue.name,
element: (
<div className="flex items-center space-x-3">
<div className="block truncate">
<span className="block truncate">{`${getSelectedIssueKey(issue.id)}`}</span>
<span className="block truncate text-gray-400">{issue.name}</span>
</div>
</div>
),
};
})}
value={value}
buttonClassName="max-h-30 overflow-y-scroll"
optionsClassName="max-h-30 overflow-y-scroll"
onChange={onChange}
icon={<UserIcon className="h-4 w-4 text-gray-400" />}
/>
)}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import { Listbox, Transition } from "@headlessui/react";
// hooks
import useUser from "lib/hooks/useUser";
// icons
import { CheckIcon } from "@heroicons/react/20/solid";
import { ClipboardDocumentListIcon, Squares2X2Icon } from "@heroicons/react/24/outline";
import { ClipboardDocumentListIcon } from "@heroicons/react/24/outline";
// ui
import { Spinner } from "ui";
// types
Expand Down
Loading