fix: command palette changes (#2465)

This commit is contained in:
sriram veeraghanta 2023-10-17 20:34:16 +05:30 committed by GitHub
parent d689c63368
commit 90776237f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 469 additions and 228 deletions

View File

@ -1,12 +1,7 @@
import React, { useCallback, useEffect, useState } from "react"; import React, { useCallback, useEffect, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import useSWR, { mutate } from "swr"; import useSWR, { mutate } from "swr";
// cmdk
import { Command } from "cmdk"; import { Command } from "cmdk";
// headless ui
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
// services // services
import { WorkspaceService } from "services/workspace.service"; import { WorkspaceService } from "services/workspace.service";
@ -60,16 +55,20 @@ import { ISSUE_DETAILS, PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys";
type Props = { type Props = {
deleteIssue: () => void; deleteIssue: () => void;
isPaletteOpen: boolean; isPaletteOpen: boolean;
setIsPaletteOpen: React.Dispatch<React.SetStateAction<boolean>>; closePalette: () => void;
}; };
// services // services
const workspaceService = new WorkspaceService(); const workspaceService = new WorkspaceService();
const issueService = new IssueService(); const issueService = new IssueService();
export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPaletteOpen }) => { export const CommandModal: React.FC<Props> = (props) => {
const { deleteIssue, isPaletteOpen, closePalette } = props;
// router
const router = useRouter();
const { workspaceSlug, projectId, issueId } = router.query;
// states
const [placeholder, setPlaceholder] = useState("Type a command or search..."); const [placeholder, setPlaceholder] = useState("Type a command or search...");
const [resultsCount, setResultsCount] = useState(0); const [resultsCount, setResultsCount] = useState(0);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [isSearching, setIsSearching] = useState(false); const [isSearching, setIsSearching] = useState(false);
@ -86,15 +85,12 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
}, },
}); });
const [isWorkspaceLevel, setIsWorkspaceLevel] = useState(false); const [isWorkspaceLevel, setIsWorkspaceLevel] = useState(false);
const [pages, setPages] = useState<string[]>([]); const [pages, setPages] = useState<string[]>([]);
const page = pages[pages.length - 1]; const page = pages[pages.length - 1];
const debouncedSearchTerm = useDebounce(searchTerm, 500); const debouncedSearchTerm = useDebounce(searchTerm, 500);
const router = useRouter();
const { workspaceSlug, projectId, issueId } = router.query;
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
const { user } = useUser(); const { user } = useUser();
@ -141,7 +137,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
const handleIssueAssignees = (assignee: string) => { const handleIssueAssignees = (assignee: string) => {
if (!issueDetails) return; if (!issueDetails) return;
setIsPaletteOpen(false); closePalette();
const updatedAssignees = issueDetails.assignees ?? []; const updatedAssignees = issueDetails.assignees ?? [];
if (updatedAssignees.includes(assignee)) { if (updatedAssignees.includes(assignee)) {
@ -153,12 +149,12 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
}; };
const redirect = (path: string) => { const redirect = (path: string) => {
setIsPaletteOpen(false); closePalette();
router.push(path); router.push(path);
}; };
const createNewWorkspace = () => { const createNewWorkspace = () => {
setIsPaletteOpen(false); closePalette();
router.push("/create-workspace"); router.push("/create-workspace");
}; };
@ -236,7 +232,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
}} }}
as={React.Fragment} as={React.Fragment}
> >
<Dialog as="div" className="relative z-30" onClose={() => setIsPaletteOpen(false)}> <Dialog as="div" className="relative z-30" onClose={() => closePalette()}>
<Transition.Child <Transition.Child
as={React.Fragment} as={React.Fragment}
enter="ease-out duration-300" enter="ease-out duration-300"
@ -269,7 +265,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
// when search is empty and page is undefined // when search is empty and page is undefined
// when user tries to close the modal with esc // when user tries to close the modal with esc
if (e.key === "Escape" && !page && !searchTerm) { if (e.key === "Escape" && !page && !searchTerm) {
setIsPaletteOpen(false); closePalette();
} }
// Escape goes to previous page // Escape goes to previous page
// Backspace goes to previous page when search is empty // Backspace goes to previous page when search is empty
@ -365,7 +361,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
<Command.Item <Command.Item
key={item.id} key={item.id}
onSelect={() => { onSelect={() => {
setIsPaletteOpen(false); closePalette();
router.push(currentSection.path(item)); router.push(currentSection.path(item));
}} }}
value={`${key}-${item?.name}`} value={`${key}-${item?.name}`}
@ -388,7 +384,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
<Command.Group heading="Issue actions"> <Command.Group heading="Issue actions">
<Command.Item <Command.Item
onSelect={() => { onSelect={() => {
setIsPaletteOpen(false); closePalette();
setPlaceholder("Change state..."); setPlaceholder("Change state...");
setSearchTerm(""); setSearchTerm("");
setPages([...pages, "change-issue-state"]); setPages([...pages, "change-issue-state"]);
@ -455,7 +451,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
</Command.Item> </Command.Item>
<Command.Item <Command.Item
onSelect={() => { onSelect={() => {
setIsPaletteOpen(false); closePalette();
copyIssueUrlToClipboard(); copyIssueUrlToClipboard();
}} }}
className="focus:outline-none" className="focus:outline-none"
@ -470,7 +466,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
<Command.Group heading="Issue"> <Command.Group heading="Issue">
<Command.Item <Command.Item
onSelect={() => { onSelect={() => {
setIsPaletteOpen(false); closePalette();
const e = new KeyboardEvent("keydown", { const e = new KeyboardEvent("keydown", {
key: "c", key: "c",
}); });
@ -490,7 +486,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
<Command.Group heading="Project"> <Command.Group heading="Project">
<Command.Item <Command.Item
onSelect={() => { onSelect={() => {
setIsPaletteOpen(false); closePalette();
const e = new KeyboardEvent("keydown", { const e = new KeyboardEvent("keydown", {
key: "p", key: "p",
}); });
@ -512,7 +508,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
<Command.Group heading="Cycle"> <Command.Group heading="Cycle">
<Command.Item <Command.Item
onSelect={() => { onSelect={() => {
setIsPaletteOpen(false); closePalette();
const e = new KeyboardEvent("keydown", { const e = new KeyboardEvent("keydown", {
key: "q", key: "q",
}); });
@ -530,7 +526,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
<Command.Group heading="Module"> <Command.Group heading="Module">
<Command.Item <Command.Item
onSelect={() => { onSelect={() => {
setIsPaletteOpen(false); closePalette();
const e = new KeyboardEvent("keydown", { const e = new KeyboardEvent("keydown", {
key: "m", key: "m",
}); });
@ -548,7 +544,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
<Command.Group heading="View"> <Command.Group heading="View">
<Command.Item <Command.Item
onSelect={() => { onSelect={() => {
setIsPaletteOpen(false); closePalette();
const e = new KeyboardEvent("keydown", { const e = new KeyboardEvent("keydown", {
key: "v", key: "v",
}); });
@ -566,7 +562,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
<Command.Group heading="Page"> <Command.Group heading="Page">
<Command.Item <Command.Item
onSelect={() => { onSelect={() => {
setIsPaletteOpen(false); closePalette();
const e = new KeyboardEvent("keydown", { const e = new KeyboardEvent("keydown", {
key: "d", key: "d",
}); });
@ -623,7 +619,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
<Command.Group heading="Help"> <Command.Group heading="Help">
<Command.Item <Command.Item
onSelect={() => { onSelect={() => {
setIsPaletteOpen(false); closePalette();
const e = new KeyboardEvent("keydown", { const e = new KeyboardEvent("keydown", {
key: "h", key: "h",
}); });
@ -638,7 +634,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
</Command.Item> </Command.Item>
<Command.Item <Command.Item
onSelect={() => { onSelect={() => {
setIsPaletteOpen(false); closePalette();
window.open("https://docs.plane.so/", "_blank"); window.open("https://docs.plane.so/", "_blank");
}} }}
className="focus:outline-none" className="focus:outline-none"
@ -650,7 +646,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
</Command.Item> </Command.Item>
<Command.Item <Command.Item
onSelect={() => { onSelect={() => {
setIsPaletteOpen(false); closePalette();
window.open("https://discord.com/invite/A92xrEGCge", "_blank"); window.open("https://discord.com/invite/A92xrEGCge", "_blank");
}} }}
className="focus:outline-none" className="focus:outline-none"
@ -662,7 +658,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
</Command.Item> </Command.Item>
<Command.Item <Command.Item
onSelect={() => { onSelect={() => {
setIsPaletteOpen(false); closePalette();
window.open("https://github.com/makeplane/plane/issues/new/choose", "_blank"); window.open("https://github.com/makeplane/plane/issues/new/choose", "_blank");
}} }}
className="focus:outline-none" className="focus:outline-none"
@ -674,7 +670,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
</Command.Item> </Command.Item>
<Command.Item <Command.Item
onSelect={() => { onSelect={() => {
setIsPaletteOpen(false); closePalette();
(window as any).$crisp.push(["do", "chat:open"]); (window as any).$crisp.push(["do", "chat:open"]);
}} }}
className="focus:outline-none" className="focus:outline-none"
@ -747,15 +743,15 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
</> </>
)} )}
{page === "change-issue-state" && issueDetails && ( {page === "change-issue-state" && issueDetails && (
<ChangeIssueState issue={issueDetails} setIsPaletteOpen={setIsPaletteOpen} user={user} /> <ChangeIssueState issue={issueDetails} setIsPaletteOpen={closePalette} user={user} />
)} )}
{page === "change-issue-priority" && issueDetails && ( {page === "change-issue-priority" && issueDetails && (
<ChangeIssuePriority issue={issueDetails} setIsPaletteOpen={setIsPaletteOpen} user={user} /> <ChangeIssuePriority issue={issueDetails} setIsPaletteOpen={closePalette} user={user} />
)} )}
{page === "change-issue-assignee" && issueDetails && ( {page === "change-issue-assignee" && issueDetails && (
<ChangeIssueAssignee issue={issueDetails} setIsPaletteOpen={setIsPaletteOpen} user={user} /> <ChangeIssueAssignee issue={issueDetails} setIsPaletteOpen={closePalette} user={user} />
)} )}
{page === "change-interface-theme" && <ChangeInterfaceTheme setIsPaletteOpen={setIsPaletteOpen} />} {page === "change-interface-theme" && <ChangeInterfaceTheme setIsPaletteOpen={closePalette} />}
</Command.List> </Command.List>
</Command> </Command>
</Dialog.Panel> </Dialog.Panel>

View File

@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useState } from "react"; import React, { useCallback, useEffect, FC } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import useSWR from "swr"; import useSWR from "swr";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
@ -6,7 +6,7 @@ import { observer } from "mobx-react-lite";
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
import useUser from "hooks/use-user"; import useUser from "hooks/use-user";
// components // components
import { CommandK, ShortcutsModal } from "components/command-palette"; import { CommandModal, ShortcutsModal } from "components/command-palette";
import { BulkDeleteIssuesModal } from "components/core"; import { BulkDeleteIssuesModal } from "components/core";
import { CreateUpdateCycleModal } from "components/cycles"; import { CreateUpdateCycleModal } from "components/cycles";
import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues"; import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
@ -26,22 +26,34 @@ import { useMobxStore } from "lib/mobx/store-provider";
// services // services
const issueService = new IssueService(); const issueService = new IssueService();
export const CommandPalette: React.FC = observer(() => { export const CommandPalette: FC = observer(() => {
const store: any = useMobxStore();
const [isPaletteOpen, setIsPaletteOpen] = useState(false);
const [isIssueModalOpen, setIsIssueModalOpen] = useState(false);
const [isProjectModalOpen, setIsProjectModalOpen] = useState(false);
const [isShortcutsModalOpen, setIsShortcutsModalOpen] = useState(false);
const [isCreateCycleModalOpen, setIsCreateCycleModalOpen] = useState(false);
const [isCreateViewModalOpen, setIsCreateViewModalOpen] = useState(false);
const [isCreateModuleModalOpen, setIsCreateModuleModalOpen] = useState(false);
const [isBulkDeleteIssuesModalOpen, setIsBulkDeleteIssuesModalOpen] = useState(false);
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
const [isCreateUpdatePageModalOpen, setIsCreateUpdatePageModalOpen] = useState(false);
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, issueId, cycleId, moduleId } = router.query; const { workspaceSlug, projectId, issueId, cycleId, moduleId } = router.query;
// store
const { commandPalette, theme: themeStore } = useMobxStore();
const {
isCommandPaletteOpen,
toggleCommandPaletteModal,
isCreateIssueModalOpen,
toggleCreateIssueModal,
isCreateCycleModalOpen,
toggleCreateCycleModal,
isCreatePageModalOpen,
toggleCreatePageModal,
isCreateProjectModalOpen,
toggleCreateProjectModal,
isCreateModuleModalOpen,
toggleCreateModuleModal,
isCreateViewModalOpen,
toggleCreateViewModal,
isShortcutModalOpen,
toggleShortcutModal,
isBulkDeleteIssueModalOpen,
toggleBulkDeleteIssueModal,
isDeleteIssueModalOpen,
toggleDeleteIssueModal,
} = commandPalette;
const { setSidebarCollapsed } = themeStore;
const { user } = useUser(); const { user } = useUser();
@ -55,7 +67,7 @@ export const CommandPalette: React.FC = observer(() => {
); );
const copyIssueUrlToClipboard = useCallback(() => { const copyIssueUrlToClipboard = useCallback(() => {
if (!router.query.issueId) return; if (!issueId) return;
const url = new URL(window.location.href); const url = new URL(window.location.href);
copyTextToClipboard(url.href) copyTextToClipboard(url.href)
@ -71,7 +83,7 @@ export const CommandPalette: React.FC = observer(() => {
title: "Some error occurred", title: "Some error occurred",
}); });
}); });
}, [router, setToastAlert]); }, [setToastAlert, issueId]);
const handleKeyDown = useCallback( const handleKeyDown = useCallback(
(e: KeyboardEvent) => { (e: KeyboardEvent) => {
@ -91,101 +103,142 @@ export const CommandPalette: React.FC = observer(() => {
if (cmdClicked) { if (cmdClicked) {
if (keyPressed === "k") { if (keyPressed === "k") {
e.preventDefault(); e.preventDefault();
setIsPaletteOpen(true); toggleCommandPaletteModal(true);
} else if (keyPressed === "c" && altKey) { } else if (keyPressed === "c" && altKey) {
e.preventDefault(); e.preventDefault();
copyIssueUrlToClipboard(); copyIssueUrlToClipboard();
} else if (keyPressed === "b") { } else if (keyPressed === "b") {
e.preventDefault(); e.preventDefault();
store.theme.setSidebarCollapsed(!store?.theme?.sidebarCollapsed); setSidebarCollapsed();
} }
} else { } else {
if (keyPressed === "c") { if (keyPressed === "c") {
setIsIssueModalOpen(true); toggleCreateIssueModal(true);
} else if (keyPressed === "p") { } else if (keyPressed === "p") {
setIsProjectModalOpen(true); toggleCreateProjectModal(true);
} else if (keyPressed === "v") { } else if (keyPressed === "v") {
setIsCreateViewModalOpen(true); toggleCreateViewModal(true);
} else if (keyPressed === "d") { } else if (keyPressed === "d") {
setIsCreateUpdatePageModalOpen(true); toggleCreatePageModal(true);
} else if (keyPressed === "h") { } else if (keyPressed === "h") {
setIsShortcutsModalOpen(true); toggleShortcutModal(true);
} else if (keyPressed === "q") { } else if (keyPressed === "q") {
setIsCreateCycleModalOpen(true); toggleCreateCycleModal(true);
} else if (keyPressed === "m") { } else if (keyPressed === "m") {
setIsCreateModuleModalOpen(true); toggleCreateModuleModal(true);
} else if (keyPressed === "backspace" || keyPressed === "delete") { } else if (keyPressed === "backspace" || keyPressed === "delete") {
e.preventDefault(); e.preventDefault();
setIsBulkDeleteIssuesModalOpen(true); toggleBulkDeleteIssueModal(true);
} }
} }
}, },
[copyIssueUrlToClipboard, store.theme] [
copyIssueUrlToClipboard,
toggleCreateProjectModal,
toggleCreateViewModal,
toggleCreatePageModal,
toggleShortcutModal,
toggleCreateCycleModal,
toggleCreateModuleModal,
toggleBulkDeleteIssueModal,
toggleCommandPaletteModal,
setSidebarCollapsed,
toggleCreateIssueModal,
]
); );
useEffect(() => { useEffect(() => {
document.addEventListener("keydown", handleKeyDown); document.addEventListener("keydown", handleKeyDown);
return () => document.removeEventListener("keydown", handleKeyDown); return () => document.removeEventListener("keydown", handleKeyDown);
}, [handleKeyDown]); }, [handleKeyDown]);
if (!user) return null; if (!user) return null;
const deleteIssue = () => { const deleteIssue = () => {
setIsPaletteOpen(false); toggleCommandPaletteModal(false);
setDeleteIssueModal(true); toggleDeleteIssueModal(true);
}; };
return ( return (
<> <>
<ShortcutsModal isOpen={isShortcutsModalOpen} setIsOpen={setIsShortcutsModalOpen} /> <ShortcutsModal
isOpen={isShortcutModalOpen}
onClose={() => {
toggleShortcutModal(false);
}}
/>
{workspaceSlug && ( {workspaceSlug && (
<CreateProjectModal isOpen={isProjectModalOpen} setIsOpen={setIsProjectModalOpen} user={user} /> <CreateProjectModal
isOpen={isCreateProjectModalOpen}
onClose={() => {
toggleCreateProjectModal(false);
}}
workspaceSlug={workspaceSlug.toString()}
/>
)} )}
{projectId && ( {workspaceSlug && projectId && (
<> <>
<CreateUpdateCycleModal <CreateUpdateCycleModal
isOpen={isCreateCycleModalOpen} isOpen={isCreateCycleModalOpen}
handleClose={() => setIsCreateCycleModalOpen(false)} handleClose={() => toggleCreateCycleModal(false)}
user={user} workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
/> />
<CreateUpdateModuleModal <CreateUpdateModuleModal
isOpen={isCreateModuleModalOpen} isOpen={isCreateModuleModalOpen}
setIsOpen={setIsCreateModuleModalOpen} onClose={() => {
user={user} toggleCreateModuleModal(false);
}}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
/> />
<CreateUpdateProjectViewModal <CreateUpdateProjectViewModal
isOpen={isCreateViewModalOpen} isOpen={isCreateViewModalOpen}
onClose={() => setIsCreateViewModalOpen(false)} onClose={() => toggleCreateViewModal(false)}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
/> />
<CreateUpdatePageModal <CreateUpdatePageModal
isOpen={isCreateUpdatePageModalOpen} isOpen={isCreatePageModalOpen}
handleClose={() => setIsCreateUpdatePageModalOpen(false)} handleClose={() => toggleCreatePageModal(false)}
user={user} user={user}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
/> />
</> </>
)} )}
{issueId && issueDetails && (
<DeleteIssueModal
handleClose={() => setDeleteIssueModal(false)}
isOpen={deleteIssueModal}
data={issueDetails}
user={user}
/>
)}
<CreateUpdateIssueModal <CreateUpdateIssueModal
isOpen={isIssueModalOpen} isOpen={isCreateIssueModalOpen}
handleClose={() => setIsIssueModalOpen(false)} handleClose={() => toggleCreateIssueModal(false)}
prePopulateData={ prePopulateData={
cycleId ? { cycle: cycleId.toString() } : moduleId ? { module: moduleId.toString() } : undefined cycleId ? { cycle: cycleId.toString() } : moduleId ? { module: moduleId.toString() } : undefined
} }
/> />
{issueId && issueDetails && (
<DeleteIssueModal
handleClose={() => toggleDeleteIssueModal(false)}
isOpen={isDeleteIssueModalOpen}
data={issueDetails}
user={user}
/>
)}
<BulkDeleteIssuesModal <BulkDeleteIssuesModal
isOpen={isBulkDeleteIssuesModalOpen} isOpen={isBulkDeleteIssueModalOpen}
setIsOpen={setIsBulkDeleteIssuesModalOpen} onClose={() => {
toggleBulkDeleteIssueModal(false);
}}
user={user} user={user}
/> />
<CommandK deleteIssue={deleteIssue} isPaletteOpen={isPaletteOpen} setIsPaletteOpen={setIsPaletteOpen} /> <CommandModal
deleteIssue={deleteIssue}
isPaletteOpen={isCommandPaletteOpen}
closePalette={() => {
toggleCommandPaletteModal(false);
}}
/>
</> </>
); );
}); });

View File

@ -1,6 +1,6 @@
export * from "./issue"; export * from "./issue";
export * from "./change-interface-theme"; export * from "./change-interface-theme";
export * from "./command-k"; export * from "./command-modal";
export * from "./command-pallette"; export * from "./command-pallette";
export * from "./helpers"; export * from "./helpers";
export * from "./shortcuts-modal"; export * from "./shortcuts-modal";

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react"; import { FC, useEffect, useState, Dispatch, SetStateAction, Fragment } from "react";
// headless ui // headless ui
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
// icons // icons
@ -8,7 +8,7 @@ import { Input } from "@plane/ui";
type Props = { type Props = {
isOpen: boolean; isOpen: boolean;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>; onClose: () => void;
}; };
const shortcuts = [ const shortcuts = [
@ -43,8 +43,11 @@ const shortcuts = [
const allShortcuts = shortcuts.map((i) => i.shortcuts).flat(1); const allShortcuts = shortcuts.map((i) => i.shortcuts).flat(1);
export const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => { export const ShortcutsModal: FC<Props> = (props) => {
const { isOpen, onClose } = props;
// states
const [query, setQuery] = useState(""); const [query, setQuery] = useState("");
// computed
const filteredShortcuts = allShortcuts.filter((shortcut) => const filteredShortcuts = allShortcuts.filter((shortcut) =>
shortcut.description.toLowerCase().includes(query.trim().toLowerCase()) || query === "" ? true : false shortcut.description.toLowerCase().includes(query.trim().toLowerCase()) || query === "" ? true : false
); );
@ -54,10 +57,10 @@ export const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
}, [isOpen]); }, [isOpen]);
return ( return (
<Transition.Root show={isOpen} as={React.Fragment}> <Transition.Root show={isOpen} as={Fragment}>
<Dialog as="div" className="relative z-20" onClose={setIsOpen}> <Dialog as="div" className="relative z-20" onClose={onClose}>
<Transition.Child <Transition.Child
as={React.Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-out duration-300"
enterFrom="opacity-0" enterFrom="opacity-0"
enterTo="opacity-100" enterTo="opacity-100"
@ -71,7 +74,7 @@ export const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
<div className="fixed inset-0 z-20 overflow-y-auto"> <div className="fixed inset-0 z-20 overflow-y-auto">
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0"> <div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<Transition.Child <Transition.Child
as={React.Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100" enterTo="opacity-100 translate-y-0 sm:scale-100"
@ -89,7 +92,7 @@ export const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
> >
<span>Keyboard Shortcuts</span> <span>Keyboard Shortcuts</span>
<span> <span>
<button type="button" onClick={() => setIsOpen(false)}> <button type="button" onClick={onClose}>
<X className="h-6 w-6 text-custom-text-200 hover:text-custom-text-100" aria-hidden="true" /> <X className="h-6 w-6 text-custom-text-200 hover:text-custom-text-100" aria-hidden="true" />
</button> </button>
</span> </span>

View File

@ -33,18 +33,20 @@ type FormInput = {
type Props = { type Props = {
isOpen: boolean; isOpen: boolean;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>; onClose: () => void;
user: IUser | undefined; user: IUser | undefined;
}; };
const issueService = new IssueService(); const issueService = new IssueService();
export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen, user }) => { export const BulkDeleteIssuesModal: React.FC<Props> = (props) => {
const [query, setQuery] = useState(""); const { isOpen, onClose, user } = props;
// router
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query; const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
// states
const [query, setQuery] = useState("");
// fetching project issues.
const { data: issues } = useSWR( const { data: issues } = useSWR(
workspaceSlug && projectId ? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string) : null, workspaceSlug && projectId ? PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string) : null,
workspaceSlug && projectId ? () => issueService.getIssues(workspaceSlug as string, projectId as string) : null workspaceSlug && projectId ? () => issueService.getIssues(workspaceSlug as string, projectId as string) : null
@ -68,9 +70,9 @@ export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen, user
}); });
const handleClose = () => { const handleClose = () => {
setIsOpen(false);
setQuery(""); setQuery("");
reset(); reset();
onClose();
}; };
const handleDelete: SubmitHandler<FormInput> = async (data) => { const handleDelete: SubmitHandler<FormInput> = async (data) => {

View File

@ -1,5 +1,4 @@
import { Fragment } from "react"; import { Fragment } from "react";
import { useRouter } from "next/router";
import { mutate } from "swr"; import { mutate } from "swr";
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
// services // services
@ -11,7 +10,7 @@ import { CycleForm } from "components/cycles";
// helper // helper
import { getDateRangeStatus } from "helpers/date-time.helper"; import { getDateRangeStatus } from "helpers/date-time.helper";
// types // types
import type { CycleDateCheckData, IUser, ICycle, IProject } from "types"; import type { CycleDateCheckData, ICycle, IProject, IUser } from "types";
// fetch keys // fetch keys
import { import {
COMPLETED_CYCLES_LIST, COMPLETED_CYCLES_LIST,
@ -27,23 +26,21 @@ type CycleModalProps = {
isOpen: boolean; isOpen: boolean;
handleClose: () => void; handleClose: () => void;
data?: ICycle | null; data?: ICycle | null;
user: IUser | undefined; workspaceSlug: string;
projectId: string;
}; };
// services // services
const cycleService = new CycleService(); const cycleService = new CycleService();
export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({ isOpen, handleClose, data, user }) => { export const CreateUpdateCycleModal: React.FC<CycleModalProps> = (props) => {
const router = useRouter(); const { isOpen, handleClose, data, workspaceSlug, projectId } = props;
const { workspaceSlug, projectId } = router.query;
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
const createCycle = async (payload: Partial<ICycle>) => { const createCycle = async (payload: Partial<ICycle>) => {
if (!workspaceSlug || !projectId) return;
await cycleService await cycleService
.createCycle(workspaceSlug.toString(), projectId.toString(), payload, user) .createCycle(workspaceSlug.toString(), projectId.toString(), payload, {} as IUser)
.then((res) => { .then((res) => {
switch (getDateRangeStatus(res.start_date, res.end_date)) { switch (getDateRangeStatus(res.start_date, res.end_date)) {
case "completed": case "completed":
@ -91,10 +88,8 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({ isOpen, hand
}; };
const updateCycle = async (cycleId: string, payload: Partial<ICycle>) => { const updateCycle = async (cycleId: string, payload: Partial<ICycle>) => {
if (!workspaceSlug || !projectId) return;
await cycleService await cycleService
.updateCycle(workspaceSlug.toString(), projectId.toString(), cycleId, payload, user) .updateCycle(workspaceSlug.toString(), projectId.toString(), cycleId, payload, {} as IUser)
.then((res) => { .then((res) => {
switch (getDateRangeStatus(data?.start_date, data?.end_date)) { switch (getDateRangeStatus(data?.start_date, data?.end_date)) {
case "completed": case "completed":
@ -177,7 +172,6 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({ isOpen, hand
if (isDateValid) { if (isDateValid) {
if (data) await updateCycle(data.id, payload); if (data) await updateCycle(data.id, payload);
else await createCycle(payload); else await createCycle(payload);
handleClose(); handleClose();
} else } else
setToastAlert({ setToastAlert({

View File

@ -50,7 +50,14 @@ export const CycleSelect: React.FC<IssueCycleSelectProps> = ({ projectId, value,
return ( return (
<> <>
<CreateUpdateCycleModal isOpen={isCycleModalActive} handleClose={closeCycleModal} user={user} /> {workspaceSlug && projectId && (
<CreateUpdateCycleModal
isOpen={isCycleModalActive}
handleClose={closeCycleModal}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
/>
)}
<Listbox as="div" className="relative" value={value} onChange={onChange} multiple={multiple}> <Listbox as="div" className="relative" value={value} onChange={onChange} multiple={multiple}>
{({ open }) => ( {({ open }) => (
<> <>

View File

@ -19,13 +19,20 @@ export const ProjectViewsHeader: FC<IProjectViewsHeader> = (props) => {
const { title } = props; const { title } = props;
// router // router
const router = useRouter(); const router = useRouter();
const { workspaceSlug } = router.query; const { workspaceSlug, projectId } = router.query;
// states // states
const [createViewModal, setCreateViewModal] = useState(false); const [createViewModal, setCreateViewModal] = useState(false);
return ( return (
<> <>
<CreateUpdateProjectViewModal isOpen={createViewModal} onClose={() => setCreateViewModal(false)} /> {workspaceSlug && projectId && (
<CreateUpdateProjectViewModal
isOpen={createViewModal}
onClose={() => setCreateViewModal(false)}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
/>
)}
<div <div
className={`relative flex w-full flex-shrink-0 flex-row z-10 items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4`} className={`relative flex w-full flex-shrink-0 flex-row z-10 items-center justify-between gap-x-2 gap-y-4 border-b border-custom-border-200 bg-custom-sidebar-background-100 p-4`}
> >

View File

@ -1,5 +1,4 @@
import { Fragment } from "react"; import { Fragment } from "react";
import { useRouter } from "next/router";
import { mutate } from "swr"; import { mutate } from "swr";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
@ -16,9 +15,10 @@ import { MODULE_LIST } from "constants/fetch-keys";
type Props = { type Props = {
isOpen: boolean; isOpen: boolean;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>; onClose: () => void;
data?: IModule; data?: IModule;
user: IUser | undefined; workspaceSlug: string;
projectId: string;
}; };
const defaultValues: Partial<IModule> = { const defaultValues: Partial<IModule> = {
@ -31,15 +31,14 @@ const defaultValues: Partial<IModule> = {
const moduleService = new ModuleService(); const moduleService = new ModuleService();
export const CreateUpdateModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, data, user }) => { export const CreateUpdateModuleModal: React.FC<Props> = (props) => {
const router = useRouter(); const { isOpen, onClose, data, workspaceSlug, projectId } = props;
const { workspaceSlug, projectId } = router.query;
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
const handleClose = () => { const handleClose = () => {
setIsOpen(false);
reset(defaultValues); reset(defaultValues);
onClose();
}; };
const { reset } = useForm<IModule>({ const { reset } = useForm<IModule>({
@ -48,7 +47,7 @@ export const CreateUpdateModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, da
const createModule = async (payload: Partial<IModule>) => { const createModule = async (payload: Partial<IModule>) => {
await moduleService await moduleService
.createModule(workspaceSlug as string, projectId as string, payload, user) .createModule(workspaceSlug as string, projectId as string, payload, {} as IUser)
.then(() => { .then(() => {
mutate(MODULE_LIST(projectId as string)); mutate(MODULE_LIST(projectId as string));
handleClose(); handleClose();
@ -70,7 +69,7 @@ export const CreateUpdateModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, da
const updateModule = async (payload: Partial<IModule>) => { const updateModule = async (payload: Partial<IModule>) => {
await moduleService await moduleService
.updateModule(workspaceSlug as string, projectId as string, data?.id ?? "", payload, user) .updateModule(workspaceSlug as string, projectId as string, data?.id ?? "", payload, {} as IUser)
.then((res) => { .then((res) => {
mutate<IModule[]>( mutate<IModule[]>(
MODULE_LIST(projectId as string), MODULE_LIST(projectId as string),

View File

@ -22,14 +22,17 @@ type Props = {
handleClose: () => void; handleClose: () => void;
data?: IPage | null; data?: IPage | null;
user: IUser | undefined; user: IUser | undefined;
workspaceSlug: string;
projectId: string;
}; };
// services // services
const pageService = new PageService(); const pageService = new PageService();
export const CreateUpdatePageModal: React.FC<Props> = ({ isOpen, handleClose, data, user }) => { export const CreateUpdatePageModal: React.FC<Props> = (props) => {
const { isOpen, handleClose, data, user, workspaceSlug, projectId } = props;
// router
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();

View File

@ -38,14 +38,14 @@ const pageService = new PageService();
const projectService = new ProjectService(); const projectService = new ProjectService();
export const PagesView: React.FC<Props> = ({ pages, viewType }) => { export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
const [createUpdatePageModal, setCreateUpdatePageModal] = useState(false); // router
const [selectedPageToUpdate, setSelectedPageToUpdate] = useState<IPage | null>(null);
const [deletePageModal, setDeletePageModal] = useState(false);
const [selectedPageToDelete, setSelectedPageToDelete] = useState<IPage | null>(null);
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug, projectId } = router.query;
// states
const [createUpdatePageModal, setCreateUpdatePageModal] = useState(false);
const [selectedPageToUpdate, setSelectedPageToUpdate] = useState<IPage | null>(null);
const [deletePageModal, setDeletePageModal] = useState(false);
const [selectedPageToDelete, setSelectedPageToDelete] = useState<IPage | null>(null);
const { user } = useUserAuth(); const { user } = useUserAuth();
@ -188,18 +188,25 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
return ( return (
<> <>
<CreateUpdatePageModal {workspaceSlug && projectId && (
isOpen={createUpdatePageModal} <>
handleClose={() => setCreateUpdatePageModal(false)} <CreateUpdatePageModal
data={selectedPageToUpdate} isOpen={createUpdatePageModal}
user={user} handleClose={() => setCreateUpdatePageModal(false)}
/> data={selectedPageToUpdate}
<DeletePageModal user={user}
isOpen={deletePageModal} workspaceSlug={workspaceSlug.toString()}
setIsOpen={setDeletePageModal} projectId={projectId.toString()}
data={selectedPageToDelete} />
user={user} <DeletePageModal
/> isOpen={deletePageModal}
setIsOpen={setDeletePageModal}
data={selectedPageToDelete}
user={user}
/>
</>
)}
{pages ? ( {pages ? (
<div className="space-y-4 h-full overflow-y-auto"> <div className="space-y-4 h-full overflow-y-auto">
{pages.length > 0 ? ( {pages.length > 0 ? (

View File

@ -1,10 +1,11 @@
import { useState, useEffect, Fragment } from "react"; import { useState, useEffect, Fragment, FC } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useForm, Controller } from "react-hook-form"; import { useForm, Controller } from "react-hook-form";
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
// icons // icons
import { Users2, X } from "lucide-react"; import { Users2, X } from "lucide-react";
// hooks // hooks
import { useMobxStore } from "lib/mobx/store-provider";
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
import { useWorkspaceMyMembership } from "contexts/workspace-member.context"; import { useWorkspaceMyMembership } from "contexts/workspace-member.context";
import useWorkspaceMembers from "hooks/use-workspace-members"; import useWorkspaceMembers from "hooks/use-workspace-members";
@ -17,16 +18,15 @@ import EmojiIconPicker from "components/emoji-icon-picker";
// helpers // helpers
import { getRandomEmoji, renderEmoji } from "helpers/emoji.helper"; import { getRandomEmoji, renderEmoji } from "helpers/emoji.helper";
// types // types
import { IUser, IProject } from "types"; import { IProject } from "types";
// constants // constants
import { NETWORK_CHOICES } from "constants/project"; import { NETWORK_CHOICES } from "constants/project";
import { useMobxStore } from "lib/mobx/store-provider";
type Props = { type Props = {
isOpen: boolean; isOpen: boolean;
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>; onClose: () => void;
setToFavorite?: boolean; setToFavorite?: boolean;
user: IUser | undefined; workspaceSlug: string;
}; };
const defaultValues: Partial<IProject> = { const defaultValues: Partial<IProject> = {
@ -40,26 +40,27 @@ const defaultValues: Partial<IProject> = {
project_lead: null, project_lead: null,
}; };
const IsGuestCondition: React.FC<{ interface IIsGuestCondition {
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>; onClose: () => void;
}> = ({ setIsOpen }) => { }
const IsGuestCondition: FC<IIsGuestCondition> = ({ onClose }) => {
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
useEffect(() => { useEffect(() => {
setIsOpen(false); onClose();
setToastAlert({ setToastAlert({
title: "Error", title: "Error",
type: "error", type: "error",
message: "You don't have permission to create project.", message: "You don't have permission to create project.",
}); });
}, [setIsOpen, setToastAlert]); }, [onClose, setToastAlert]);
return null; return null;
}; };
export const CreateProjectModal: React.FC<Props> = (props) => { export const CreateProjectModal: React.FC<Props> = (props) => {
const { isOpen, setIsOpen, setToFavorite = false } = props; const { isOpen, onClose, setToFavorite = false, workspaceSlug } = props;
// store // store
const { project: projectStore } = useMobxStore(); const { project: projectStore } = useMobxStore();
// states // states
@ -67,9 +68,6 @@ export const CreateProjectModal: React.FC<Props> = (props) => {
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
const router = useRouter();
const { workspaceSlug } = router.query;
const { memberDetails } = useWorkspaceMyMembership(); const { memberDetails } = useWorkspaceMyMembership();
const { workspaceMembers } = useWorkspaceMembers(workspaceSlug?.toString() ?? ""); const { workspaceMembers } = useWorkspaceMembers(workspaceSlug?.toString() ?? "");
@ -86,7 +84,7 @@ export const CreateProjectModal: React.FC<Props> = (props) => {
}); });
const handleClose = () => { const handleClose = () => {
setIsOpen(false); onClose();
setIsChangeInIdentifierRequired(true); setIsChangeInIdentifierRequired(true);
reset(defaultValues); reset(defaultValues);
}; };
@ -172,7 +170,7 @@ export const CreateProjectModal: React.FC<Props> = (props) => {
const currentNetwork = NETWORK_CHOICES.find((n) => n.key === watch("network")); const currentNetwork = NETWORK_CHOICES.find((n) => n.key === watch("network"));
if (memberDetails && isOpen) if (memberDetails.role <= 10) return <IsGuestCondition setIsOpen={setIsOpen} />; if (memberDetails && isOpen) if (memberDetails.role <= 10) return <IsGuestCondition onClose={onClose} />;
return ( return (
<Transition.Root show={isOpen} as={Fragment}> <Transition.Root show={isOpen} as={Fragment}>

View File

@ -108,12 +108,16 @@ export const ProjectSidebarList: FC = observer(() => {
return ( return (
<> <>
<CreateProjectModal {workspaceSlug && (
isOpen={isProjectModalOpen} <CreateProjectModal
setIsOpen={setIsProjectModalOpen} isOpen={isProjectModalOpen}
setToFavorite={isFavoriteProjectCreate} onClose={() => {
user={user} setIsProjectModalOpen(false);
/> }}
setToFavorite={isFavoriteProjectCreate}
workspaceSlug={workspaceSlug.toString()}
/>
)}
<div <div
ref={containerRef} ref={containerRef}
className={`h-full overflow-y-auto px-4 space-y-2 ${ className={`h-full overflow-y-auto px-4 space-y-2 ${

View File

@ -1,11 +1,8 @@
import React from "react"; import { FC, Fragment } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { Dialog, Transition } from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks // hooks
import { useMobxStore } from "lib/mobx/store-provider";
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// components // components
import { ProjectViewForm } from "components/views"; import { ProjectViewForm } from "components/views";
@ -17,31 +14,24 @@ type Props = {
isOpen: boolean; isOpen: boolean;
onClose: () => void; onClose: () => void;
preLoadedData?: Partial<IProjectView> | null; preLoadedData?: Partial<IProjectView> | null;
workspaceSlug: string;
projectId: string;
}; };
export const CreateUpdateProjectViewModal: React.FC<Props> = observer((props) => { export const CreateUpdateProjectViewModal: FC<Props> = observer((props) => {
const { data, isOpen, onClose, preLoadedData } = props; const { data, isOpen, onClose, preLoadedData, workspaceSlug, projectId } = props;
// store
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { projectViews: projectViewsStore } = useMobxStore(); const { projectViews: projectViewsStore } = useMobxStore();
// hooks
const { setToastAlert } = useToast(); const { setToastAlert } = useToast();
const handleClose = () => { const handleClose = () => {
onClose(); onClose();
}; };
const createView = async (formData: IProjectView) => { const createView = async (payload: IProjectView) => {
if (!workspaceSlug || !projectId) return;
const payload = {
...formData,
};
await projectViewsStore await projectViewsStore
.createView(workspaceSlug.toString(), projectId.toString(), payload) .createView(workspaceSlug, projectId, payload)
.then(() => handleClose()) .then(() => handleClose())
.catch(() => .catch(() =>
setToastAlert({ setToastAlert({
@ -52,15 +42,9 @@ export const CreateUpdateProjectViewModal: React.FC<Props> = observer((props) =>
); );
}; };
const updateView = async (formData: IProjectView) => { const updateView = async (payload: IProjectView) => {
if (!workspaceSlug || !projectId) return;
const payload = {
...formData,
};
await projectViewsStore await projectViewsStore
.updateView(workspaceSlug.toString(), projectId.toString(), data?.id as string, payload) .updateView(workspaceSlug, projectId, data?.id as string, payload)
.then(() => handleClose()) .then(() => handleClose())
.catch(() => .catch(() =>
setToastAlert({ setToastAlert({
@ -77,10 +61,10 @@ export const CreateUpdateProjectViewModal: React.FC<Props> = observer((props) =>
}; };
return ( return (
<Transition.Root show={isOpen} as={React.Fragment}> <Transition.Root show={isOpen} as={Fragment}>
<Dialog as="div" className="relative z-20" onClose={handleClose}> <Dialog as="div" className="relative z-20" onClose={handleClose}>
<Transition.Child <Transition.Child
as={React.Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-out duration-300"
enterFrom="opacity-0" enterFrom="opacity-0"
enterTo="opacity-100" enterTo="opacity-100"
@ -94,7 +78,7 @@ export const CreateUpdateProjectViewModal: React.FC<Props> = observer((props) =>
<div className="fixed inset-0 z-20 overflow-y-auto"> <div className="fixed inset-0 z-20 overflow-y-auto">
<div className="flex min-h-full items-center justify-center p-4 text-center sm:p-0"> <div className="flex min-h-full items-center justify-center p-4 text-center sm:p-0">
<Transition.Child <Transition.Child
as={React.Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100" enterTo="opacity-100 translate-y-0 sm:scale-100"

View File

@ -16,7 +16,7 @@ export const AppLayout: FC<IAppLayout> = (props) => {
return ( return (
<> <>
{/* <CommandPalette /> */} <CommandPalette />
<UserAuthWrapper> <UserAuthWrapper>
<WorkspaceAuthWrapper> <WorkspaceAuthWrapper>
<div className="relative flex h-screen w-full overflow-hidden"> <div className="relative flex h-screen w-full overflow-hidden">

View File

@ -66,12 +66,18 @@ const ProjectModules: NextPage = () => {
<AppLayout <AppLayout
header={<ModulesHeader name={activeProject?.name} modulesView={modulesView} setModulesView={setModulesView} />} header={<ModulesHeader name={activeProject?.name} modulesView={modulesView} setModulesView={setModulesView} />}
> >
<CreateUpdateModuleModal {workspaceSlug && projectId && (
isOpen={createUpdateModule} <CreateUpdateModuleModal
setIsOpen={setCreateUpdateModule} isOpen={createUpdateModule}
data={selectedModule} onClose={() => {
user={user} setCreateUpdateModule(false);
/> }}
data={selectedModule}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
/>
)}
{modules ? ( {modules ? (
modules.length > 0 ? ( modules.length > 0 ? (
<> <>

View File

@ -51,12 +51,11 @@ const tabsList = ["Recent", "All", "Favorites", "Created by me", "Created by oth
const projectService = new ProjectService(); const projectService = new ProjectService();
const ProjectPages: NextPage = () => { const ProjectPages: NextPage = () => {
const [createUpdatePageModal, setCreateUpdatePageModal] = useState(false);
const [viewType, setViewType] = useState<TPageViewProps>("list");
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug, projectId } = router.query;
// states
const [createUpdatePageModal, setCreateUpdatePageModal] = useState(false);
const [viewType, setViewType] = useState<TPageViewProps>("list");
const { user } = useUserAuth(); const { user } = useUserAuth();
@ -87,11 +86,16 @@ const ProjectPages: NextPage = () => {
return ( return (
<> <>
<CreateUpdatePageModal {workspaceSlug && projectId && (
isOpen={createUpdatePageModal} <CreateUpdatePageModal
handleClose={() => setCreateUpdatePageModal(false)} isOpen={createUpdatePageModal}
user={user} handleClose={() => setCreateUpdatePageModal(false)}
/> user={user}
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
/>
)}
<ProjectAuthorizationWrapper <ProjectAuthorizationWrapper
breadcrumbs={ breadcrumbs={
<Breadcrumbs onBack={() => router.back()}> <Breadcrumbs onBack={() => router.back()}>

View File

@ -0,0 +1,161 @@
import { observable, action, makeObservable } from "mobx";
// types
import { RootStore } from "./root";
// services
import { ProjectService } from "services/project";
import { PageService } from "services/page.service";
export interface ICommandPaletteStore {
isCommandPaletteOpen: boolean;
isShortcutModalOpen: boolean;
isCreateProjectModalOpen: boolean;
isCreateCycleModalOpen: boolean;
isCreateModuleModalOpen: boolean;
isCreateViewModalOpen: boolean;
isCreatePageModalOpen: boolean;
isCreateIssueModalOpen: boolean;
isDeleteIssueModalOpen: boolean;
isBulkDeleteIssueModalOpen: boolean;
toggleCommandPaletteModal: (value?: boolean) => void;
toggleShortcutModal: (value?: boolean) => void;
toggleCreateProjectModal: (value?: boolean) => void;
toggleCreateCycleModal: (value?: boolean) => void;
toggleCreateViewModal: (value?: boolean) => void;
toggleCreatePageModal: (value?: boolean) => void;
toggleCreateIssueModal: (value?: boolean) => void;
toggleCreateModuleModal: (value?: boolean) => void;
toggleDeleteIssueModal: (value?: boolean) => void;
toggleBulkDeleteIssueModal: (value?: boolean) => void;
}
class CommandPaletteStore implements ICommandPaletteStore {
isCommandPaletteOpen: boolean = false;
isShortcutModalOpen: boolean = false;
isCreateProjectModalOpen: boolean = false;
isCreateCycleModalOpen: boolean = false;
isCreateModuleModalOpen: boolean = false;
isCreateViewModalOpen: boolean = false;
isCreatePageModalOpen: boolean = false;
isCreateIssueModalOpen: boolean = false;
isDeleteIssueModalOpen: boolean = false;
isBulkDeleteIssueModalOpen: boolean = false;
// root store
rootStore;
// service
projectService;
pageService;
constructor(_rootStore: RootStore) {
makeObservable(this, {
// observable
isCommandPaletteOpen: observable.ref,
isShortcutModalOpen: observable.ref,
isCreateProjectModalOpen: observable.ref,
isCreateCycleModalOpen: observable.ref,
isCreateModuleModalOpen: observable.ref,
isCreateViewModalOpen: observable.ref,
isCreatePageModalOpen: observable.ref,
isDeleteIssueModalOpen: observable.ref,
isCreateIssueModalOpen: observable.ref,
// computed
// projectPages: computed,
// action
toggleCommandPaletteModal: action,
toggleShortcutModal: action,
toggleCreateProjectModal: action,
toggleCreateCycleModal: action,
toggleCreateViewModal: action,
toggleCreatePageModal: action,
toggleCreateIssueModal: action,
toggleCreateModuleModal: action,
toggleBulkDeleteIssueModal: action,
});
this.rootStore = _rootStore;
this.projectService = new ProjectService();
this.pageService = new PageService();
}
toggleCommandPaletteModal = (value?: boolean) => {
if (value) {
this.isCommandPaletteOpen = value;
} else {
this.isCommandPaletteOpen = !this.isCommandPaletteOpen;
}
};
toggleShortcutModal = (value?: boolean) => {
if (value) {
this.isShortcutModalOpen = value;
} else {
this.isShortcutModalOpen = !this.isShortcutModalOpen;
}
};
toggleCreateProjectModal = (value?: boolean) => {
if (value) {
this.isCreateProjectModalOpen = value;
} else {
this.isCreateProjectModalOpen = !this.isCreateProjectModalOpen;
}
};
toggleCreateCycleModal = (value?: boolean) => {
if (value) {
this.isCreateCycleModalOpen = value;
} else {
this.isCreateCycleModalOpen = !this.isCreateCycleModalOpen;
}
};
toggleCreateViewModal = (value?: boolean) => {
if (value) {
this.isCreateViewModalOpen = value;
} else {
this.isCreateViewModalOpen = !this.isCreateViewModalOpen;
}
};
toggleCreatePageModal = (value?: boolean) => {
if (value) {
this.isCreatePageModalOpen = value;
} else {
this.isCreatePageModalOpen = !this.isCreatePageModalOpen;
}
};
toggleCreateIssueModal = (value?: boolean) => {
if (value) {
this.isCreateIssueModalOpen = value;
} else {
this.isCreateIssueModalOpen = !this.isCreateIssueModalOpen;
}
};
toggleDeleteIssueModal = (value?: boolean) => {
if (value) {
this.isDeleteIssueModalOpen = value;
} else {
this.isDeleteIssueModalOpen = !this.isDeleteIssueModalOpen;
}
};
toggleCreateModuleModal = (value?: boolean) => {
if (value) {
this.isCreateModuleModalOpen = value;
} else {
this.isCreateModuleModalOpen = !this.isCreateModuleModalOpen;
}
};
toggleBulkDeleteIssueModal = (value?: boolean) => {
if (value) {
this.isBulkDeleteIssueModalOpen = value;
} else {
this.isBulkDeleteIssueModalOpen = !this.isBulkDeleteIssueModalOpen;
}
};
}
export default CommandPaletteStore;

View File

@ -1,7 +1,8 @@
import { enableStaticRendering } from "mobx-react-lite"; import { enableStaticRendering } from "mobx-react-lite";
// store imports // store imports
import UserStore from "store/user.store"; import CommandPaletteStore, { ICommandPaletteStore } from "./command-palette.store";
import ThemeStore from "store/theme.store"; import UserStore, { IUserStore } from "store/user.store";
import ThemeStore, { IThemeStore } from "store/theme.store";
import { import {
DraftIssuesStore, DraftIssuesStore,
IIssueDetailStore, IIssueDetailStore,
@ -79,9 +80,10 @@ import {
enableStaticRendering(typeof window === "undefined"); enableStaticRendering(typeof window === "undefined");
export class RootStore { export class RootStore {
user; user: IUserStore;
theme; theme: IThemeStore;
commandPalette: ICommandPaletteStore;
workspace: IWorkspaceStore; workspace: IWorkspaceStore;
workspaceFilter: IWorkspaceFilterStore; workspaceFilter: IWorkspaceFilterStore;
@ -129,6 +131,7 @@ export class RootStore {
inboxFilters: IInboxFiltersStore; inboxFilters: IInboxFiltersStore;
constructor() { constructor() {
this.commandPalette = new CommandPaletteStore(this);
this.user = new UserStore(this); this.user = new UserStore(this);
this.theme = new ThemeStore(this); this.theme = new ThemeStore(this);

View File

@ -3,7 +3,15 @@ import { action, observable, makeObservable } from "mobx";
// helper // helper
import { applyTheme, unsetCustomCssVariables } from "helpers/theme.helper"; import { applyTheme, unsetCustomCssVariables } from "helpers/theme.helper";
class ThemeStore { export interface IThemeStore {
theme: string | null;
sidebarCollapsed: boolean | null;
setSidebarCollapsed: (collapsed?: boolean) => void;
setTheme: (theme: any) => void;
}
class ThemeStore implements IThemeStore {
sidebarCollapsed: boolean | null = null; sidebarCollapsed: boolean | null = null;
theme: string | null = null; theme: string | null = null;
// root store // root store
@ -24,8 +32,8 @@ class ThemeStore {
this.initialLoad(); this.initialLoad();
} }
setSidebarCollapsed(collapsed: boolean | null = null) { setSidebarCollapsed(collapsed?: boolean) {
if (collapsed === null) { if (!collapsed) {
let _sidebarCollapsed: string | boolean | null = localStorage.getItem("app_sidebar_collapsed"); let _sidebarCollapsed: string | boolean | null = localStorage.getItem("app_sidebar_collapsed");
_sidebarCollapsed = _sidebarCollapsed ? (_sidebarCollapsed === "true" ? true : false) : false; _sidebarCollapsed = _sidebarCollapsed ? (_sidebarCollapsed === "true" ? true : false) : false;
this.sidebarCollapsed = _sidebarCollapsed; this.sidebarCollapsed = _sidebarCollapsed;

View File

@ -8,7 +8,7 @@ import { WorkspaceService } from "services/workspace.service";
import { IUser, IUserSettings } from "types/users"; import { IUser, IUserSettings } from "types/users";
import { IWorkspaceMember, IProjectMember } from "types"; import { IWorkspaceMember, IProjectMember } from "types";
interface IUserStore { export interface IUserStore {
loader: boolean; loader: boolean;
currentUser: IUser | null; currentUser: IUser | null;
@ -28,9 +28,11 @@ interface IUserStore {
fetchUserWorkspaceInfo: (workspaceSlug: string) => Promise<IWorkspaceMember>; fetchUserWorkspaceInfo: (workspaceSlug: string) => Promise<IWorkspaceMember>;
fetchUserProjectInfo: (workspaceSlug: string, projectId: string) => Promise<IProjectMember>; fetchUserProjectInfo: (workspaceSlug: string, projectId: string) => Promise<IProjectMember>;
fetchUserDashboardInfo: (workspaceSlug: string, month: number) => Promise<any>;
updateTourCompleted: () => Promise<void>; updateTourCompleted: () => Promise<void>;
updateCurrentUser: (data: Partial<IUser>) => Promise<IUser>; updateCurrentUser: (data: Partial<IUser>) => Promise<IUser>;
updateCurrentUserTheme: (theme: string) => Promise<IUser>;
} }
class UserStore implements IUserStore { class UserStore implements IUserStore {