forked from github/plane
fix: command palette changes (#2465)
This commit is contained in:
parent
d689c63368
commit
90776237f3
@ -1,12 +1,7 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import useSWR, { mutate } from "swr";
|
||||
|
||||
// cmdk
|
||||
import { Command } from "cmdk";
|
||||
// headless ui
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// services
|
||||
import { WorkspaceService } from "services/workspace.service";
|
||||
@ -60,16 +55,20 @@ import { ISSUE_DETAILS, PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys";
|
||||
type Props = {
|
||||
deleteIssue: () => void;
|
||||
isPaletteOpen: boolean;
|
||||
setIsPaletteOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
closePalette: () => void;
|
||||
};
|
||||
|
||||
// services
|
||||
const workspaceService = new WorkspaceService();
|
||||
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 [resultsCount, setResultsCount] = useState(0);
|
||||
const [isLoading, setIsLoading] = 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 [pages, setPages] = useState<string[]>([]);
|
||||
|
||||
const page = pages[pages.length - 1];
|
||||
|
||||
const debouncedSearchTerm = useDebounce(searchTerm, 500);
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, issueId } = router.query;
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const { user } = useUser();
|
||||
@ -141,7 +137,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
const handleIssueAssignees = (assignee: string) => {
|
||||
if (!issueDetails) return;
|
||||
|
||||
setIsPaletteOpen(false);
|
||||
closePalette();
|
||||
const updatedAssignees = issueDetails.assignees ?? [];
|
||||
|
||||
if (updatedAssignees.includes(assignee)) {
|
||||
@ -153,12 +149,12 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
};
|
||||
|
||||
const redirect = (path: string) => {
|
||||
setIsPaletteOpen(false);
|
||||
closePalette();
|
||||
router.push(path);
|
||||
};
|
||||
|
||||
const createNewWorkspace = () => {
|
||||
setIsPaletteOpen(false);
|
||||
closePalette();
|
||||
router.push("/create-workspace");
|
||||
};
|
||||
|
||||
@ -236,7 +232,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
}}
|
||||
as={React.Fragment}
|
||||
>
|
||||
<Dialog as="div" className="relative z-30" onClose={() => setIsPaletteOpen(false)}>
|
||||
<Dialog as="div" className="relative z-30" onClose={() => closePalette()}>
|
||||
<Transition.Child
|
||||
as={React.Fragment}
|
||||
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 user tries to close the modal with esc
|
||||
if (e.key === "Escape" && !page && !searchTerm) {
|
||||
setIsPaletteOpen(false);
|
||||
closePalette();
|
||||
}
|
||||
// Escape goes to previous page
|
||||
// Backspace goes to previous page when search is empty
|
||||
@ -365,7 +361,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
<Command.Item
|
||||
key={item.id}
|
||||
onSelect={() => {
|
||||
setIsPaletteOpen(false);
|
||||
closePalette();
|
||||
router.push(currentSection.path(item));
|
||||
}}
|
||||
value={`${key}-${item?.name}`}
|
||||
@ -388,7 +384,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
<Command.Group heading="Issue actions">
|
||||
<Command.Item
|
||||
onSelect={() => {
|
||||
setIsPaletteOpen(false);
|
||||
closePalette();
|
||||
setPlaceholder("Change state...");
|
||||
setSearchTerm("");
|
||||
setPages([...pages, "change-issue-state"]);
|
||||
@ -455,7 +451,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
</Command.Item>
|
||||
<Command.Item
|
||||
onSelect={() => {
|
||||
setIsPaletteOpen(false);
|
||||
closePalette();
|
||||
copyIssueUrlToClipboard();
|
||||
}}
|
||||
className="focus:outline-none"
|
||||
@ -470,7 +466,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
<Command.Group heading="Issue">
|
||||
<Command.Item
|
||||
onSelect={() => {
|
||||
setIsPaletteOpen(false);
|
||||
closePalette();
|
||||
const e = new KeyboardEvent("keydown", {
|
||||
key: "c",
|
||||
});
|
||||
@ -490,7 +486,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
<Command.Group heading="Project">
|
||||
<Command.Item
|
||||
onSelect={() => {
|
||||
setIsPaletteOpen(false);
|
||||
closePalette();
|
||||
const e = new KeyboardEvent("keydown", {
|
||||
key: "p",
|
||||
});
|
||||
@ -512,7 +508,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
<Command.Group heading="Cycle">
|
||||
<Command.Item
|
||||
onSelect={() => {
|
||||
setIsPaletteOpen(false);
|
||||
closePalette();
|
||||
const e = new KeyboardEvent("keydown", {
|
||||
key: "q",
|
||||
});
|
||||
@ -530,7 +526,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
<Command.Group heading="Module">
|
||||
<Command.Item
|
||||
onSelect={() => {
|
||||
setIsPaletteOpen(false);
|
||||
closePalette();
|
||||
const e = new KeyboardEvent("keydown", {
|
||||
key: "m",
|
||||
});
|
||||
@ -548,7 +544,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
<Command.Group heading="View">
|
||||
<Command.Item
|
||||
onSelect={() => {
|
||||
setIsPaletteOpen(false);
|
||||
closePalette();
|
||||
const e = new KeyboardEvent("keydown", {
|
||||
key: "v",
|
||||
});
|
||||
@ -566,7 +562,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
<Command.Group heading="Page">
|
||||
<Command.Item
|
||||
onSelect={() => {
|
||||
setIsPaletteOpen(false);
|
||||
closePalette();
|
||||
const e = new KeyboardEvent("keydown", {
|
||||
key: "d",
|
||||
});
|
||||
@ -623,7 +619,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
<Command.Group heading="Help">
|
||||
<Command.Item
|
||||
onSelect={() => {
|
||||
setIsPaletteOpen(false);
|
||||
closePalette();
|
||||
const e = new KeyboardEvent("keydown", {
|
||||
key: "h",
|
||||
});
|
||||
@ -638,7 +634,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
</Command.Item>
|
||||
<Command.Item
|
||||
onSelect={() => {
|
||||
setIsPaletteOpen(false);
|
||||
closePalette();
|
||||
window.open("https://docs.plane.so/", "_blank");
|
||||
}}
|
||||
className="focus:outline-none"
|
||||
@ -650,7 +646,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
</Command.Item>
|
||||
<Command.Item
|
||||
onSelect={() => {
|
||||
setIsPaletteOpen(false);
|
||||
closePalette();
|
||||
window.open("https://discord.com/invite/A92xrEGCge", "_blank");
|
||||
}}
|
||||
className="focus:outline-none"
|
||||
@ -662,7 +658,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
</Command.Item>
|
||||
<Command.Item
|
||||
onSelect={() => {
|
||||
setIsPaletteOpen(false);
|
||||
closePalette();
|
||||
window.open("https://github.com/makeplane/plane/issues/new/choose", "_blank");
|
||||
}}
|
||||
className="focus:outline-none"
|
||||
@ -674,7 +670,7 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
</Command.Item>
|
||||
<Command.Item
|
||||
onSelect={() => {
|
||||
setIsPaletteOpen(false);
|
||||
closePalette();
|
||||
(window as any).$crisp.push(["do", "chat:open"]);
|
||||
}}
|
||||
className="focus:outline-none"
|
||||
@ -747,15 +743,15 @@ export const CommandK: React.FC<Props> = ({ deleteIssue, isPaletteOpen, setIsPal
|
||||
</>
|
||||
)}
|
||||
{page === "change-issue-state" && issueDetails && (
|
||||
<ChangeIssueState issue={issueDetails} setIsPaletteOpen={setIsPaletteOpen} user={user} />
|
||||
<ChangeIssueState issue={issueDetails} setIsPaletteOpen={closePalette} user={user} />
|
||||
)}
|
||||
{page === "change-issue-priority" && issueDetails && (
|
||||
<ChangeIssuePriority issue={issueDetails} setIsPaletteOpen={setIsPaletteOpen} user={user} />
|
||||
<ChangeIssuePriority issue={issueDetails} setIsPaletteOpen={closePalette} user={user} />
|
||||
)}
|
||||
{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>
|
||||
</Dialog.Panel>
|
@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import React, { useCallback, useEffect, FC } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
import { observer } from "mobx-react-lite";
|
||||
@ -6,7 +6,7 @@ import { observer } from "mobx-react-lite";
|
||||
import useToast from "hooks/use-toast";
|
||||
import useUser from "hooks/use-user";
|
||||
// components
|
||||
import { CommandK, ShortcutsModal } from "components/command-palette";
|
||||
import { CommandModal, ShortcutsModal } from "components/command-palette";
|
||||
import { BulkDeleteIssuesModal } from "components/core";
|
||||
import { CreateUpdateCycleModal } from "components/cycles";
|
||||
import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
|
||||
@ -26,22 +26,34 @@ import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// services
|
||||
const issueService = new IssueService();
|
||||
|
||||
export const CommandPalette: React.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);
|
||||
|
||||
export const CommandPalette: FC = observer(() => {
|
||||
const router = useRouter();
|
||||
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();
|
||||
|
||||
@ -55,7 +67,7 @@ export const CommandPalette: React.FC = observer(() => {
|
||||
);
|
||||
|
||||
const copyIssueUrlToClipboard = useCallback(() => {
|
||||
if (!router.query.issueId) return;
|
||||
if (!issueId) return;
|
||||
|
||||
const url = new URL(window.location.href);
|
||||
copyTextToClipboard(url.href)
|
||||
@ -71,7 +83,7 @@ export const CommandPalette: React.FC = observer(() => {
|
||||
title: "Some error occurred",
|
||||
});
|
||||
});
|
||||
}, [router, setToastAlert]);
|
||||
}, [setToastAlert, issueId]);
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: KeyboardEvent) => {
|
||||
@ -91,101 +103,142 @@ export const CommandPalette: React.FC = observer(() => {
|
||||
if (cmdClicked) {
|
||||
if (keyPressed === "k") {
|
||||
e.preventDefault();
|
||||
setIsPaletteOpen(true);
|
||||
toggleCommandPaletteModal(true);
|
||||
} else if (keyPressed === "c" && altKey) {
|
||||
e.preventDefault();
|
||||
copyIssueUrlToClipboard();
|
||||
} else if (keyPressed === "b") {
|
||||
e.preventDefault();
|
||||
store.theme.setSidebarCollapsed(!store?.theme?.sidebarCollapsed);
|
||||
setSidebarCollapsed();
|
||||
}
|
||||
} else {
|
||||
if (keyPressed === "c") {
|
||||
setIsIssueModalOpen(true);
|
||||
toggleCreateIssueModal(true);
|
||||
} else if (keyPressed === "p") {
|
||||
setIsProjectModalOpen(true);
|
||||
toggleCreateProjectModal(true);
|
||||
} else if (keyPressed === "v") {
|
||||
setIsCreateViewModalOpen(true);
|
||||
toggleCreateViewModal(true);
|
||||
} else if (keyPressed === "d") {
|
||||
setIsCreateUpdatePageModalOpen(true);
|
||||
toggleCreatePageModal(true);
|
||||
} else if (keyPressed === "h") {
|
||||
setIsShortcutsModalOpen(true);
|
||||
toggleShortcutModal(true);
|
||||
} else if (keyPressed === "q") {
|
||||
setIsCreateCycleModalOpen(true);
|
||||
toggleCreateCycleModal(true);
|
||||
} else if (keyPressed === "m") {
|
||||
setIsCreateModuleModalOpen(true);
|
||||
toggleCreateModuleModal(true);
|
||||
} else if (keyPressed === "backspace" || keyPressed === "delete") {
|
||||
e.preventDefault();
|
||||
setIsBulkDeleteIssuesModalOpen(true);
|
||||
toggleBulkDeleteIssueModal(true);
|
||||
}
|
||||
}
|
||||
},
|
||||
[copyIssueUrlToClipboard, store.theme]
|
||||
[
|
||||
copyIssueUrlToClipboard,
|
||||
toggleCreateProjectModal,
|
||||
toggleCreateViewModal,
|
||||
toggleCreatePageModal,
|
||||
toggleShortcutModal,
|
||||
toggleCreateCycleModal,
|
||||
toggleCreateModuleModal,
|
||||
toggleBulkDeleteIssueModal,
|
||||
toggleCommandPaletteModal,
|
||||
setSidebarCollapsed,
|
||||
toggleCreateIssueModal,
|
||||
]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
|
||||
return () => document.removeEventListener("keydown", handleKeyDown);
|
||||
}, [handleKeyDown]);
|
||||
|
||||
if (!user) return null;
|
||||
|
||||
const deleteIssue = () => {
|
||||
setIsPaletteOpen(false);
|
||||
setDeleteIssueModal(true);
|
||||
toggleCommandPaletteModal(false);
|
||||
toggleDeleteIssueModal(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ShortcutsModal isOpen={isShortcutsModalOpen} setIsOpen={setIsShortcutsModalOpen} />
|
||||
<ShortcutsModal
|
||||
isOpen={isShortcutModalOpen}
|
||||
onClose={() => {
|
||||
toggleShortcutModal(false);
|
||||
}}
|
||||
/>
|
||||
{workspaceSlug && (
|
||||
<CreateProjectModal isOpen={isProjectModalOpen} setIsOpen={setIsProjectModalOpen} user={user} />
|
||||
<CreateProjectModal
|
||||
isOpen={isCreateProjectModalOpen}
|
||||
onClose={() => {
|
||||
toggleCreateProjectModal(false);
|
||||
}}
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
/>
|
||||
)}
|
||||
{projectId && (
|
||||
{workspaceSlug && projectId && (
|
||||
<>
|
||||
<CreateUpdateCycleModal
|
||||
isOpen={isCreateCycleModalOpen}
|
||||
handleClose={() => setIsCreateCycleModalOpen(false)}
|
||||
user={user}
|
||||
handleClose={() => toggleCreateCycleModal(false)}
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
projectId={projectId.toString()}
|
||||
/>
|
||||
<CreateUpdateModuleModal
|
||||
isOpen={isCreateModuleModalOpen}
|
||||
setIsOpen={setIsCreateModuleModalOpen}
|
||||
user={user}
|
||||
onClose={() => {
|
||||
toggleCreateModuleModal(false);
|
||||
}}
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
projectId={projectId.toString()}
|
||||
/>
|
||||
<CreateUpdateProjectViewModal
|
||||
isOpen={isCreateViewModalOpen}
|
||||
onClose={() => setIsCreateViewModalOpen(false)}
|
||||
onClose={() => toggleCreateViewModal(false)}
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
projectId={projectId.toString()}
|
||||
/>
|
||||
<CreateUpdatePageModal
|
||||
isOpen={isCreateUpdatePageModalOpen}
|
||||
handleClose={() => setIsCreateUpdatePageModalOpen(false)}
|
||||
isOpen={isCreatePageModalOpen}
|
||||
handleClose={() => toggleCreatePageModal(false)}
|
||||
user={user}
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
projectId={projectId.toString()}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{issueId && issueDetails && (
|
||||
<DeleteIssueModal
|
||||
handleClose={() => setDeleteIssueModal(false)}
|
||||
isOpen={deleteIssueModal}
|
||||
data={issueDetails}
|
||||
user={user}
|
||||
/>
|
||||
)}
|
||||
|
||||
<CreateUpdateIssueModal
|
||||
isOpen={isIssueModalOpen}
|
||||
handleClose={() => setIsIssueModalOpen(false)}
|
||||
isOpen={isCreateIssueModalOpen}
|
||||
handleClose={() => toggleCreateIssueModal(false)}
|
||||
prePopulateData={
|
||||
cycleId ? { cycle: cycleId.toString() } : moduleId ? { module: moduleId.toString() } : undefined
|
||||
}
|
||||
/>
|
||||
<BulkDeleteIssuesModal
|
||||
isOpen={isBulkDeleteIssuesModalOpen}
|
||||
setIsOpen={setIsBulkDeleteIssuesModalOpen}
|
||||
|
||||
{issueId && issueDetails && (
|
||||
<DeleteIssueModal
|
||||
handleClose={() => toggleDeleteIssueModal(false)}
|
||||
isOpen={isDeleteIssueModalOpen}
|
||||
data={issueDetails}
|
||||
user={user}
|
||||
/>
|
||||
<CommandK deleteIssue={deleteIssue} isPaletteOpen={isPaletteOpen} setIsPaletteOpen={setIsPaletteOpen} />
|
||||
)}
|
||||
|
||||
<BulkDeleteIssuesModal
|
||||
isOpen={isBulkDeleteIssueModalOpen}
|
||||
onClose={() => {
|
||||
toggleBulkDeleteIssueModal(false);
|
||||
}}
|
||||
user={user}
|
||||
/>
|
||||
<CommandModal
|
||||
deleteIssue={deleteIssue}
|
||||
isPaletteOpen={isCommandPaletteOpen}
|
||||
closePalette={() => {
|
||||
toggleCommandPaletteModal(false);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
export * from "./issue";
|
||||
export * from "./change-interface-theme";
|
||||
export * from "./command-k";
|
||||
export * from "./command-modal";
|
||||
export * from "./command-pallette";
|
||||
export * from "./helpers";
|
||||
export * from "./shortcuts-modal";
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { FC, useEffect, useState, Dispatch, SetStateAction, Fragment } from "react";
|
||||
// headless ui
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// icons
|
||||
@ -8,7 +8,7 @@ import { Input } from "@plane/ui";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
const shortcuts = [
|
||||
@ -43,8 +43,11 @@ const shortcuts = [
|
||||
|
||||
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("");
|
||||
// computed
|
||||
const filteredShortcuts = allShortcuts.filter((shortcut) =>
|
||||
shortcut.description.toLowerCase().includes(query.trim().toLowerCase()) || query === "" ? true : false
|
||||
);
|
||||
@ -54,10 +57,10 @@ export const ShortcutsModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
|
||||
}, [isOpen]);
|
||||
|
||||
return (
|
||||
<Transition.Root show={isOpen} as={React.Fragment}>
|
||||
<Dialog as="div" className="relative z-20" onClose={setIsOpen}>
|
||||
<Transition.Root show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-20" onClose={onClose}>
|
||||
<Transition.Child
|
||||
as={React.Fragment}
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
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="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<Transition.Child
|
||||
as={React.Fragment}
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
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>
|
||||
<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" />
|
||||
</button>
|
||||
</span>
|
||||
|
@ -33,18 +33,20 @@ type FormInput = {
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
onClose: () => void;
|
||||
user: IUser | undefined;
|
||||
};
|
||||
|
||||
const issueService = new IssueService();
|
||||
|
||||
export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen, user }) => {
|
||||
const [query, setQuery] = useState("");
|
||||
|
||||
export const BulkDeleteIssuesModal: React.FC<Props> = (props) => {
|
||||
const { isOpen, onClose, user } = props;
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, cycleId, moduleId, viewId } = router.query;
|
||||
|
||||
// states
|
||||
const [query, setQuery] = useState("");
|
||||
// fetching project issues.
|
||||
const { data: issues } = useSWR(
|
||||
workspaceSlug && projectId ? PROJECT_ISSUES_LIST(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 = () => {
|
||||
setIsOpen(false);
|
||||
setQuery("");
|
||||
reset();
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleDelete: SubmitHandler<FormInput> = async (data) => {
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { Fragment } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { mutate } from "swr";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// services
|
||||
@ -11,7 +10,7 @@ import { CycleForm } from "components/cycles";
|
||||
// helper
|
||||
import { getDateRangeStatus } from "helpers/date-time.helper";
|
||||
// types
|
||||
import type { CycleDateCheckData, IUser, ICycle, IProject } from "types";
|
||||
import type { CycleDateCheckData, ICycle, IProject, IUser } from "types";
|
||||
// fetch keys
|
||||
import {
|
||||
COMPLETED_CYCLES_LIST,
|
||||
@ -27,23 +26,21 @@ type CycleModalProps = {
|
||||
isOpen: boolean;
|
||||
handleClose: () => void;
|
||||
data?: ICycle | null;
|
||||
user: IUser | undefined;
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
};
|
||||
|
||||
// services
|
||||
const cycleService = new CycleService();
|
||||
|
||||
export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({ isOpen, handleClose, data, user }) => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
export const CreateUpdateCycleModal: React.FC<CycleModalProps> = (props) => {
|
||||
const { isOpen, handleClose, data, workspaceSlug, projectId } = props;
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const createCycle = async (payload: Partial<ICycle>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
await cycleService
|
||||
.createCycle(workspaceSlug.toString(), projectId.toString(), payload, user)
|
||||
.createCycle(workspaceSlug.toString(), projectId.toString(), payload, {} as IUser)
|
||||
.then((res) => {
|
||||
switch (getDateRangeStatus(res.start_date, res.end_date)) {
|
||||
case "completed":
|
||||
@ -91,10 +88,8 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({ isOpen, hand
|
||||
};
|
||||
|
||||
const updateCycle = async (cycleId: string, payload: Partial<ICycle>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
await cycleService
|
||||
.updateCycle(workspaceSlug.toString(), projectId.toString(), cycleId, payload, user)
|
||||
.updateCycle(workspaceSlug.toString(), projectId.toString(), cycleId, payload, {} as IUser)
|
||||
.then((res) => {
|
||||
switch (getDateRangeStatus(data?.start_date, data?.end_date)) {
|
||||
case "completed":
|
||||
@ -177,7 +172,6 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({ isOpen, hand
|
||||
if (isDateValid) {
|
||||
if (data) await updateCycle(data.id, payload);
|
||||
else await createCycle(payload);
|
||||
|
||||
handleClose();
|
||||
} else
|
||||
setToastAlert({
|
||||
|
@ -50,7 +50,14 @@ export const CycleSelect: React.FC<IssueCycleSelectProps> = ({ projectId, value,
|
||||
|
||||
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}>
|
||||
{({ open }) => (
|
||||
<>
|
||||
|
@ -19,13 +19,20 @@ export const ProjectViewsHeader: FC<IProjectViewsHeader> = (props) => {
|
||||
const { title } = props;
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
// states
|
||||
const [createViewModal, setCreateViewModal] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<CreateUpdateProjectViewModal isOpen={createViewModal} onClose={() => setCreateViewModal(false)} />
|
||||
{workspaceSlug && projectId && (
|
||||
<CreateUpdateProjectViewModal
|
||||
isOpen={createViewModal}
|
||||
onClose={() => setCreateViewModal(false)}
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
projectId={projectId.toString()}
|
||||
/>
|
||||
)}
|
||||
<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`}
|
||||
>
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { Fragment } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { mutate } from "swr";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
@ -16,9 +15,10 @@ import { MODULE_LIST } from "constants/fetch-keys";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
onClose: () => void;
|
||||
data?: IModule;
|
||||
user: IUser | undefined;
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
};
|
||||
|
||||
const defaultValues: Partial<IModule> = {
|
||||
@ -31,15 +31,14 @@ const defaultValues: Partial<IModule> = {
|
||||
|
||||
const moduleService = new ModuleService();
|
||||
|
||||
export const CreateUpdateModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, data, user }) => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
export const CreateUpdateModuleModal: React.FC<Props> = (props) => {
|
||||
const { isOpen, onClose, data, workspaceSlug, projectId } = props;
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const handleClose = () => {
|
||||
setIsOpen(false);
|
||||
reset(defaultValues);
|
||||
onClose();
|
||||
};
|
||||
|
||||
const { reset } = useForm<IModule>({
|
||||
@ -48,7 +47,7 @@ export const CreateUpdateModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, da
|
||||
|
||||
const createModule = async (payload: Partial<IModule>) => {
|
||||
await moduleService
|
||||
.createModule(workspaceSlug as string, projectId as string, payload, user)
|
||||
.createModule(workspaceSlug as string, projectId as string, payload, {} as IUser)
|
||||
.then(() => {
|
||||
mutate(MODULE_LIST(projectId as string));
|
||||
handleClose();
|
||||
@ -70,7 +69,7 @@ export const CreateUpdateModuleModal: React.FC<Props> = ({ isOpen, setIsOpen, da
|
||||
|
||||
const updateModule = async (payload: Partial<IModule>) => {
|
||||
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) => {
|
||||
mutate<IModule[]>(
|
||||
MODULE_LIST(projectId as string),
|
||||
|
@ -22,14 +22,17 @@ type Props = {
|
||||
handleClose: () => void;
|
||||
data?: IPage | null;
|
||||
user: IUser | undefined;
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
};
|
||||
|
||||
// services
|
||||
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 { workspaceSlug, projectId } = router.query;
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
|
@ -38,14 +38,14 @@ const pageService = new PageService();
|
||||
const projectService = new ProjectService();
|
||||
|
||||
export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
|
||||
const [createUpdatePageModal, setCreateUpdatePageModal] = useState(false);
|
||||
const [selectedPageToUpdate, setSelectedPageToUpdate] = useState<IPage | null>(null);
|
||||
|
||||
const [deletePageModal, setDeletePageModal] = useState(false);
|
||||
const [selectedPageToDelete, setSelectedPageToDelete] = useState<IPage | null>(null);
|
||||
|
||||
// router
|
||||
const router = useRouter();
|
||||
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();
|
||||
|
||||
@ -187,12 +187,16 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{workspaceSlug && projectId && (
|
||||
<>
|
||||
<CreateUpdatePageModal
|
||||
isOpen={createUpdatePageModal}
|
||||
handleClose={() => setCreateUpdatePageModal(false)}
|
||||
data={selectedPageToUpdate}
|
||||
user={user}
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
projectId={projectId.toString()}
|
||||
/>
|
||||
<DeletePageModal
|
||||
isOpen={deletePageModal}
|
||||
@ -200,6 +204,9 @@ export const PagesView: React.FC<Props> = ({ pages, viewType }) => {
|
||||
data={selectedPageToDelete}
|
||||
user={user}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{pages ? (
|
||||
<div className="space-y-4 h-full overflow-y-auto">
|
||||
{pages.length > 0 ? (
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { useState, useEffect, Fragment } from "react";
|
||||
import { useState, useEffect, Fragment, FC } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
// icons
|
||||
import { Users2, X } from "lucide-react";
|
||||
// hooks
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
import useToast from "hooks/use-toast";
|
||||
import { useWorkspaceMyMembership } from "contexts/workspace-member.context";
|
||||
import useWorkspaceMembers from "hooks/use-workspace-members";
|
||||
@ -17,16 +18,15 @@ import EmojiIconPicker from "components/emoji-icon-picker";
|
||||
// helpers
|
||||
import { getRandomEmoji, renderEmoji } from "helpers/emoji.helper";
|
||||
// types
|
||||
import { IUser, IProject } from "types";
|
||||
import { IProject } from "types";
|
||||
// constants
|
||||
import { NETWORK_CHOICES } from "constants/project";
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
onClose: () => void;
|
||||
setToFavorite?: boolean;
|
||||
user: IUser | undefined;
|
||||
workspaceSlug: string;
|
||||
};
|
||||
|
||||
const defaultValues: Partial<IProject> = {
|
||||
@ -40,26 +40,27 @@ const defaultValues: Partial<IProject> = {
|
||||
project_lead: null,
|
||||
};
|
||||
|
||||
const IsGuestCondition: React.FC<{
|
||||
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}> = ({ setIsOpen }) => {
|
||||
interface IIsGuestCondition {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const IsGuestCondition: FC<IIsGuestCondition> = ({ onClose }) => {
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
useEffect(() => {
|
||||
setIsOpen(false);
|
||||
|
||||
onClose();
|
||||
setToastAlert({
|
||||
title: "Error",
|
||||
type: "error",
|
||||
message: "You don't have permission to create project.",
|
||||
});
|
||||
}, [setIsOpen, setToastAlert]);
|
||||
}, [onClose, setToastAlert]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const CreateProjectModal: React.FC<Props> = (props) => {
|
||||
const { isOpen, setIsOpen, setToFavorite = false } = props;
|
||||
const { isOpen, onClose, setToFavorite = false, workspaceSlug } = props;
|
||||
// store
|
||||
const { project: projectStore } = useMobxStore();
|
||||
// states
|
||||
@ -67,9 +68,6 @@ export const CreateProjectModal: React.FC<Props> = (props) => {
|
||||
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
const { memberDetails } = useWorkspaceMyMembership();
|
||||
const { workspaceMembers } = useWorkspaceMembers(workspaceSlug?.toString() ?? "");
|
||||
|
||||
@ -86,7 +84,7 @@ export const CreateProjectModal: React.FC<Props> = (props) => {
|
||||
});
|
||||
|
||||
const handleClose = () => {
|
||||
setIsOpen(false);
|
||||
onClose();
|
||||
setIsChangeInIdentifierRequired(true);
|
||||
reset(defaultValues);
|
||||
};
|
||||
@ -172,7 +170,7 @@ export const CreateProjectModal: React.FC<Props> = (props) => {
|
||||
|
||||
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 (
|
||||
<Transition.Root show={isOpen} as={Fragment}>
|
||||
|
@ -108,12 +108,16 @@ export const ProjectSidebarList: FC = observer(() => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{workspaceSlug && (
|
||||
<CreateProjectModal
|
||||
isOpen={isProjectModalOpen}
|
||||
setIsOpen={setIsProjectModalOpen}
|
||||
onClose={() => {
|
||||
setIsProjectModalOpen(false);
|
||||
}}
|
||||
setToFavorite={isFavoriteProjectCreate}
|
||||
user={user}
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={`h-full overflow-y-auto px-4 space-y-2 ${
|
||||
|
@ -1,11 +1,8 @@
|
||||
import React from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { FC, Fragment } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Dialog, Transition } from "@headlessui/react";
|
||||
|
||||
// mobx store
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
// hooks
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
import useToast from "hooks/use-toast";
|
||||
// components
|
||||
import { ProjectViewForm } from "components/views";
|
||||
@ -17,31 +14,24 @@ type Props = {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
preLoadedData?: Partial<IProjectView> | null;
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
};
|
||||
|
||||
export const CreateUpdateProjectViewModal: React.FC<Props> = observer((props) => {
|
||||
const { data, isOpen, onClose, preLoadedData } = props;
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
|
||||
export const CreateUpdateProjectViewModal: FC<Props> = observer((props) => {
|
||||
const { data, isOpen, onClose, preLoadedData, workspaceSlug, projectId } = props;
|
||||
// store
|
||||
const { projectViews: projectViewsStore } = useMobxStore();
|
||||
|
||||
// hooks
|
||||
const { setToastAlert } = useToast();
|
||||
|
||||
const handleClose = () => {
|
||||
onClose();
|
||||
};
|
||||
|
||||
const createView = async (formData: IProjectView) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
const payload = {
|
||||
...formData,
|
||||
};
|
||||
|
||||
const createView = async (payload: IProjectView) => {
|
||||
await projectViewsStore
|
||||
.createView(workspaceSlug.toString(), projectId.toString(), payload)
|
||||
.createView(workspaceSlug, projectId, payload)
|
||||
.then(() => handleClose())
|
||||
.catch(() =>
|
||||
setToastAlert({
|
||||
@ -52,15 +42,9 @@ export const CreateUpdateProjectViewModal: React.FC<Props> = observer((props) =>
|
||||
);
|
||||
};
|
||||
|
||||
const updateView = async (formData: IProjectView) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
const payload = {
|
||||
...formData,
|
||||
};
|
||||
|
||||
const updateView = async (payload: IProjectView) => {
|
||||
await projectViewsStore
|
||||
.updateView(workspaceSlug.toString(), projectId.toString(), data?.id as string, payload)
|
||||
.updateView(workspaceSlug, projectId, data?.id as string, payload)
|
||||
.then(() => handleClose())
|
||||
.catch(() =>
|
||||
setToastAlert({
|
||||
@ -77,10 +61,10 @@ export const CreateUpdateProjectViewModal: React.FC<Props> = observer((props) =>
|
||||
};
|
||||
|
||||
return (
|
||||
<Transition.Root show={isOpen} as={React.Fragment}>
|
||||
<Transition.Root show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-20" onClose={handleClose}>
|
||||
<Transition.Child
|
||||
as={React.Fragment}
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
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="flex min-h-full items-center justify-center p-4 text-center sm:p-0">
|
||||
<Transition.Child
|
||||
as={React.Fragment}
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
|
@ -16,7 +16,7 @@ export const AppLayout: FC<IAppLayout> = (props) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* <CommandPalette /> */}
|
||||
<CommandPalette />
|
||||
<UserAuthWrapper>
|
||||
<WorkspaceAuthWrapper>
|
||||
<div className="relative flex h-screen w-full overflow-hidden">
|
||||
|
@ -66,12 +66,18 @@ const ProjectModules: NextPage = () => {
|
||||
<AppLayout
|
||||
header={<ModulesHeader name={activeProject?.name} modulesView={modulesView} setModulesView={setModulesView} />}
|
||||
>
|
||||
{workspaceSlug && projectId && (
|
||||
<CreateUpdateModuleModal
|
||||
isOpen={createUpdateModule}
|
||||
setIsOpen={setCreateUpdateModule}
|
||||
onClose={() => {
|
||||
setCreateUpdateModule(false);
|
||||
}}
|
||||
data={selectedModule}
|
||||
user={user}
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
projectId={projectId.toString()}
|
||||
/>
|
||||
)}
|
||||
|
||||
{modules ? (
|
||||
modules.length > 0 ? (
|
||||
<>
|
||||
|
@ -51,12 +51,11 @@ const tabsList = ["Recent", "All", "Favorites", "Created by me", "Created by oth
|
||||
const projectService = new ProjectService();
|
||||
|
||||
const ProjectPages: NextPage = () => {
|
||||
const [createUpdatePageModal, setCreateUpdatePageModal] = useState(false);
|
||||
|
||||
const [viewType, setViewType] = useState<TPageViewProps>("list");
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
// states
|
||||
const [createUpdatePageModal, setCreateUpdatePageModal] = useState(false);
|
||||
const [viewType, setViewType] = useState<TPageViewProps>("list");
|
||||
|
||||
const { user } = useUserAuth();
|
||||
|
||||
@ -87,11 +86,16 @@ const ProjectPages: NextPage = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{workspaceSlug && projectId && (
|
||||
<CreateUpdatePageModal
|
||||
isOpen={createUpdatePageModal}
|
||||
handleClose={() => setCreateUpdatePageModal(false)}
|
||||
user={user}
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
projectId={projectId.toString()}
|
||||
/>
|
||||
)}
|
||||
|
||||
<ProjectAuthorizationWrapper
|
||||
breadcrumbs={
|
||||
<Breadcrumbs onBack={() => router.back()}>
|
||||
|
161
web/store/command-palette.store.ts
Normal file
161
web/store/command-palette.store.ts
Normal 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;
|
@ -1,7 +1,8 @@
|
||||
import { enableStaticRendering } from "mobx-react-lite";
|
||||
// store imports
|
||||
import UserStore from "store/user.store";
|
||||
import ThemeStore from "store/theme.store";
|
||||
import CommandPaletteStore, { ICommandPaletteStore } from "./command-palette.store";
|
||||
import UserStore, { IUserStore } from "store/user.store";
|
||||
import ThemeStore, { IThemeStore } from "store/theme.store";
|
||||
import {
|
||||
DraftIssuesStore,
|
||||
IIssueDetailStore,
|
||||
@ -79,9 +80,10 @@ import {
|
||||
enableStaticRendering(typeof window === "undefined");
|
||||
|
||||
export class RootStore {
|
||||
user;
|
||||
theme;
|
||||
user: IUserStore;
|
||||
theme: IThemeStore;
|
||||
|
||||
commandPalette: ICommandPaletteStore;
|
||||
workspace: IWorkspaceStore;
|
||||
workspaceFilter: IWorkspaceFilterStore;
|
||||
|
||||
@ -129,6 +131,7 @@ export class RootStore {
|
||||
inboxFilters: IInboxFiltersStore;
|
||||
|
||||
constructor() {
|
||||
this.commandPalette = new CommandPaletteStore(this);
|
||||
this.user = new UserStore(this);
|
||||
this.theme = new ThemeStore(this);
|
||||
|
||||
|
@ -3,7 +3,15 @@ import { action, observable, makeObservable } from "mobx";
|
||||
// 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;
|
||||
theme: string | null = null;
|
||||
// root store
|
||||
@ -24,8 +32,8 @@ class ThemeStore {
|
||||
this.initialLoad();
|
||||
}
|
||||
|
||||
setSidebarCollapsed(collapsed: boolean | null = null) {
|
||||
if (collapsed === null) {
|
||||
setSidebarCollapsed(collapsed?: boolean) {
|
||||
if (!collapsed) {
|
||||
let _sidebarCollapsed: string | boolean | null = localStorage.getItem("app_sidebar_collapsed");
|
||||
_sidebarCollapsed = _sidebarCollapsed ? (_sidebarCollapsed === "true" ? true : false) : false;
|
||||
this.sidebarCollapsed = _sidebarCollapsed;
|
||||
|
@ -8,7 +8,7 @@ import { WorkspaceService } from "services/workspace.service";
|
||||
import { IUser, IUserSettings } from "types/users";
|
||||
import { IWorkspaceMember, IProjectMember } from "types";
|
||||
|
||||
interface IUserStore {
|
||||
export interface IUserStore {
|
||||
loader: boolean;
|
||||
|
||||
currentUser: IUser | null;
|
||||
@ -28,9 +28,11 @@ interface IUserStore {
|
||||
|
||||
fetchUserWorkspaceInfo: (workspaceSlug: string) => Promise<IWorkspaceMember>;
|
||||
fetchUserProjectInfo: (workspaceSlug: string, projectId: string) => Promise<IProjectMember>;
|
||||
fetchUserDashboardInfo: (workspaceSlug: string, month: number) => Promise<any>;
|
||||
|
||||
updateTourCompleted: () => Promise<void>;
|
||||
updateCurrentUser: (data: Partial<IUser>) => Promise<IUser>;
|
||||
updateCurrentUserTheme: (theme: string) => Promise<IUser>;
|
||||
}
|
||||
|
||||
class UserStore implements IUserStore {
|
||||
|
Loading…
Reference in New Issue
Block a user