From ca7d3309d31e44336d68e57fcd572e8699373074 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Fri, 23 Jun 2023 19:30:53 +0530 Subject: [PATCH] chore: accept issue confirmation modal (#1377) * chore: accept issue confirmation modal * chore: add inbox option to the command menu * fix: status colors not loading * chore: show state name on the inbox issue sidebar --- .../command-palette/command-pallette.tsx | 54 ++++++++--- .../components/inbox/accept-issue-modal.tsx | 92 +++++++++++++++++++ .../components/inbox/inbox-action-headers.tsx | 26 +++--- .../components/inbox/inbox-main-content.tsx | 4 +- apps/app/components/inbox/index.ts | 1 + .../components/inbox/issues-list-sidebar.tsx | 5 +- .../issues/sidebar-select/state.tsx | 29 ++++-- .../components/ui/multi-level-dropdown.tsx | 2 +- apps/app/styles/command-pallette.css | 4 +- 9 files changed, 177 insertions(+), 40 deletions(-) create mode 100644 apps/app/components/inbox/accept-issue-modal.tsx diff --git a/apps/app/components/command-palette/command-pallette.tsx b/apps/app/components/command-palette/command-pallette.tsx index cb2330b24..32634f18c 100644 --- a/apps/app/components/command-palette/command-pallette.tsx +++ b/apps/app/components/command-palette/command-pallette.tsx @@ -9,6 +9,7 @@ import { ChatBubbleOvalLeftEllipsisIcon, DocumentTextIcon, FolderPlusIcon, + InboxIcon, LinkIcon, MagnifyingGlassIcon, RocketLaunchIcon, @@ -34,6 +35,7 @@ import { Dialog, Transition } from "@headlessui/react"; // cmdk import { Command } from "cmdk"; // hooks +import useProjectDetails from "hooks/use-project-details"; import useTheme from "hooks/use-theme"; import useToast from "hooks/use-toast"; import useUser from "hooks/use-user"; @@ -64,10 +66,11 @@ import { // services import issuesService from "services/issues.service"; import workspaceService from "services/workspace.service"; +import inboxService from "services/inbox.service"; // types import { IIssue, IWorkspaceSearchResults } from "types"; // fetch keys -import { ISSUE_DETAILS, PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys"; +import { INBOX_LIST, ISSUE_DETAILS, PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys"; export const CommandPalette: React.FC = () => { const [isPaletteOpen, setIsPaletteOpen] = useState(false); @@ -105,6 +108,8 @@ export const CommandPalette: React.FC = () => { const { workspaceSlug, projectId, issueId, inboxId } = router.query; const { user } = useUser(); + const { projectDetails } = useProjectDetails(); + const { setToastAlert } = useToast(); const { toggleCollapsed } = useTheme(); @@ -116,6 +121,13 @@ export const CommandPalette: React.FC = () => { : null ); + const { data: inboxList } = useSWR( + workspaceSlug && projectId ? INBOX_LIST(projectId as string) : null, + workspaceSlug && projectId + ? () => inboxService.getInboxes(workspaceSlug as string, projectId as string) + : null + ); + const updateIssue = useCallback( async (formData: Partial) => { if (!workspaceSlug || !projectId || !issueId) return; @@ -321,9 +333,9 @@ export const CommandPalette: React.FC = () => { setDeleteIssueModal(true); }; - const goToSettings = (path: string = "") => { + const redirect = (path: string) => { setIsPaletteOpen(false); - router.push(`/${workspaceSlug}/settings/${path}`); + router.push(path); }; return ( @@ -396,7 +408,7 @@ export const CommandPalette: React.FC = () => { leaveFrom="opacity-100" leaveTo="opacity-0" > -
+
@@ -409,14 +421,14 @@ export const CommandPalette: React.FC = () => { leaveFrom="opacity-100 translate-y-0 sm:scale-100" leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" > - + { if (value.toLowerCase().includes(search.toLowerCase())) return 1; return 0; }} onKeyDown={(e) => { - // when seach is empty and page is undefined + // when search is empty and page is undefined // when user tries to close the modal with esc if (e.key === "Escape" && !page && !searchTerm) { setIsPaletteOpen(false); @@ -698,6 +710,24 @@ export const CommandPalette: React.FC = () => { D + + {projectDetails && projectDetails.inbox_view && ( + + + redirect( + `/${workspaceSlug}/projects/${projectId}/inbox/${inboxList?.[0]?.id}` + ) + } + className="focus:outline-none" + > +
+ + Open inbox +
+
+
+ )} )} @@ -814,7 +844,7 @@ export const CommandPalette: React.FC = () => { {page === "settings" && workspaceSlug && ( <> goToSettings()} + onSelect={() => redirect(`/${workspaceSlug}/settings`)} className="focus:outline-none" >
@@ -823,7 +853,7 @@ export const CommandPalette: React.FC = () => {
goToSettings("members")} + onSelect={() => redirect(`/${workspaceSlug}/settings/members`)} className="focus:outline-none" >
@@ -832,7 +862,7 @@ export const CommandPalette: React.FC = () => {
goToSettings("billing")} + onSelect={() => redirect(`/${workspaceSlug}/settings/billing`)} className="focus:outline-none" >
@@ -841,7 +871,7 @@ export const CommandPalette: React.FC = () => {
goToSettings("integrations")} + onSelect={() => redirect(`/${workspaceSlug}/settings/integrations`)} className="focus:outline-none" >
@@ -850,12 +880,12 @@ export const CommandPalette: React.FC = () => {
goToSettings("import-export")} + onSelect={() => redirect(`/${workspaceSlug}/settings/import-export`)} className="focus:outline-none" >
- Import/ Export + Import/Export
diff --git a/apps/app/components/inbox/accept-issue-modal.tsx b/apps/app/components/inbox/accept-issue-modal.tsx new file mode 100644 index 000000000..6427c562c --- /dev/null +++ b/apps/app/components/inbox/accept-issue-modal.tsx @@ -0,0 +1,92 @@ +import React, { useState } from "react"; + +// headless ui +import { Dialog, Transition } from "@headlessui/react"; +// icons +import { CheckCircleIcon } from "@heroicons/react/24/outline"; +// ui +import { SecondaryButton, PrimaryButton } from "components/ui"; +// types +import type { IInboxIssue } from "types"; + +type Props = { + isOpen: boolean; + handleClose: () => void; + data: IInboxIssue | undefined; + onSubmit: () => Promise; +}; + +export const AcceptIssueModal: React.FC = ({ isOpen, handleClose, data, onSubmit }) => { + const [isAccepting, setIsAccepting] = useState(false); + + const onClose = () => { + setIsAccepting(false); + handleClose(); + }; + + const handleAccept = () => { + setIsAccepting(true); + + onSubmit().finally(() => setIsAccepting(false)); + }; + + return ( + + + +
+ + +
+
+ + +
+
+ + + +

Accept Issue

+
+
+ +

+ Are you sure you want to accept issue{" "} + + {data?.project_detail?.identifier}-{data?.sequence_id} + + {""}? Once accepted, this issue will be added to the project issues list. +

+
+
+ Cancel + + {isAccepting ? "Accepting..." : "Accept Issue"} + +
+
+
+
+
+
+
+
+ ); +}; diff --git a/apps/app/components/inbox/inbox-action-headers.tsx b/apps/app/components/inbox/inbox-action-headers.tsx index 5702d560b..ac144ba0b 100644 --- a/apps/app/components/inbox/inbox-action-headers.tsx +++ b/apps/app/components/inbox/inbox-action-headers.tsx @@ -18,6 +18,7 @@ import useUserAuth from "hooks/use-user-auth"; import useToast from "hooks/use-toast"; // components import { + AcceptIssueModal, DeclineIssueModal, DeleteIssueModal, FiltersDropdown, @@ -41,9 +42,9 @@ import type { IInboxIssueDetail, TInboxStatus } from "types"; import { INBOX_ISSUE_DETAILS } from "constants/fetch-keys"; export const InboxActionHeader = () => { - const [isAccepting, setIsAccepting] = useState(false); const [date, setDate] = useState(new Date()); const [selectDuplicateIssue, setSelectDuplicateIssue] = useState(false); + const [acceptIssueModal, setAcceptIssueModal] = useState(false); const [declineIssueModal, setDeclineIssueModal] = useState(false); const [deleteIssueModal, setDeleteIssueModal] = useState(false); @@ -102,14 +103,6 @@ export const InboxActionHeader = () => { }); }; - const handleAcceptIssue = () => { - setIsAccepting(true); - - markInboxStatus({ - status: 1, - }).finally(() => setIsAccepting(false)); - }; - const issue = inboxIssues?.find((issue) => issue.bridge_id === inboxIssueId); const currentIssueIndex = inboxIssues?.findIndex((issue) => issue.bridge_id === inboxIssueId) ?? 0; @@ -144,6 +137,16 @@ export const InboxActionHeader = () => { }).finally(() => setSelectDuplicateIssue(false)); }} /> + setAcceptIssueModal(false)} + data={inboxIssues?.find((i) => i.bridge_id === inboxIssueId)} + onSubmit={async () => { + await markInboxStatus({ + status: 1, + }).finally(() => setAcceptIssueModal(false)); + }} + /> setDeclineIssueModal(false)} @@ -252,11 +255,10 @@ export const InboxActionHeader = () => { setAcceptIssueModal(true)} > - {isAccepting ? "Accepting..." : "Accept"} + Accept
)} diff --git a/apps/app/components/inbox/inbox-main-content.tsx b/apps/app/components/inbox/inbox-main-content.tsx index d03368944..a75c00317 100644 --- a/apps/app/components/inbox/inbox-main-content.tsx +++ b/apps/app/components/inbox/inbox-main-content.tsx @@ -227,7 +227,9 @@ export const InboxMainContent: React.FC = () => { issueStatus === 0 && new Date(issueDetails.issue_inbox[0].snoozed_till ?? "") < new Date() ? "text-red-500 border-red-500 bg-red-500/10" - : `${inboxStatusDetails?.textColor} ${inboxStatusDetails?.bgColor} ${inboxStatusDetails?.borderColor}` + : inboxStatusDetails + ? `${inboxStatusDetails.textColor} ${inboxStatusDetails.bgColor} ${inboxStatusDetails.borderColor}` + : "" }`} > {issueStatus === -2 ? ( diff --git a/apps/app/components/inbox/index.ts b/apps/app/components/inbox/index.ts index 7cdd8ee9d..38cea0348 100644 --- a/apps/app/components/inbox/index.ts +++ b/apps/app/components/inbox/index.ts @@ -1,3 +1,4 @@ +export * from "./accept-issue-modal"; export * from "./decline-issue-modal"; export * from "./delete-issue-modal"; export * from "./filters-dropdown"; diff --git a/apps/app/components/inbox/issues-list-sidebar.tsx b/apps/app/components/inbox/issues-list-sidebar.tsx index 9f5c85db1..6126be117 100644 --- a/apps/app/components/inbox/issues-list-sidebar.tsx +++ b/apps/app/components/inbox/issues-list-sidebar.tsx @@ -11,7 +11,7 @@ export const IssuesListSidebar = () => { const router = useRouter(); const { inboxIssueId } = router.query; - const { issues: inboxIssues } = useInboxView(); + const { issues: inboxIssues, filtersLength } = useInboxView(); return (
@@ -29,7 +29,8 @@ export const IssuesListSidebar = () => {
) : (
- No issues found for the selected filters. Try changing the filters. + {filtersLength > 0 && + "No issues found for the selected filters. Try changing the filters."}
) ) : ( diff --git a/apps/app/components/issues/sidebar-select/state.tsx b/apps/app/components/issues/sidebar-select/state.tsx index 8abb362db..02d1dd5cb 100644 --- a/apps/app/components/issues/sidebar-select/state.tsx +++ b/apps/app/components/issues/sidebar-select/state.tsx @@ -27,7 +27,7 @@ type Props = { export const SidebarStateSelect: React.FC = ({ value, onChange, userAuth }) => { const router = useRouter(); - const { workspaceSlug, projectId } = router.query; + const { workspaceSlug, projectId, inboxIssueId } = router.query; const { data: stateGroups } = useSWR( workspaceSlug && projectId ? STATES_LIST(projectId as string) : null, @@ -50,15 +50,24 @@ export const SidebarStateSelect: React.FC = ({ value, onChange, userAuth
- {getStateGroupIcon( - selectedState?.group ?? "backlog", - "16", - "16", - selectedState?.color ?? "" - )} - {addSpaceIfCamelCase(selectedState?.name ?? "")} -
+ selectedState ? ( +
+ {getStateGroupIcon( + selectedState?.group ?? "backlog", + "16", + "16", + selectedState?.color ?? "" + )} + {addSpaceIfCamelCase(selectedState?.name ?? "")} +
+ ) : inboxIssueId ? ( +
+ {getStateGroupIcon("backlog", "16", "16", "#ff7700")} + Triage +
+ ) : ( + "None" + ) } value={value} onChange={onChange} diff --git a/apps/app/components/ui/multi-level-dropdown.tsx b/apps/app/components/ui/multi-level-dropdown.tsx index e93fba887..b0997972f 100644 --- a/apps/app/components/ui/multi-level-dropdown.tsx +++ b/apps/app/components/ui/multi-level-dropdown.tsx @@ -35,7 +35,7 @@ export const MultiLevelDropdown: React.FC = ({ const [openChildFor, setOpenChildFor] = useState(null); return ( - + {({ open }) => ( <>
diff --git a/apps/app/styles/command-pallette.css b/apps/app/styles/command-pallette.css index 5362f308a..a421eeba9 100644 --- a/apps/app/styles/command-pallette.css +++ b/apps/app/styles/command-pallette.css @@ -31,9 +31,9 @@ } [cmdk-item]:hover { - background-color: rgba(var(--color-bg-base)); + background-color: rgba(var(--color-bg-surface-2)); } [cmdk-item][aria-selected="true"] { - background-color: rgba(var(--color-bg-base)); + background-color: rgba(var(--color-bg-surface-2)); }