Skip to content

Commit 0680545

Browse files
authored
Merge pull request #1 from dakshesh14/main
dev: copy shortcuts, magic login links, improved settings page
2 parents 2a57b11 + 97544c1 commit 0680545

File tree

25 files changed

+881
-508
lines changed

25 files changed

+881
-508
lines changed

components/command-palette/index.tsx

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,18 @@ import { useRouter } from "next/router";
55
import { Combobox, Dialog, Transition } from "@headlessui/react";
66
// hooks
77
import useUser from "lib/hooks/useUser";
8+
import useTheme from "lib/hooks/useTheme";
9+
import useToast from "lib/hooks/useToast";
810
// icons
911
import { MagnifyingGlassIcon } from "@heroicons/react/20/solid";
1012
import { DocumentPlusIcon, FolderPlusIcon, FolderIcon } from "@heroicons/react/24/outline";
1113
// commons
12-
import { classNames } from "constants/common";
14+
import { classNames, copyTextToClipboard } from "constants/common";
1315
// components
1416
import ShortcutsModal from "components/command-palette/shortcuts";
1517
import CreateProjectModal from "components/project/CreateProjectModal";
1618
import CreateUpdateIssuesModal from "components/project/issues/CreateUpdateIssueModal";
1719
import CreateUpdateCycleModal from "components/project/cycles/CreateUpdateCyclesModal";
18-
// hooks
19-
import useTheme from "lib/hooks/useTheme";
2020
// types
2121
import { IIssue } from "types";
2222
type ItemType = {
@@ -40,6 +40,8 @@ const CommandPalette: React.FC = () => {
4040

4141
const { toggleCollapsed } = useTheme();
4242

43+
const { setToastAlert } = useToast();
44+
4345
const filteredIssues: IIssue[] =
4446
query === ""
4547
? issues?.results ?? []
@@ -72,7 +74,7 @@ const CommandPalette: React.FC = () => {
7274

7375
const handleKeyDown = useCallback(
7476
(e: KeyboardEvent) => {
75-
if (e.key === "/") {
77+
if (e.ctrlKey && e.key === "/") {
7678
e.preventDefault();
7779
setIsPaletteOpen(true);
7880
} else if (e.ctrlKey && e.key === "i") {
@@ -90,9 +92,28 @@ const CommandPalette: React.FC = () => {
9092
} else if (e.ctrlKey && e.key === "q") {
9193
e.preventDefault();
9294
setIsCreateCycleModalOpen(true);
95+
} else if (e.ctrlKey && e.altKey && e.key === "c") {
96+
e.preventDefault();
97+
98+
if (!router.query.issueId) return;
99+
100+
const url = new URL(window.location.href);
101+
copyTextToClipboard(url.href)
102+
.then(() => {
103+
setToastAlert({
104+
type: "success",
105+
title: "Copied to clipboard",
106+
});
107+
})
108+
.catch(() => {
109+
setToastAlert({
110+
type: "error",
111+
title: "Some error occurred",
112+
});
113+
});
93114
}
94115
},
95-
[toggleCollapsed]
116+
[toggleCollapsed, setToastAlert, router]
96117
);
97118

98119
useEffect(() => {

components/command-palette/shortcuts.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
5959
{
6060
title: "Navigation",
6161
shortcuts: [
62-
{ key: "/", description: "To open navigator" },
62+
{ key: "Ctrl + /", description: "To open navigator" },
6363
{ key: "↑", description: "Move up" },
6464
{ key: "↓", description: "Move down" },
6565
{ key: "←", description: "Move left" },
@@ -75,6 +75,10 @@ const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
7575
{ key: "Ctrl + i", description: "To open create issue modal" },
7676
{ key: "Ctrl + q", description: "To open create cycle modal" },
7777
{ key: "Ctrl + h", description: "To open shortcuts guide" },
78+
{
79+
key: "Ctrl + alt + c",
80+
description: "To copy issue url when on issue detail page.",
81+
},
7882
],
7983
},
8084
].map(({ title, shortcuts }) => (

components/project/CreateProjectModal.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ const CreateProjectModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
9292
const checkIdentifier = (slug: string, value: string) => {
9393
projectServices.checkProjectIdentifierAvailability(slug, value).then((response) => {
9494
console.log(response);
95-
9695
if (response.exists) setError("identifier", { message: "Identifier already exists" });
9796
});
9897
};

components/project/cycles/CycleView.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,11 @@ const SprintView: React.FC<Props> = ({
130130
<span
131131
className="text-black rounded px-2 py-0.5 text-sm border"
132132
style={{
133-
backgroundColor: `${issue.issue_details.state_detail.color}20`,
134-
borderColor: issue.issue_details.state_detail.color,
133+
backgroundColor: `${issue.issue_details.state_detail?.color}20`,
134+
borderColor: issue.issue_details.state_detail?.color,
135135
}}
136136
>
137-
{issue.issue_details.state_detail.name}
137+
{issue.issue_details.state_detail?.name}
138138
</span>
139139
<div className="relative">
140140
<Menu>

components/project/issues/CreateUpdateIssueModal/SelectAssignee.tsx

Lines changed: 13 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,21 @@
1-
import React, { useContext } from "react";
1+
import React from "react";
22
// swr
33
import useSWR from "swr";
44
// react hook form
55
import { Controller } from "react-hook-form";
6-
// headless ui
7-
import { Listbox, Transition } from "@headlessui/react";
86
// service
97
import projectServices from "lib/services/project.service";
108
// hooks
119
import useUser from "lib/hooks/useUser";
1210
// fetch keys
1311
import { PROJECT_MEMBERS } from "constants/fetch-keys";
14-
// icons
15-
import { CheckIcon } from "@heroicons/react/20/solid";
16-
1712
// types
1813
import type { Control } from "react-hook-form";
1914
import type { IIssue, WorkspaceMember } from "types";
2015
import { UserIcon } from "@heroicons/react/24/outline";
2116

17+
import { SearchListbox } from "ui";
18+
2219
type Props = {
2320
control: Control<IIssue, any>;
2421
};
@@ -38,86 +35,17 @@ const SelectAssignee: React.FC<Props> = ({ control }) => {
3835
control={control}
3936
name="assignees_list"
4037
render={({ field: { value, onChange } }) => (
41-
<Listbox
38+
<SearchListbox
39+
title="Assignees"
40+
optionsFontsize="sm"
41+
options={people?.map((person) => {
42+
return { value: person.member.id, display: person.member.first_name };
43+
})}
44+
multiple={true}
4245
value={value}
43-
onChange={(data: any) => {
44-
const valueCopy = [...(value ?? [])];
45-
if (valueCopy.some((i) => i === data)) onChange(valueCopy.filter((i) => i !== data));
46-
else onChange([...valueCopy, data]);
47-
}}
48-
>
49-
{({ open }) => (
50-
<>
51-
<div className="relative">
52-
<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">
53-
<UserIcon className="h-3 w-3" />
54-
<span className="block truncate">
55-
{value && value.length > 0
56-
? value
57-
.map(
58-
(id) =>
59-
people
60-
?.find((i) => i.member.id === id)
61-
?.member.email.substring(0, 4) + "..."
62-
)
63-
.join(", ")
64-
: "Assignees"}
65-
</span>
66-
</Listbox.Button>
67-
68-
<Transition
69-
show={open}
70-
as={React.Fragment}
71-
leave="transition ease-in duration-100"
72-
leaveFrom="opacity-100"
73-
leaveTo="opacity-0"
74-
>
75-
<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">
76-
<div className="p-1">
77-
{people?.map((person) => (
78-
<Listbox.Option
79-
key={person.member.id}
80-
className={({ active }) =>
81-
`${
82-
active ? "text-white bg-theme" : "text-gray-900"
83-
} cursor-pointer select-none relative p-2 rounded-md`
84-
}
85-
value={person.member.id}
86-
>
87-
{({ selected, active }) => (
88-
<>
89-
<span
90-
className={`${
91-
selected || (value ?? []).some((i) => i === person.member.id)
92-
? "font-semibold"
93-
: "font-normal"
94-
} block truncate`}
95-
>
96-
{person.member.email}
97-
</span>
98-
99-
{selected ? (
100-
<span
101-
className={`absolute inset-y-0 right-0 flex items-center pr-4 ${
102-
active || (value ?? []).some((i) => i === person.member.id)
103-
? "text-white"
104-
: "text-indigo-600"
105-
}`}
106-
>
107-
<CheckIcon className="h-5 w-5" aria-hidden="true" />
108-
</span>
109-
) : null}
110-
</>
111-
)}
112-
</Listbox.Option>
113-
))}
114-
</div>
115-
</Listbox.Options>
116-
</Transition>
117-
</div>
118-
</>
119-
)}
120-
</Listbox>
46+
onChange={onChange}
47+
icon={<UserIcon className="h-4 w-4 text-gray-400" />}
48+
/>
12149
)}
12250
></Controller>
12351
);

components/project/issues/CreateUpdateIssueModal/SelectParentIssues.tsx

Lines changed: 26 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
import React from "react";
22
// react hook form
33
import { Controller } from "react-hook-form";
4-
// headless ui
5-
import { Listbox, Transition } from "@headlessui/react";
64
// hooks
75
import useUser from "lib/hooks/useUser";
8-
// icons
9-
import { CheckIcon } from "@heroicons/react/20/solid";
106
// types
117
import type { IIssue } from "types";
128
import type { Control } from "react-hook-form";
@@ -16,12 +12,14 @@ type Props = {
1612
control: Control<IIssue, any>;
1713
};
1814

15+
import { SearchListbox } from "ui";
16+
1917
const SelectParent: React.FC<Props> = ({ control }) => {
2018
const { issues: projectIssues } = useUser();
2119

2220
const getSelectedIssueKey = (issueId: string | undefined) => {
2321
const identifier = projectIssues?.results?.find((i) => i.id.toString() === issueId?.toString())
24-
?.project_detail.identifier;
22+
?.project_detail?.identifier;
2523

2624
const sequenceId = projectIssues?.results?.find(
2725
(i) => i.id.toString() === issueId?.toString()
@@ -36,53 +34,29 @@ const SelectParent: React.FC<Props> = ({ control }) => {
3634
control={control}
3735
name="parent"
3836
render={({ field: { value, onChange } }) => (
39-
<Listbox as="div" value={value} onChange={onChange}>
40-
{({ open }) => (
41-
<>
42-
<div className="relative">
43-
<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">
44-
<UserIcon className="h-3 w-3 flex-shrink-0" />
45-
<span className="block truncate">{getSelectedIssueKey(value?.toString())}</span>
46-
</Listbox.Button>
47-
48-
<Transition
49-
show={open}
50-
as={React.Fragment}
51-
leave="transition ease-in duration-100"
52-
leaveFrom="opacity-100"
53-
leaveTo="opacity-0"
54-
>
55-
<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">
56-
<div className="p-1">
57-
{projectIssues?.results?.map((issue) => (
58-
<Listbox.Option
59-
key={issue.id}
60-
value={issue.id}
61-
className={({ active }) =>
62-
`relative cursor-pointer select-none p-2 rounded-md ${
63-
active ? "bg-theme text-white" : "text-gray-900"
64-
}`
65-
}
66-
>
67-
{({ active, selected }) => (
68-
<>
69-
<span className={`block truncate ${selected && "font-medium"}`}>
70-
<span className="font-medium">
71-
{issue.project_detail.identifier}-{issue.sequence_id}
72-
</span>{" "}
73-
{issue.name}
74-
</span>
75-
</>
76-
)}
77-
</Listbox.Option>
78-
))}
79-
</div>
80-
</Listbox.Options>
81-
</Transition>
82-
</div>
83-
</>
84-
)}
85-
</Listbox>
37+
<SearchListbox
38+
title="Parent issue"
39+
optionsFontsize="sm"
40+
options={projectIssues?.results?.map((issue) => {
41+
return {
42+
value: issue.id,
43+
display: issue.name,
44+
element: (
45+
<div className="flex items-center space-x-3">
46+
<div className="block truncate">
47+
<span className="block truncate">{`${getSelectedIssueKey(issue.id)}`}</span>
48+
<span className="block truncate text-gray-400">{issue.name}</span>
49+
</div>
50+
</div>
51+
),
52+
};
53+
})}
54+
value={value}
55+
buttonClassName="max-h-30 overflow-y-scroll"
56+
optionsClassName="max-h-30 overflow-y-scroll"
57+
onChange={onChange}
58+
icon={<UserIcon className="h-4 w-4 text-gray-400" />}
59+
/>
8660
)}
8761
/>
8862
);

components/project/issues/CreateUpdateIssueModal/SelectProject.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ import { Listbox, Transition } from "@headlessui/react";
66
// hooks
77
import useUser from "lib/hooks/useUser";
88
// icons
9-
import { CheckIcon } from "@heroicons/react/20/solid";
10-
import { ClipboardDocumentListIcon, Squares2X2Icon } from "@heroicons/react/24/outline";
9+
import { ClipboardDocumentListIcon } from "@heroicons/react/24/outline";
1110
// ui
1211
import { Spinner } from "ui";
1312
// types

0 commit comments

Comments
 (0)