diff --git a/web/components/issues/issue-layouts/empty-states/cycle.tsx b/web/components/issues/issue-layouts/empty-states/cycle.tsx index 642e1dad9..53ea45001 100644 --- a/web/components/issues/issue-layouts/empty-states/cycle.tsx +++ b/web/components/issues/issue-layouts/empty-states/cycle.tsx @@ -1,38 +1,85 @@ +import { useState } from "react"; +import { observer } from "mobx-react-lite"; import { PlusIcon } from "lucide-react"; +// mobx store +import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import useToast from "hooks/use-toast"; // components import { EmptyState } from "components/common"; +import { ExistingIssuesListModal } from "components/core"; +// ui +import { Button } from "@plane/ui"; // assets import emptyIssue from "public/empty-state/issue.svg"; -import { Button } from "@plane/ui"; +// types +import { ISearchIssueResponse } from "types"; type Props = { - openIssuesListModal: () => void; + workspaceSlug: string | undefined; + projectId: string | undefined; + cycleId: string | undefined; }; -export const CycleEmptyState: React.FC = ({ openIssuesListModal }) => ( -
- , - onClick: () => { - const e = new KeyboardEvent("keydown", { - key: "c", - }); - document.dispatchEvent(e); - }, - }} - secondaryButton={ - - } - /> -
-); +export const CycleEmptyState: React.FC = observer((props) => { + const { workspaceSlug, projectId, cycleId } = props; + // states + const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false); + + const { cycleIssue: cycleIssueStore } = useMobxStore(); + + const { setToastAlert } = useToast(); + + const handleAddIssuesToCycle = async (data: ISearchIssueResponse[]) => { + if (!workspaceSlug || !projectId || !cycleId) return; + + const issueIds = data.map((i) => i.id); + + await cycleIssueStore + .addIssueToCycle(workspaceSlug.toString(), projectId.toString(), cycleId.toString(), issueIds) + .catch(() => { + setToastAlert({ + type: "error", + title: "Error!", + message: "Selected issues could not be added to the cycle. Please try again.", + }); + }); + }; + + return ( + <> + setCycleIssuesListModal(false)} + searchParams={{ cycle: true }} + handleOnSubmit={handleAddIssuesToCycle} + /> +
+ , + onClick: () => { + const e = new KeyboardEvent("keydown", { + key: "c", + }); + document.dispatchEvent(e); + }, + }} + secondaryButton={ + + } + /> +
+ + ); +}); diff --git a/web/components/issues/issue-layouts/empty-states/module.tsx b/web/components/issues/issue-layouts/empty-states/module.tsx index a71be523f..7e5edeeb8 100644 --- a/web/components/issues/issue-layouts/empty-states/module.tsx +++ b/web/components/issues/issue-layouts/empty-states/module.tsx @@ -4,36 +4,78 @@ import { EmptyState } from "components/common"; import { Button } from "@plane/ui"; // assets import emptyIssue from "public/empty-state/issue.svg"; +import { ExistingIssuesListModal } from "components/core"; +import { observer } from "mobx-react-lite"; +import { useMobxStore } from "lib/mobx/store-provider"; +import { ISearchIssueResponse } from "types"; +import useToast from "hooks/use-toast"; +import { useState } from "react"; type Props = { - openIssuesListModal: () => void; + workspaceSlug: string | undefined; + projectId: string | undefined; + moduleId: string | undefined; }; -export const ModuleEmptyState: React.FC = ({ openIssuesListModal }) => ( -
- , - onClick: () => { - const e = new KeyboardEvent("keydown", { - key: "c", - }); - document.dispatchEvent(e); - }, - }} - secondaryButton={ - - } - /> -
-); +export const ModuleEmptyState: React.FC = observer((props) => { + const { workspaceSlug, projectId, moduleId } = props; + // states + const [moduleIssuesListModal, setModuleIssuesListModal] = useState(false); + + const { moduleIssue: moduleIssueStore } = useMobxStore(); + + const { setToastAlert } = useToast(); + + const handleAddIssuesToModule = async (data: ISearchIssueResponse[]) => { + if (!workspaceSlug || !projectId || !moduleId) return; + + const issueIds = data.map((i) => i.id); + + await moduleIssueStore + .addIssueToModule(workspaceSlug.toString(), projectId.toString(), moduleId.toString(), issueIds) + .catch(() => + setToastAlert({ + type: "error", + title: "Error!", + message: "Selected issues could not be added to the module. Please try again.", + }) + ); + }; + + return ( + <> + setModuleIssuesListModal(false)} + searchParams={{ module: true }} + handleOnSubmit={handleAddIssuesToModule} + /> +
+ , + onClick: () => { + const e = new KeyboardEvent("keydown", { + key: "c", + }); + document.dispatchEvent(e); + }, + }} + secondaryButton={ + + } + /> +
+ + ); +}); diff --git a/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx b/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx index 29d91cae8..4d9fa7f08 100644 --- a/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/cycle-layout-root.tsx @@ -20,11 +20,7 @@ import { Spinner } from "@plane/ui"; // helpers import { getDateRangeStatus } from "helpers/date-time.helper"; -type Props = { - openIssuesListModal: () => void; -}; - -export const CycleLayoutRoot: React.FC = observer(({ openIssuesListModal }) => { +export const CycleLayoutRoot: React.FC = observer(() => { const [transferIssuesModal, setTransferIssuesModal] = useState(false); const router = useRouter(); @@ -73,7 +69,11 @@ export const CycleLayoutRoot: React.FC = observer(({ openIssuesListModal {cycleStatus === "completed" && setTransferIssuesModal(true)} />} {(activeLayout === "list" || activeLayout === "spreadsheet") && issueCount === 0 ? ( - + ) : (
{activeLayout === "list" ? ( diff --git a/web/components/issues/issue-layouts/roots/module-layout-root.tsx b/web/components/issues/issue-layouts/roots/module-layout-root.tsx index d4f1efb28..3386725bc 100644 --- a/web/components/issues/issue-layouts/roots/module-layout-root.tsx +++ b/web/components/issues/issue-layouts/roots/module-layout-root.tsx @@ -18,11 +18,7 @@ import { // ui import { Spinner } from "@plane/ui"; -type Props = { - openIssuesListModal: () => void; -}; - -export const ModuleLayoutRoot: React.FC = observer(({ openIssuesListModal }) => { +export const ModuleLayoutRoot: React.FC = observer(() => { const router = useRouter(); const { workspaceSlug, projectId, moduleId } = router.query as { workspaceSlug: string; @@ -66,7 +62,11 @@ export const ModuleLayoutRoot: React.FC = observer(({ openIssuesListModal
{(activeLayout === "list" || activeLayout === "spreadsheet") && issueCount === 0 ? ( - + ) : (
{activeLayout === "list" ? ( diff --git a/web/components/issues/issue-peek-overview/properties.tsx b/web/components/issues/issue-peek-overview/properties.tsx index dbc1bfdbb..303687d39 100644 --- a/web/components/issues/issue-peek-overview/properties.tsx +++ b/web/components/issues/issue-peek-overview/properties.tsx @@ -74,13 +74,13 @@ export const PeekOverviewProperties: FC = observer((pro }; const addIssueToCycle = async (cycleId: string) => { if (!workspaceSlug || !issue || !cycleId) return; - cycleIssueStore.addIssueToCycle(workspaceSlug.toString(), issue.project_detail.id, cycleId, issue.id); + cycleIssueStore.addIssueToCycle(workspaceSlug.toString(), issue.project_detail.id, cycleId, [issue.id]); }; const addIssueToModule = async (moduleId: string) => { if (!workspaceSlug || !issue || !moduleId) return; - moduleIssueStore.addIssueToModule(workspaceSlug.toString(), issue.project_detail.id, moduleId, issue.id); + moduleIssueStore.addIssueToModule(workspaceSlug.toString(), issue.project_detail.id, moduleId, [issue.id]); }; const handleLabels = (formData: Partial) => { issueUpdate({ ...issue, ...formData }); diff --git a/web/components/issues/modal.tsx b/web/components/issues/modal.tsx index d99be8616..239940ef1 100644 --- a/web/components/issues/modal.tsx +++ b/web/components/issues/modal.tsx @@ -171,13 +171,13 @@ export const CreateUpdateIssueModal: React.FC = observer((prop const addIssueToCycle = async (issueId: string, cycleId: string) => { if (!workspaceSlug || !activeProject) return; - cycleIssueStore.addIssueToCycle(workspaceSlug.toString(), activeProject, cycleId, issueId); + cycleIssueStore.addIssueToCycle(workspaceSlug.toString(), activeProject, cycleId, [issueId]); }; const addIssueToModule = async (issueId: string, moduleId: string) => { if (!workspaceSlug || !activeProject) return; - moduleIssueStore.addIssueToModule(workspaceSlug.toString(), activeProject, moduleId, issueId); + moduleIssueStore.addIssueToModule(workspaceSlug.toString(), activeProject, moduleId, [issueId]); }; const createIssue = async (payload: Partial) => { diff --git a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx index 775cfcd11..318d61cf5 100644 --- a/web/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx +++ b/web/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx @@ -1,19 +1,14 @@ -import { useState, ReactElement } from "react"; +import { ReactElement } from "react"; import { useRouter } from "next/router"; import useSWR from "swr"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; -// services -import { IssueService } from "services/issue"; // hooks import useLocalStorage from "hooks/use-local-storage"; -import useUser from "hooks/use-user"; -import useToast from "hooks/use-toast"; // layouts import { AppLayout } from "layouts/app-layout"; // components import { CycleIssuesHeader } from "components/headers"; -import { ExistingIssuesListModal } from "components/core"; import { CycleDetailsSidebar } from "components/cycles"; import { CycleLayoutRoot } from "components/issues/issue-layouts"; // ui @@ -21,23 +16,14 @@ import { EmptyState } from "components/common"; // assets import emptyCycle from "public/empty-state/cycle.svg"; // types -import { ISearchIssueResponse } from "types"; import { NextPageWithLayout } from "types/app"; -const issueService = new IssueService(); - const CycleDetailPage: NextPageWithLayout = () => { - const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false); - const router = useRouter(); const { workspaceSlug, projectId, cycleId } = router.query; const { cycle: cycleStore } = useMobxStore(); - const { user } = useUser(); - - const { setToastAlert } = useToast(); - const { setValue, storedValue } = useLocalStorage("cycle_sidebar_collapsed", "false"); const isSidebarCollapsed = storedValue ? (storedValue === "true" ? true : false) : false; @@ -52,38 +38,8 @@ const CycleDetailPage: NextPageWithLayout = () => { setValue(`${!isSidebarCollapsed}`); }; - // TODO: add this function to bulk add issues to cycle - const handleAddIssuesToCycle = async (data: ISearchIssueResponse[]) => { - if (!workspaceSlug || !projectId) return; - - const payload = { - issues: data.map((i) => i.id), - }; - - await issueService - .addIssueToCycle(workspaceSlug as string, projectId as string, cycleId as string, payload, user) - .catch(() => { - setToastAlert({ - type: "error", - title: "Error!", - message: "Selected issues could not be added to the cycle. Please try again.", - }); - }); - }; - - const openIssuesListModal = () => { - setCycleIssuesListModal(true); - }; - return ( <> - {/* TODO: Update logic to bulk add issues to a cycle */} - setCycleIssuesListModal(false)} - searchParams={{ cycle: true }} - handleOnSubmit={handleAddIssuesToCycle} - /> {error ? ( { <>
- +
{cycleId && !isSidebarCollapsed && (
{ - // states - const [moduleIssuesListModal, setModuleIssuesListModal] = useState(false); // router const router = useRouter(); const { workspaceSlug, projectId, moduleId } = router.query; // store const { module: moduleStore } = useMobxStore(); - // hooks - const { user } = useUser(); - const { setToastAlert } = useToast(); // local storage const { setValue, storedValue } = useLocalStorage("module_sidebar_collapsed", "false"); const isSidebarCollapsed = storedValue ? (storedValue === "true" ? true : false) : false; @@ -48,42 +35,12 @@ const ModuleIssuesPage: NextPageWithLayout = () => { : null ); - // TODO: add this function to bulk add issues to cycle - const handleAddIssuesToModule = async (data: ISearchIssueResponse[]) => { - if (!workspaceSlug || !projectId) return; - - const payload = { - issues: data.map((i) => i.id), - }; - - await moduleService - .addIssuesToModule(workspaceSlug as string, projectId as string, moduleId as string, payload, user) - .catch(() => - setToastAlert({ - type: "error", - title: "Error!", - message: "Selected issues could not be added to the module. Please try again.", - }) - ); - }; - - const openIssuesListModal = () => { - setModuleIssuesListModal(true); - }; - const toggleSidebar = () => { setValue(`${!isSidebarCollapsed}`); }; return ( <> - {/* TODO: Update logic to bulk add issues to a cycle */} - setModuleIssuesListModal(false)} - searchParams={{ module: true }} - handleOnSubmit={handleAddIssuesToModule} - /> {error ? ( { ) : (
- +
{moduleId && !isSidebarCollapsed && (
void; updateGanttIssueStructure: (workspaceSlug: string, cycleId: string, issue: IIssue, payload: IBlockUpdateData) => void; deleteIssue: (group_id: string | null, sub_group_id: string | null, issue: IIssue) => void; - addIssueToCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => void; + addIssueToCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => Promise; removeIssueFromCycle: (workspaceSlug: string, projectId: string, cycleId: string, bridgeId: string) => void; } @@ -322,7 +322,7 @@ export class CycleIssueStore implements ICycleIssueStore { } }; - addIssueToCycle = async (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => { + addIssueToCycle = async (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => { try { const user = this.rootStore.user.currentUser ?? undefined; @@ -331,7 +331,7 @@ export class CycleIssueStore implements ICycleIssueStore { projectId, cycleId, { - issues: [issueId], + issues: issueIds, }, user ); diff --git a/web/store/module/module_issue.store.ts b/web/store/module/module_issue.store.ts index 5b5893b0d..165b51b62 100644 --- a/web/store/module/module_issue.store.ts +++ b/web/store/module/module_issue.store.ts @@ -40,7 +40,7 @@ export interface IModuleIssueStore { payload: IBlockUpdateData ) => void; deleteIssue: (group_id: string | null, sub_group_id: string | null, issue: IIssue) => void; - addIssueToModule: (workspaceSlug: string, projectId: string, moduleId: string, issueId: string) => Promise; + addIssueToModule: (workspaceSlug: string, projectId: string, moduleId: string, issueIds: string[]) => Promise; removeIssueFromModule: (workspaceSlug: string, projectId: string, moduleId: string, bridgeId: string) => Promise; } @@ -337,7 +337,7 @@ export class ModuleIssueStore implements IModuleIssueStore { } }; - addIssueToModule = async (workspaceSlug: string, projectId: string, moduleId: string, issueId: string) => { + addIssueToModule = async (workspaceSlug: string, projectId: string, moduleId: string, issueIds: string[]) => { try { const user = this.rootStore.user.currentUser ?? undefined; @@ -346,7 +346,7 @@ export class ModuleIssueStore implements IModuleIssueStore { projectId, moduleId, { - issues: [issueId], + issues: issueIds, }, user );