[WEB-4828] refactor: remove legacy project-level draft work items components (#7694)
This commit is contained in:
parent
fd5ba6c7b8
commit
d960d7ce88
38 changed files with 16 additions and 1313 deletions
|
|
@ -1,178 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import { FC, useCallback } from "react";
|
|
||||||
import { observer } from "mobx-react";
|
|
||||||
import { useParams } from "next/navigation";
|
|
||||||
// plane constants
|
|
||||||
import { EIssueFilterType, ISSUE_DISPLAY_FILTERS_BY_PAGE } from "@plane/constants";
|
|
||||||
// i18n
|
|
||||||
import { useTranslation } from "@plane/i18n";
|
|
||||||
// types
|
|
||||||
import {
|
|
||||||
EIssuesStoreType,
|
|
||||||
IIssueDisplayFilterOptions,
|
|
||||||
IIssueDisplayProperties,
|
|
||||||
IIssueFilterOptions,
|
|
||||||
EIssueLayoutTypes,
|
|
||||||
} from "@plane/types";
|
|
||||||
// ui
|
|
||||||
import { Breadcrumbs, LayersIcon, Tooltip } from "@plane/ui";
|
|
||||||
// components
|
|
||||||
import { isIssueFilterActive } from "@plane/utils";
|
|
||||||
import { BreadcrumbLink } from "@/components/common/breadcrumb-link";
|
|
||||||
import {
|
|
||||||
DisplayFiltersSelection,
|
|
||||||
FiltersDropdown,
|
|
||||||
FilterSelection,
|
|
||||||
LayoutSelection,
|
|
||||||
} from "@/components/issues/issue-layouts/filters";
|
|
||||||
// helpers
|
|
||||||
// hooks
|
|
||||||
import { useIssues } from "@/hooks/store/use-issues";
|
|
||||||
import { useLabel } from "@/hooks/store/use-label";
|
|
||||||
import { useMember } from "@/hooks/store/use-member";
|
|
||||||
import { useProject } from "@/hooks/store/use-project";
|
|
||||||
import { useProjectState } from "@/hooks/store/use-project-state";
|
|
||||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
|
||||||
// plane web
|
|
||||||
import { ProjectBreadcrumb } from "@/plane-web/components/breadcrumbs/project";
|
|
||||||
|
|
||||||
// FIXME: Deprecated. Remove it
|
|
||||||
export const ProjectDraftIssueHeader: FC = observer(() => {
|
|
||||||
// i18n
|
|
||||||
const { t } = useTranslation();
|
|
||||||
// router
|
|
||||||
const { workspaceSlug, projectId } = useParams() as { workspaceSlug: string; projectId: string };
|
|
||||||
// store hooks
|
|
||||||
const {
|
|
||||||
issuesFilter: { issueFilters, updateFilters },
|
|
||||||
} = useIssues(EIssuesStoreType.DRAFT);
|
|
||||||
const { currentProjectDetails, loader } = useProject();
|
|
||||||
const { projectStates } = useProjectState();
|
|
||||||
const { projectLabels } = useLabel();
|
|
||||||
const {
|
|
||||||
project: { projectMemberIds },
|
|
||||||
} = useMember();
|
|
||||||
const { isMobile } = usePlatformOS();
|
|
||||||
const activeLayout = issueFilters?.displayFilters?.layout;
|
|
||||||
|
|
||||||
const handleFiltersUpdate = useCallback(
|
|
||||||
(key: keyof IIssueFilterOptions, value: string | string[]) => {
|
|
||||||
if (!workspaceSlug || !projectId) return;
|
|
||||||
const newValues = issueFilters?.filters?.[key] ?? [];
|
|
||||||
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
// this validation is majorly for the filter start_date, target_date custom
|
|
||||||
value.forEach((val) => {
|
|
||||||
if (!newValues.includes(val)) newValues.push(val);
|
|
||||||
else newValues.splice(newValues.indexOf(val), 1);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (issueFilters?.filters?.[key]?.includes(value)) newValues.splice(newValues.indexOf(value), 1);
|
|
||||||
else newValues.push(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.FILTERS, { [key]: newValues });
|
|
||||||
},
|
|
||||||
[workspaceSlug, projectId, issueFilters, updateFilters]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleLayoutChange = useCallback(
|
|
||||||
(layout: EIssueLayoutTypes) => {
|
|
||||||
if (!workspaceSlug || !projectId) return;
|
|
||||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_FILTERS, { layout: layout });
|
|
||||||
},
|
|
||||||
[workspaceSlug, projectId, updateFilters]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleDisplayFilters = useCallback(
|
|
||||||
(updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => {
|
|
||||||
if (!workspaceSlug || !projectId) return;
|
|
||||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_FILTERS, updatedDisplayFilter);
|
|
||||||
},
|
|
||||||
[workspaceSlug, projectId, updateFilters]
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleDisplayProperties = useCallback(
|
|
||||||
(property: Partial<IIssueDisplayProperties>) => {
|
|
||||||
if (!workspaceSlug || !projectId) return;
|
|
||||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_PROPERTIES, property);
|
|
||||||
},
|
|
||||||
[workspaceSlug, projectId, updateFilters]
|
|
||||||
);
|
|
||||||
|
|
||||||
const issueCount = undefined;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative z-10 flex h-header w-full flex-shrink-0 flex-row items-center justify-between gap-x-2 gap-y-4 bg-custom-sidebar-background-100 p-4">
|
|
||||||
<div className="flex w-full flex-grow items-center gap-2 overflow-ellipsis whitespace-nowrap">
|
|
||||||
<div className="flex items-center gap-2.5">
|
|
||||||
<Breadcrumbs isLoading={loader === "init-loader"}>
|
|
||||||
<ProjectBreadcrumb workspaceSlug={workspaceSlug} projectId={projectId} />
|
|
||||||
|
|
||||||
<Breadcrumbs.Item
|
|
||||||
component={
|
|
||||||
<BreadcrumbLink
|
|
||||||
label="Draft work items"
|
|
||||||
icon={<LayersIcon className="h-4 w-4 text-custom-text-300" />}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Breadcrumbs>
|
|
||||||
{issueCount && issueCount > 0 ? (
|
|
||||||
<Tooltip
|
|
||||||
isMobile={isMobile}
|
|
||||||
tooltipContent={`There are ${issueCount} ${issueCount > 1 ? "work items" : "work item"} in project's draft`}
|
|
||||||
position="bottom"
|
|
||||||
>
|
|
||||||
<span className="cursor-default flex items-center text-center justify-center px-2.5 py-0.5 flex-shrink-0 bg-custom-primary-100/20 text-custom-primary-100 text-xs font-semibold rounded-xl">
|
|
||||||
{issueCount}
|
|
||||||
</span>
|
|
||||||
</Tooltip>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="ml-auto flex items-center gap-2">
|
|
||||||
<LayoutSelection
|
|
||||||
layouts={[EIssueLayoutTypes.LIST, EIssueLayoutTypes.KANBAN]}
|
|
||||||
onChange={(layout) => handleLayoutChange(layout)}
|
|
||||||
selectedLayout={activeLayout}
|
|
||||||
/>
|
|
||||||
<FiltersDropdown
|
|
||||||
title={t("common.filters")}
|
|
||||||
placement="bottom-end"
|
|
||||||
isFiltersApplied={isIssueFilterActive(issueFilters)}
|
|
||||||
>
|
|
||||||
<FilterSelection
|
|
||||||
filters={issueFilters?.filters ?? {}}
|
|
||||||
handleFiltersUpdate={handleFiltersUpdate}
|
|
||||||
displayFilters={issueFilters?.displayFilters ?? {}}
|
|
||||||
handleDisplayFiltersUpdate={handleDisplayFilters}
|
|
||||||
layoutDisplayFiltersOptions={
|
|
||||||
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_PAGE.issues[activeLayout] : undefined
|
|
||||||
}
|
|
||||||
labels={projectLabels}
|
|
||||||
memberIds={projectMemberIds ?? undefined}
|
|
||||||
states={projectStates}
|
|
||||||
cycleViewDisabled={!currentProjectDetails?.cycle_view}
|
|
||||||
moduleViewDisabled={!currentProjectDetails?.module_view}
|
|
||||||
/>
|
|
||||||
</FiltersDropdown>
|
|
||||||
<FiltersDropdown title={t("common.display")} placement="bottom-end">
|
|
||||||
<DisplayFiltersSelection
|
|
||||||
layoutDisplayFiltersOptions={
|
|
||||||
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_PAGE.issues[activeLayout] : undefined
|
|
||||||
}
|
|
||||||
displayFilters={issueFilters?.displayFilters ?? {}}
|
|
||||||
handleDisplayFiltersUpdate={handleDisplayFilters}
|
|
||||||
displayProperties={issueFilters?.displayProperties ?? {}}
|
|
||||||
handleDisplayPropertiesUpdate={handleDisplayProperties}
|
|
||||||
cycleViewDisabled={!currentProjectDetails?.cycle_view}
|
|
||||||
moduleViewDisabled={!currentProjectDetails?.module_view}
|
|
||||||
/>
|
|
||||||
</FiltersDropdown>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
// components
|
|
||||||
import { AppHeader } from "@/components/core/app-header";
|
|
||||||
import { ContentWrapper } from "@/components/core/content-wrapper";
|
|
||||||
import { ProjectDraftIssueHeader } from "./header";
|
|
||||||
|
|
||||||
export default function ProjectDraftIssuesLayou({ children }: { children: React.ReactNode }) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<AppHeader header={<ProjectDraftIssueHeader />} />
|
|
||||||
<ContentWrapper>{children}</ContentWrapper>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import { observer } from "mobx-react";
|
|
||||||
import { useParams } from "next/navigation";
|
|
||||||
import { X, PenSquare } from "lucide-react";
|
|
||||||
// components
|
|
||||||
import { PageHead } from "@/components/core/page-title";
|
|
||||||
import { DraftIssueLayoutRoot } from "@/components/issues/issue-layouts/roots/draft-issue-layout-root";
|
|
||||||
// hooks
|
|
||||||
import { useProject } from "@/hooks/store/use-project";
|
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
|
||||||
|
|
||||||
const ProjectDraftIssuesPage = observer(() => {
|
|
||||||
const router = useAppRouter();
|
|
||||||
const { workspaceSlug, projectId } = useParams();
|
|
||||||
// store
|
|
||||||
const { getProjectById } = useProject();
|
|
||||||
// derived values
|
|
||||||
const project = projectId ? getProjectById(projectId.toString()) : undefined;
|
|
||||||
const pageTitle = project?.name ? `${project?.name} - Draft work items` : undefined;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PageHead title={pageTitle} />
|
|
||||||
<div className="flex h-full w-full flex-col">
|
|
||||||
<div className="gap-1 flex items-center border-b border-custom-border-200 px-4 py-2.5 shadow-sm bg-custom-background-100 z-[12]">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => router.push(`/${workspaceSlug}/projects/${projectId}/issues/`)}
|
|
||||||
className="flex items-center gap-1.5 rounded-full border border-custom-border-200 px-3 py-1.5 text-xs"
|
|
||||||
>
|
|
||||||
<PenSquare className="h-4 w-4" />
|
|
||||||
<span>Draft work items</span>
|
|
||||||
<X className="h-3 w-3" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<DraftIssueLayoutRoot />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default ProjectDraftIssuesPage;
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useParams, usePathname } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
// plane imports
|
// plane imports
|
||||||
import { EIssueServiceType, EIssuesStoreType, TIssue } from "@plane/types";
|
import { EIssueServiceType, EIssuesStoreType, TIssue } from "@plane/types";
|
||||||
// components
|
// components
|
||||||
|
|
@ -22,7 +22,6 @@ export type TIssueLevelModalsProps = {
|
||||||
export const IssueLevelModals: FC<TIssueLevelModalsProps> = observer((props) => {
|
export const IssueLevelModals: FC<TIssueLevelModalsProps> = observer((props) => {
|
||||||
const { projectId, issueId } = props;
|
const { projectId, issueId } = props;
|
||||||
// router
|
// router
|
||||||
const pathname = usePathname();
|
|
||||||
const { workspaceSlug, cycleId, moduleId } = useParams();
|
const { workspaceSlug, cycleId, moduleId } = useParams();
|
||||||
const router = useAppRouter();
|
const router = useAppRouter();
|
||||||
// store hooks
|
// store hooks
|
||||||
|
|
@ -45,7 +44,6 @@ export const IssueLevelModals: FC<TIssueLevelModalsProps> = observer((props) =>
|
||||||
} = useCommandPalette();
|
} = useCommandPalette();
|
||||||
// derived values
|
// derived values
|
||||||
const issueDetails = issueId ? getIssueById(issueId) : undefined;
|
const issueDetails = issueId ? getIssueById(issueId) : undefined;
|
||||||
const isDraftIssue = pathname?.includes("draft-issues") || false;
|
|
||||||
const { fetchSubIssues: fetchSubWorkItems } = useIssueDetail();
|
const { fetchSubIssues: fetchSubWorkItems } = useIssueDetail();
|
||||||
const { fetchSubIssues: fetchEpicSubWorkItems } = useIssueDetail(EIssueServiceType.EPICS);
|
const { fetchSubIssues: fetchEpicSubWorkItems } = useIssueDetail(EIssueServiceType.EPICS);
|
||||||
|
|
||||||
|
|
@ -81,7 +79,6 @@ export const IssueLevelModals: FC<TIssueLevelModalsProps> = observer((props) =>
|
||||||
isOpen={isCreateIssueModalOpen}
|
isOpen={isCreateIssueModalOpen}
|
||||||
onClose={() => toggleCreateIssueModal(false)}
|
onClose={() => toggleCreateIssueModal(false)}
|
||||||
data={getCreateIssueModalData()}
|
data={getCreateIssueModalData()}
|
||||||
isDraft={isDraftIssue}
|
|
||||||
onSubmit={handleCreateIssueSubmit}
|
onSubmit={handleCreateIssueSubmit}
|
||||||
allowedProjectIds={createWorkItemAllowedProjectIds}
|
allowedProjectIds={createWorkItemAllowedProjectIds}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
import { observer } from "mobx-react";
|
|
||||||
|
|
||||||
// FIXME: Project drafts is deprecated. Remove this component and all the related code.
|
|
||||||
export const ProjectDraftEmptyState: React.FC = observer(() => (
|
|
||||||
<div className="relative h-full w-full overflow-y-auto" />
|
|
||||||
));
|
|
||||||
|
|
@ -5,7 +5,6 @@ import { TeamProjectWorkItemEmptyState } from "@/plane-web/components/issues/iss
|
||||||
// components
|
// components
|
||||||
import { ProjectArchivedEmptyState } from "./archived-issues";
|
import { ProjectArchivedEmptyState } from "./archived-issues";
|
||||||
import { CycleEmptyState } from "./cycle";
|
import { CycleEmptyState } from "./cycle";
|
||||||
import { ProjectDraftEmptyState } from "./draft-issues";
|
|
||||||
import { GlobalViewEmptyState } from "./global-view";
|
import { GlobalViewEmptyState } from "./global-view";
|
||||||
import { ModuleEmptyState } from "./module";
|
import { ModuleEmptyState } from "./module";
|
||||||
import { ProfileViewEmptyState } from "./profile-view";
|
import { ProfileViewEmptyState } from "./profile-view";
|
||||||
|
|
@ -29,8 +28,6 @@ export const IssueLayoutEmptyState = (props: Props) => {
|
||||||
return <CycleEmptyState />;
|
return <CycleEmptyState />;
|
||||||
case EIssuesStoreType.MODULE:
|
case EIssuesStoreType.MODULE:
|
||||||
return <ModuleEmptyState />;
|
return <ModuleEmptyState />;
|
||||||
case EIssuesStoreType.DRAFT:
|
|
||||||
return <ProjectDraftEmptyState />;
|
|
||||||
case EIssuesStoreType.GLOBAL:
|
case EIssuesStoreType.GLOBAL:
|
||||||
return <GlobalViewEmptyState />;
|
return <GlobalViewEmptyState />;
|
||||||
case EIssuesStoreType.PROFILE:
|
case EIssuesStoreType.PROFILE:
|
||||||
|
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
import { observer } from "mobx-react";
|
|
||||||
import { useParams } from "next/navigation";
|
|
||||||
// plane imports
|
|
||||||
import { EIssueFilterType } from "@plane/constants";
|
|
||||||
import { EIssuesStoreType, IIssueFilterOptions } from "@plane/types";
|
|
||||||
// hooks
|
|
||||||
import { useIssues } from "@/hooks/store/use-issues";
|
|
||||||
import { useLabel } from "@/hooks/store/use-label";
|
|
||||||
import { useProjectState } from "@/hooks/store/use-project-state";
|
|
||||||
// local imports
|
|
||||||
import { AppliedFiltersList } from "../filters-list";
|
|
||||||
|
|
||||||
export const DraftIssueAppliedFiltersRoot: React.FC = observer(() => {
|
|
||||||
// router
|
|
||||||
const { workspaceSlug, projectId } = useParams() as { workspaceSlug: string; projectId: string };
|
|
||||||
// store hooks
|
|
||||||
const {
|
|
||||||
issuesFilter: { issueFilters, updateFilters },
|
|
||||||
} = useIssues(EIssuesStoreType.DRAFT);
|
|
||||||
const { projectLabels } = useLabel();
|
|
||||||
const { projectStates } = useProjectState();
|
|
||||||
// derived values
|
|
||||||
const userFilters = issueFilters?.filters;
|
|
||||||
// filters whose value not null or empty array
|
|
||||||
const appliedFilters: IIssueFilterOptions = {};
|
|
||||||
Object.entries(userFilters ?? {}).forEach(([key, value]) => {
|
|
||||||
if (!value) return;
|
|
||||||
if (Array.isArray(value) && value.length === 0) return;
|
|
||||||
appliedFilters[key as keyof IIssueFilterOptions] = value;
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleRemoveFilter = (key: keyof IIssueFilterOptions, value: string | null) => {
|
|
||||||
if (!workspaceSlug || !projectId) return;
|
|
||||||
|
|
||||||
// remove all values of the key if value is null
|
|
||||||
if (!value) {
|
|
||||||
updateFilters(workspaceSlug.toString(), projectId.toString(), EIssueFilterType.FILTERS, {
|
|
||||||
[key]: null,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove the passed value from the key
|
|
||||||
let newValues = issueFilters?.filters?.[key] ?? [];
|
|
||||||
newValues = newValues.filter((val) => val !== value);
|
|
||||||
|
|
||||||
updateFilters(workspaceSlug.toString(), projectId.toString(), EIssueFilterType.FILTERS, {
|
|
||||||
[key]: newValues,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClearAllFilters = () => {
|
|
||||||
if (!workspaceSlug || !projectId) return;
|
|
||||||
|
|
||||||
const newFilters: IIssueFilterOptions = {};
|
|
||||||
Object.keys(userFilters ?? {}).forEach((key) => {
|
|
||||||
newFilters[key as keyof IIssueFilterOptions] = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
updateFilters(workspaceSlug.toString(), projectId.toString(), EIssueFilterType.FILTERS, { ...newFilters });
|
|
||||||
};
|
|
||||||
|
|
||||||
// return if no filters are applied
|
|
||||||
if (Object.keys(appliedFilters).length === 0) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex justify-between p-4 gap-2.5">
|
|
||||||
<AppliedFiltersList
|
|
||||||
appliedFilters={appliedFilters}
|
|
||||||
handleClearAllFilters={handleClearAllFilters}
|
|
||||||
handleRemoveFilter={handleRemoveFilter}
|
|
||||||
labels={projectLabels ?? []}
|
|
||||||
states={projectStates}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
@ -34,7 +34,6 @@ export type KanbanStoreType =
|
||||||
| EIssuesStoreType.MODULE
|
| EIssuesStoreType.MODULE
|
||||||
| EIssuesStoreType.CYCLE
|
| EIssuesStoreType.CYCLE
|
||||||
| EIssuesStoreType.PROJECT_VIEW
|
| EIssuesStoreType.PROJECT_VIEW
|
||||||
| EIssuesStoreType.DRAFT
|
|
||||||
| EIssuesStoreType.PROFILE
|
| EIssuesStoreType.PROFILE
|
||||||
| EIssuesStoreType.TEAM
|
| EIssuesStoreType.TEAM
|
||||||
| EIssuesStoreType.TEAM_VIEW
|
| EIssuesStoreType.TEAM_VIEW
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useParams, usePathname } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
// lucide icons
|
// lucide icons
|
||||||
import { Minimize2, Maximize2, Circle, Plus } from "lucide-react";
|
import { Minimize2, Maximize2, Circle, Plus } from "lucide-react";
|
||||||
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||||
|
|
@ -58,9 +58,6 @@ export const HeaderGroupByCard: FC<IHeaderGroupByCard> = observer((props) => {
|
||||||
const storeType = useIssueStoreType();
|
const storeType = useIssueStoreType();
|
||||||
// router
|
// router
|
||||||
const { workspaceSlug, projectId, moduleId, cycleId } = useParams();
|
const { workspaceSlug, projectId, moduleId, cycleId } = useParams();
|
||||||
const pathname = usePathname();
|
|
||||||
|
|
||||||
const isDraftIssue = pathname.includes("draft-issue");
|
|
||||||
|
|
||||||
const renderExistingIssueModal = moduleId || cycleId;
|
const renderExistingIssueModal = moduleId || cycleId;
|
||||||
const ExistingIssuesListModalPayload = moduleId ? { module: moduleId.toString() } : { cycle: true };
|
const ExistingIssuesListModalPayload = moduleId ? { module: moduleId.toString() } : { cycle: true };
|
||||||
|
|
@ -97,7 +94,6 @@ export const HeaderGroupByCard: FC<IHeaderGroupByCard> = observer((props) => {
|
||||||
onClose={() => setIsOpen(false)}
|
onClose={() => setIsOpen(false)}
|
||||||
data={issuePayload}
|
data={issuePayload}
|
||||||
storeType={storeType}
|
storeType={storeType}
|
||||||
isDraft={isDraftIssue}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
import { observer } from "mobx-react";
|
|
||||||
// local imports
|
|
||||||
import { DraftIssueQuickActions } from "../../quick-action-dropdowns";
|
|
||||||
import { BaseKanBanRoot } from "../base-kanban-root";
|
|
||||||
|
|
||||||
export const DraftKanBanLayout: React.FC = observer(() => <BaseKanBanRoot QuickActions={DraftIssueQuickActions} />);
|
|
||||||
|
|
@ -30,7 +30,6 @@ type ListStoreType =
|
||||||
| EIssuesStoreType.MODULE
|
| EIssuesStoreType.MODULE
|
||||||
| EIssuesStoreType.CYCLE
|
| EIssuesStoreType.CYCLE
|
||||||
| EIssuesStoreType.PROJECT_VIEW
|
| EIssuesStoreType.PROJECT_VIEW
|
||||||
| EIssuesStoreType.DRAFT
|
|
||||||
| EIssuesStoreType.PROFILE
|
| EIssuesStoreType.PROFILE
|
||||||
| EIssuesStoreType.ARCHIVED
|
| EIssuesStoreType.ARCHIVED
|
||||||
| EIssuesStoreType.WORKSPACE_DRAFT
|
| EIssuesStoreType.WORKSPACE_DRAFT
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useParams, usePathname } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { CircleDashed, Plus } from "lucide-react";
|
import { CircleDashed, Plus } from "lucide-react";
|
||||||
// types
|
// types
|
||||||
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
import { WORK_ITEM_TRACKER_EVENTS } from "@plane/constants";
|
||||||
|
|
@ -58,10 +58,8 @@ export const HeaderGroupByCard = observer((props: IHeaderGroupByCard) => {
|
||||||
const [openExistingIssueListModal, setOpenExistingIssueListModal] = useState(false);
|
const [openExistingIssueListModal, setOpenExistingIssueListModal] = useState(false);
|
||||||
// router
|
// router
|
||||||
const { workspaceSlug, projectId, moduleId, cycleId } = useParams();
|
const { workspaceSlug, projectId, moduleId, cycleId } = useParams();
|
||||||
const pathname = usePathname();
|
|
||||||
const storeType = useIssueStoreType();
|
const storeType = useIssueStoreType();
|
||||||
// derived values
|
// derived values
|
||||||
const isDraftIssue = pathname.includes("draft-issue");
|
|
||||||
const renderExistingIssueModal = moduleId || cycleId;
|
const renderExistingIssueModal = moduleId || cycleId;
|
||||||
const existingIssuesListModalPayload = moduleId ? { module: moduleId.toString() } : { cycle: true };
|
const existingIssuesListModalPayload = moduleId ? { module: moduleId.toString() } : { cycle: true };
|
||||||
const isGroupSelectionEmpty = selectionHelpers.isGroupSelected(groupID) === "empty";
|
const isGroupSelectionEmpty = selectionHelpers.isGroupSelected(groupID) === "empty";
|
||||||
|
|
@ -167,7 +165,6 @@ export const HeaderGroupByCard = observer((props: IHeaderGroupByCard) => {
|
||||||
onClose={() => setIsOpen(false)}
|
onClose={() => setIsOpen(false)}
|
||||||
data={issuePayload}
|
data={issuePayload}
|
||||||
storeType={storeType}
|
storeType={storeType}
|
||||||
isDraft={isDraftIssue}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
import { FC } from "react";
|
|
||||||
import { observer } from "mobx-react";
|
|
||||||
import { useParams } from "next/navigation";
|
|
||||||
// local imports
|
|
||||||
import { DraftIssueQuickActions } from "../../quick-action-dropdowns";
|
|
||||||
import { BaseListRoot } from "../base-list-root";
|
|
||||||
|
|
||||||
export const DraftIssueListLayout: FC = observer(() => {
|
|
||||||
const { workspaceSlug, projectId } = useParams();
|
|
||||||
|
|
||||||
if (!workspaceSlug || !projectId) return null;
|
|
||||||
|
|
||||||
return <BaseListRoot QuickActions={DraftIssueQuickActions} />;
|
|
||||||
});
|
|
||||||
|
|
@ -118,7 +118,6 @@ export const AllIssueQuickActions: React.FC<IQuickActionProps> = observer((props
|
||||||
if (issueToEdit && handleUpdate) await handleUpdate(data);
|
if (issueToEdit && handleUpdate) await handleUpdate(data);
|
||||||
}}
|
}}
|
||||||
storeType={EIssuesStoreType.GLOBAL}
|
storeType={EIssuesStoreType.GLOBAL}
|
||||||
isDraft={false}
|
|
||||||
/>
|
/>
|
||||||
{issue.project_id && workspaceSlug && (
|
{issue.project_id && workspaceSlug && (
|
||||||
<DuplicateWorkItemModal
|
<DuplicateWorkItemModal
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,6 @@ export const CycleIssueQuickActions: React.FC<IQuickActionProps> = observer((pro
|
||||||
if (issueToEdit && handleUpdate) await handleUpdate(data);
|
if (issueToEdit && handleUpdate) await handleUpdate(data);
|
||||||
}}
|
}}
|
||||||
storeType={EIssuesStoreType.CYCLE}
|
storeType={EIssuesStoreType.CYCLE}
|
||||||
isDraft={false}
|
|
||||||
/>
|
/>
|
||||||
{issue.project_id && workspaceSlug && (
|
{issue.project_id && workspaceSlug && (
|
||||||
<DuplicateWorkItemModal
|
<DuplicateWorkItemModal
|
||||||
|
|
|
||||||
|
|
@ -1,163 +0,0 @@
|
||||||
"use client";
|
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
import omit from "lodash/omit";
|
|
||||||
import { observer } from "mobx-react";
|
|
||||||
import { useParams, usePathname } from "next/navigation";
|
|
||||||
// plane imports
|
|
||||||
import { EUserPermissions, EUserPermissionsLevel, WORK_ITEM_TRACKER_ELEMENTS } from "@plane/constants";
|
|
||||||
import { EIssuesStoreType, TIssue } from "@plane/types";
|
|
||||||
import { ContextMenu, CustomMenu, TContextMenuItem } from "@plane/ui";
|
|
||||||
import { cn } from "@plane/utils";
|
|
||||||
// hooks
|
|
||||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
|
||||||
import { useUserPermissions } from "@/hooks/store/user";
|
|
||||||
// local imports
|
|
||||||
import { DeleteIssueModal } from "../../delete-issue-modal";
|
|
||||||
import { CreateUpdateIssueModal } from "../../issue-modal/modal";
|
|
||||||
import { IQuickActionProps } from "../list/list-view-types";
|
|
||||||
import { useDraftIssueMenuItems, MenuItemFactoryProps } from "./helper";
|
|
||||||
|
|
||||||
export const DraftIssueQuickActions: React.FC<IQuickActionProps> = observer((props) => {
|
|
||||||
const {
|
|
||||||
issue,
|
|
||||||
handleDelete,
|
|
||||||
handleUpdate,
|
|
||||||
customActionButton,
|
|
||||||
portalElement,
|
|
||||||
readOnly = false,
|
|
||||||
placements = "bottom-end",
|
|
||||||
parentRef,
|
|
||||||
} = props;
|
|
||||||
// router
|
|
||||||
const { workspaceSlug } = useParams();
|
|
||||||
const pathname = usePathname();
|
|
||||||
// states
|
|
||||||
const [createUpdateIssueModal, setCreateUpdateIssueModal] = useState(false);
|
|
||||||
const [issueToEdit, setIssueToEdit] = useState<TIssue | undefined>(undefined);
|
|
||||||
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
|
|
||||||
// store hooks
|
|
||||||
const { allowPermissions } = useUserPermissions();
|
|
||||||
// derived values
|
|
||||||
const activeLayout = "Draft Issues";
|
|
||||||
// auth
|
|
||||||
const isEditingAllowed =
|
|
||||||
allowPermissions(
|
|
||||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
|
||||||
EUserPermissionsLevel.PROJECT,
|
|
||||||
workspaceSlug?.toString(),
|
|
||||||
issue.project_id ?? undefined
|
|
||||||
) && !readOnly;
|
|
||||||
const isDeletingAllowed = isEditingAllowed;
|
|
||||||
|
|
||||||
const isDraftIssue = pathname?.includes("draft-issues") || false;
|
|
||||||
|
|
||||||
const duplicateIssuePayload = omit(
|
|
||||||
{
|
|
||||||
...issue,
|
|
||||||
name: `${issue.name} (copy)`,
|
|
||||||
is_draft: isDraftIssue ? false : issue.is_draft,
|
|
||||||
sourceIssueId: issue.id,
|
|
||||||
},
|
|
||||||
["id"]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Menu items and modals using helper
|
|
||||||
const menuItemProps: MenuItemFactoryProps = {
|
|
||||||
issue,
|
|
||||||
workspaceSlug: workspaceSlug?.toString(),
|
|
||||||
activeLayout,
|
|
||||||
isEditingAllowed,
|
|
||||||
isDeletingAllowed,
|
|
||||||
isDraftIssue,
|
|
||||||
setIssueToEdit,
|
|
||||||
setCreateUpdateIssueModal,
|
|
||||||
setDeleteIssueModal,
|
|
||||||
handleDelete,
|
|
||||||
handleUpdate,
|
|
||||||
storeType: EIssuesStoreType.DRAFT,
|
|
||||||
};
|
|
||||||
|
|
||||||
const MENU_ITEMS = useDraftIssueMenuItems(menuItemProps);
|
|
||||||
|
|
||||||
const CONTEXT_MENU_ITEMS: TContextMenuItem[] = MENU_ITEMS.map((item) => ({
|
|
||||||
...item,
|
|
||||||
onClick: () => {
|
|
||||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.QUICK_ACTIONS.DRAFT });
|
|
||||||
item.action();
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{/* Modals */}
|
|
||||||
<DeleteIssueModal
|
|
||||||
data={issue}
|
|
||||||
isOpen={deleteIssueModal}
|
|
||||||
handleClose={() => setDeleteIssueModal(false)}
|
|
||||||
onSubmit={handleDelete}
|
|
||||||
/>
|
|
||||||
<CreateUpdateIssueModal
|
|
||||||
isOpen={createUpdateIssueModal}
|
|
||||||
onClose={() => {
|
|
||||||
setCreateUpdateIssueModal(false);
|
|
||||||
setIssueToEdit(undefined);
|
|
||||||
}}
|
|
||||||
data={issueToEdit ?? duplicateIssuePayload}
|
|
||||||
onSubmit={async (data) => {
|
|
||||||
if (issueToEdit && handleUpdate) await handleUpdate(data);
|
|
||||||
}}
|
|
||||||
storeType={EIssuesStoreType.DRAFT}
|
|
||||||
isDraft={isDraftIssue}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<ContextMenu parentRef={parentRef} items={CONTEXT_MENU_ITEMS} />
|
|
||||||
<CustomMenu
|
|
||||||
ellipsis
|
|
||||||
placement={placements}
|
|
||||||
customButton={customActionButton}
|
|
||||||
portalElement={portalElement}
|
|
||||||
menuItemsClassName="z-[14]"
|
|
||||||
maxHeight="lg"
|
|
||||||
closeOnSelect
|
|
||||||
>
|
|
||||||
{MENU_ITEMS.map((item) => {
|
|
||||||
if (item.shouldRender === false) return null;
|
|
||||||
return (
|
|
||||||
<CustomMenu.MenuItem
|
|
||||||
key={item.key}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
captureClick({ elementName: WORK_ITEM_TRACKER_ELEMENTS.QUICK_ACTIONS.DRAFT });
|
|
||||||
item.action();
|
|
||||||
}}
|
|
||||||
className={cn(
|
|
||||||
"flex items-center gap-2",
|
|
||||||
{
|
|
||||||
"text-custom-text-400": item.disabled,
|
|
||||||
},
|
|
||||||
item.className
|
|
||||||
)}
|
|
||||||
disabled={item.disabled}
|
|
||||||
>
|
|
||||||
{item.icon && <item.icon className={cn("h-3 w-3", item.iconClassName)} />}
|
|
||||||
<div>
|
|
||||||
<h5>{item.title}</h5>
|
|
||||||
{item.description && (
|
|
||||||
<p
|
|
||||||
className={cn("text-custom-text-300 whitespace-pre-line", {
|
|
||||||
"text-custom-text-400": item.disabled,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{item.description}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</CustomMenu.MenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</CustomMenu>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
@ -54,7 +54,6 @@ export interface MenuItemFactoryProps {
|
||||||
isRestoringAllowed?: boolean;
|
isRestoringAllowed?: boolean;
|
||||||
isInArchivableGroup?: boolean;
|
isInArchivableGroup?: boolean;
|
||||||
issueTypeDetail?: { is_active?: boolean };
|
issueTypeDetail?: { is_active?: boolean };
|
||||||
isDraftIssue?: boolean;
|
|
||||||
// Action handlers
|
// Action handlers
|
||||||
setIssueToEdit: (issue: TIssue | undefined) => void;
|
setIssueToEdit: (issue: TIssue | undefined) => void;
|
||||||
setCreateUpdateIssueModal: (open: boolean) => void;
|
setCreateUpdateIssueModal: (open: boolean) => void;
|
||||||
|
|
@ -369,9 +368,3 @@ export const useArchivedIssueMenuItems = (props: MenuItemFactoryProps): TContext
|
||||||
[factory]
|
[factory]
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useDraftIssueMenuItems = (props: MenuItemFactoryProps): TContextMenuItem[] => {
|
|
||||||
const factory = useMenuItemFactory(props);
|
|
||||||
|
|
||||||
return useMemo(() => [factory.createEditMenuItem(), factory.createDeleteMenuItem()], [factory]);
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
export * from "./all-issue";
|
export * from "./all-issue";
|
||||||
export * from "./archived-issue";
|
export * from "./archived-issue";
|
||||||
export * from "./cycle-issue";
|
export * from "./cycle-issue";
|
||||||
export * from "./draft-issue";
|
|
||||||
export * from "./module-issue";
|
export * from "./module-issue";
|
||||||
export * from "./project-issue";
|
export * from "./project-issue";
|
||||||
export * from "./helper";
|
export * from "./helper";
|
||||||
|
|
|
||||||
|
|
@ -88,13 +88,10 @@ export const WorkItemDetailQuickActions: React.FC<TWorkItemDetailQuickActionProp
|
||||||
|
|
||||||
const isDeletingAllowed = isEditingAllowed;
|
const isDeletingAllowed = isEditingAllowed;
|
||||||
|
|
||||||
const isDraftIssue = pathname?.includes("draft-issues") || false;
|
|
||||||
|
|
||||||
const duplicateIssuePayload = omit(
|
const duplicateIssuePayload = omit(
|
||||||
{
|
{
|
||||||
...issue,
|
...issue,
|
||||||
name: `${issue.name} (copy)`,
|
name: `${issue.name} (copy)`,
|
||||||
is_draft: isDraftIssue ? false : issue.is_draft,
|
|
||||||
sourceIssueId: issue.id,
|
sourceIssueId: issue.id,
|
||||||
},
|
},
|
||||||
["id"]
|
["id"]
|
||||||
|
|
@ -137,7 +134,6 @@ export const WorkItemDetailQuickActions: React.FC<TWorkItemDetailQuickActionProp
|
||||||
isRestoringAllowed,
|
isRestoringAllowed,
|
||||||
isDeletingAllowed,
|
isDeletingAllowed,
|
||||||
isInArchivableGroup,
|
isInArchivableGroup,
|
||||||
isDraftIssue,
|
|
||||||
setIssueToEdit,
|
setIssueToEdit,
|
||||||
setCreateUpdateIssueModal: customEditAction,
|
setCreateUpdateIssueModal: customEditAction,
|
||||||
setDeleteIssueModal: customDeleteAction,
|
setDeleteIssueModal: customDeleteAction,
|
||||||
|
|
@ -220,7 +216,6 @@ export const WorkItemDetailQuickActions: React.FC<TWorkItemDetailQuickActionProp
|
||||||
if (issueToEdit && handleUpdate) await handleUpdate(data);
|
if (issueToEdit && handleUpdate) await handleUpdate(data);
|
||||||
}}
|
}}
|
||||||
storeType={EIssuesStoreType.PROJECT}
|
storeType={EIssuesStoreType.PROJECT}
|
||||||
isDraft={isDraftIssue}
|
|
||||||
fetchIssueDetails={false}
|
fetchIssueDetails={false}
|
||||||
/>
|
/>
|
||||||
{issue.project_id && workspaceSlug && (
|
{issue.project_id && workspaceSlug && (
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,6 @@ export const ModuleIssueQuickActions: React.FC<IQuickActionProps> = observer((pr
|
||||||
if (issueToEdit && handleUpdate) await handleUpdate(data);
|
if (issueToEdit && handleUpdate) await handleUpdate(data);
|
||||||
}}
|
}}
|
||||||
storeType={EIssuesStoreType.MODULE}
|
storeType={EIssuesStoreType.MODULE}
|
||||||
isDraft={false}
|
|
||||||
/>
|
/>
|
||||||
{issue.project_id && workspaceSlug && (
|
{issue.project_id && workspaceSlug && (
|
||||||
<DuplicateWorkItemModal
|
<DuplicateWorkItemModal
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import omit from "lodash/omit";
|
import omit from "lodash/omit";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { useParams, usePathname } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
// plane imports
|
// plane imports
|
||||||
import {
|
import {
|
||||||
ARCHIVABLE_STATE_GROUPS,
|
ARCHIVABLE_STATE_GROUPS,
|
||||||
|
|
@ -43,7 +43,6 @@ export const ProjectIssueQuickActions: React.FC<IQuickActionProps> = observer((p
|
||||||
} = props;
|
} = props;
|
||||||
// router
|
// router
|
||||||
const { workspaceSlug } = useParams();
|
const { workspaceSlug } = useParams();
|
||||||
const pathname = usePathname();
|
|
||||||
// states
|
// states
|
||||||
const [createUpdateIssueModal, setCreateUpdateIssueModal] = useState(false);
|
const [createUpdateIssueModal, setCreateUpdateIssueModal] = useState(false);
|
||||||
const [issueToEdit, setIssueToEdit] = useState<TIssue | undefined>(undefined);
|
const [issueToEdit, setIssueToEdit] = useState<TIssue | undefined>(undefined);
|
||||||
|
|
@ -71,13 +70,10 @@ export const ProjectIssueQuickActions: React.FC<IQuickActionProps> = observer((p
|
||||||
const isInArchivableGroup = !!stateDetails && ARCHIVABLE_STATE_GROUPS.includes(stateDetails?.group);
|
const isInArchivableGroup = !!stateDetails && ARCHIVABLE_STATE_GROUPS.includes(stateDetails?.group);
|
||||||
const isDeletingAllowed = isEditingAllowed;
|
const isDeletingAllowed = isEditingAllowed;
|
||||||
|
|
||||||
const isDraftIssue = pathname?.includes("draft-issues") || false;
|
|
||||||
|
|
||||||
const duplicateIssuePayload = omit(
|
const duplicateIssuePayload = omit(
|
||||||
{
|
{
|
||||||
...issue,
|
...issue,
|
||||||
name: `${issue.name} (copy)`,
|
name: `${issue.name} (copy)`,
|
||||||
is_draft: isDraftIssue ? false : issue.is_draft,
|
|
||||||
sourceIssueId: issue.id,
|
sourceIssueId: issue.id,
|
||||||
},
|
},
|
||||||
["id"]
|
["id"]
|
||||||
|
|
@ -93,7 +89,6 @@ export const ProjectIssueQuickActions: React.FC<IQuickActionProps> = observer((p
|
||||||
isArchivingAllowed,
|
isArchivingAllowed,
|
||||||
isDeletingAllowed,
|
isDeletingAllowed,
|
||||||
isInArchivableGroup,
|
isInArchivableGroup,
|
||||||
isDraftIssue,
|
|
||||||
setIssueToEdit,
|
setIssueToEdit,
|
||||||
setCreateUpdateIssueModal,
|
setCreateUpdateIssueModal,
|
||||||
setDeleteIssueModal,
|
setDeleteIssueModal,
|
||||||
|
|
@ -141,7 +136,6 @@ export const ProjectIssueQuickActions: React.FC<IQuickActionProps> = observer((p
|
||||||
if (issueToEdit && handleUpdate) await handleUpdate(data);
|
if (issueToEdit && handleUpdate) await handleUpdate(data);
|
||||||
}}
|
}}
|
||||||
storeType={EIssuesStoreType.PROJECT}
|
storeType={EIssuesStoreType.PROJECT}
|
||||||
isDraft={isDraftIssue}
|
|
||||||
/>
|
/>
|
||||||
{issue.project_id && workspaceSlug && (
|
{issue.project_id && workspaceSlug && (
|
||||||
<DuplicateWorkItemModal
|
<DuplicateWorkItemModal
|
||||||
|
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
import { observer } from "mobx-react";
|
|
||||||
import { useParams } from "next/navigation";
|
|
||||||
import useSWR from "swr";
|
|
||||||
// plane constants
|
|
||||||
import { EIssuesStoreType, EIssueLayoutTypes } from "@plane/types";
|
|
||||||
// components
|
|
||||||
import { LogoSpinner } from "@/components/common/logo-spinner";
|
|
||||||
import { IssuePeekOverview } from "@/components/issues/peek-overview";
|
|
||||||
// hooks
|
|
||||||
import { useIssues } from "@/hooks/store/use-issues";
|
|
||||||
import { IssuesStoreContext } from "@/hooks/use-issue-layout-store";
|
|
||||||
// components
|
|
||||||
import { DraftIssueAppliedFiltersRoot } from "../filters/applied-filters/roots/draft-issue";
|
|
||||||
import { DraftKanBanLayout } from "../kanban/roots/draft-issue-root";
|
|
||||||
import { DraftIssueListLayout } from "../list/roots/draft-issue-root";
|
|
||||||
// ui
|
|
||||||
// constants
|
|
||||||
|
|
||||||
const DraftIssueLayout = (props: { activeLayout: EIssueLayoutTypes | undefined }) => {
|
|
||||||
switch (props.activeLayout) {
|
|
||||||
case EIssueLayoutTypes.LIST:
|
|
||||||
return <DraftIssueListLayout />;
|
|
||||||
case EIssueLayoutTypes.KANBAN:
|
|
||||||
return <DraftKanBanLayout />;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
export const DraftIssueLayoutRoot: React.FC = observer(() => {
|
|
||||||
// router
|
|
||||||
const { workspaceSlug, projectId } = useParams();
|
|
||||||
// hooks
|
|
||||||
const { issuesFilter } = useIssues(EIssuesStoreType.DRAFT);
|
|
||||||
|
|
||||||
const { isLoading } = useSWR(
|
|
||||||
workspaceSlug && projectId ? `DRAFT_ISSUES_${workspaceSlug.toString()}_${projectId.toString()}` : null,
|
|
||||||
async () => {
|
|
||||||
if (workspaceSlug && projectId) {
|
|
||||||
await issuesFilter?.fetchFilters(workspaceSlug.toString(), projectId.toString());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ revalidateIfStale: false, revalidateOnFocus: false }
|
|
||||||
);
|
|
||||||
|
|
||||||
const issueFilters = issuesFilter?.getIssueFilters(projectId?.toString());
|
|
||||||
const activeLayout = issueFilters?.displayFilters?.layout || undefined;
|
|
||||||
|
|
||||||
if (!workspaceSlug || !projectId) return <></>;
|
|
||||||
|
|
||||||
if (isLoading && !issueFilters)
|
|
||||||
return (
|
|
||||||
<div className="h-full w-full flex items-center justify-center">
|
|
||||||
<LogoSpinner />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<IssuesStoreContext.Provider value={EIssuesStoreType.DRAFT}>
|
|
||||||
<div className="relative flex h-full w-full flex-col overflow-hidden">
|
|
||||||
<DraftIssueAppliedFiltersRoot />
|
|
||||||
<div className="relative h-full w-full overflow-auto">
|
|
||||||
<DraftIssueLayout activeLayout={activeLayout} />
|
|
||||||
{/* issue peek overview */}
|
|
||||||
<IssuePeekOverview is_draft />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</IssuesStoreContext.Provider>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
@ -86,12 +86,7 @@ export const CreateUpdateIssueModalBase: React.FC<IssuesModalProps> = observer((
|
||||||
setDescription(data?.description_html || "<p></p>");
|
setDescription(data?.description_html || "<p></p>");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const response = await fetchIssue(
|
const response = await fetchIssue(workspaceSlug.toString(), projectId.toString(), issueId);
|
||||||
workspaceSlug.toString(),
|
|
||||||
projectId.toString(),
|
|
||||||
issueId,
|
|
||||||
isDraft ? "DRAFT" : "DEFAULT"
|
|
||||||
);
|
|
||||||
if (response) setDescription(response?.description_html || "<p></p>");
|
if (response) setDescription(response?.description_html || "<p></p>");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ export const IssuePeekOverview: FC<IWorkItemPeekOverview> = observer((props) =>
|
||||||
fetch: async (workspaceSlug: string, projectId: string, issueId: string) => {
|
fetch: async (workspaceSlug: string, projectId: string, issueId: string) => {
|
||||||
try {
|
try {
|
||||||
setError(false);
|
setError(false);
|
||||||
await fetchIssue(workspaceSlug, projectId, issueId, is_draft ? "DRAFT" : "DEFAULT");
|
await fetchIssue(workspaceSlug, projectId, issueId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setError(true);
|
setError(true);
|
||||||
console.error("Error fetching the parent issue", error);
|
console.error("Error fetching the parent issue", error);
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ export const SidebarQuickActions = observer(() => {
|
||||||
onClose={() => setIsDraftIssueModalOpen(false)}
|
onClose={() => setIsDraftIssueModalOpen(false)}
|
||||||
data={workspaceDraftIssue ?? {}}
|
data={workspaceDraftIssue ?? {}}
|
||||||
onSubmit={() => removeWorkspaceDraftIssue()}
|
onSubmit={() => removeWorkspaceDraftIssue()}
|
||||||
|
fetchIssueDetails={false}
|
||||||
isDraft
|
isDraft
|
||||||
/>
|
/>
|
||||||
<div className={cn("flex items-center justify-between gap-1 cursor-pointer", {})}>
|
<div className={cn("flex items-center justify-between gap-1 cursor-pointer", {})}>
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import type { ITeamViewIssues, ITeamViewIssuesFilter } from "@/plane-web/store/i
|
||||||
import type { IWorkspaceIssues } from "@/plane-web/store/issue/workspace/issue.store";
|
import type { IWorkspaceIssues } from "@/plane-web/store/issue/workspace/issue.store";
|
||||||
import type { IArchivedIssues, IArchivedIssuesFilter } from "@/store/issue/archived";
|
import type { IArchivedIssues, IArchivedIssuesFilter } from "@/store/issue/archived";
|
||||||
import type { ICycleIssues, ICycleIssuesFilter } from "@/store/issue/cycle";
|
import type { ICycleIssues, ICycleIssuesFilter } from "@/store/issue/cycle";
|
||||||
import type { IDraftIssues, IDraftIssuesFilter } from "@/store/issue/draft";
|
|
||||||
import type { IModuleIssues, IModuleIssuesFilter } from "@/store/issue/module";
|
import type { IModuleIssues, IModuleIssuesFilter } from "@/store/issue/module";
|
||||||
import type { IProfileIssues, IProfileIssuesFilter } from "@/store/issue/profile";
|
import type { IProfileIssues, IProfileIssuesFilter } from "@/store/issue/profile";
|
||||||
import type { IProjectIssues, IProjectIssuesFilter } from "@/store/issue/project";
|
import type { IProjectIssues, IProjectIssuesFilter } from "@/store/issue/project";
|
||||||
|
|
@ -65,10 +64,6 @@ export type TStoreIssues = {
|
||||||
issues: IArchivedIssues;
|
issues: IArchivedIssues;
|
||||||
issuesFilter: IArchivedIssuesFilter;
|
issuesFilter: IArchivedIssuesFilter;
|
||||||
};
|
};
|
||||||
[EIssuesStoreType.DRAFT]: defaultIssueStore & {
|
|
||||||
issues: IDraftIssues;
|
|
||||||
issuesFilter: IDraftIssuesFilter;
|
|
||||||
};
|
|
||||||
[EIssuesStoreType.DEFAULT]: defaultIssueStore & {
|
[EIssuesStoreType.DEFAULT]: defaultIssueStore & {
|
||||||
issues: IProjectIssues;
|
issues: IProjectIssues;
|
||||||
issuesFilter: IProjectIssuesFilter;
|
issuesFilter: IProjectIssuesFilter;
|
||||||
|
|
@ -142,11 +137,6 @@ export const useIssues = <T extends EIssuesStoreType>(storeType?: T): TStoreIssu
|
||||||
issues: context.issue.archivedIssues,
|
issues: context.issue.archivedIssues,
|
||||||
issuesFilter: context.issue.archivedIssuesFilter,
|
issuesFilter: context.issue.archivedIssuesFilter,
|
||||||
}) as TStoreIssues[T];
|
}) as TStoreIssues[T];
|
||||||
case EIssuesStoreType.DRAFT:
|
|
||||||
return merge(defaultStore, {
|
|
||||||
issues: context.issue.draftIssues,
|
|
||||||
issuesFilter: context.issue.draftIssuesFilter,
|
|
||||||
}) as TStoreIssues[T];
|
|
||||||
case EIssuesStoreType.EPIC:
|
case EIssuesStoreType.EPIC:
|
||||||
return merge(defaultStore, {
|
return merge(defaultStore, {
|
||||||
issues: context.issue.projectEpics,
|
issues: context.issue.projectEpics,
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ type DNDStoreType =
|
||||||
| EIssuesStoreType.MODULE
|
| EIssuesStoreType.MODULE
|
||||||
| EIssuesStoreType.CYCLE
|
| EIssuesStoreType.CYCLE
|
||||||
| EIssuesStoreType.PROJECT_VIEW
|
| EIssuesStoreType.PROJECT_VIEW
|
||||||
| EIssuesStoreType.DRAFT
|
|
||||||
| EIssuesStoreType.PROFILE
|
| EIssuesStoreType.PROFILE
|
||||||
| EIssuesStoreType.ARCHIVED
|
| EIssuesStoreType.ARCHIVED
|
||||||
| EIssuesStoreType.WORKSPACE_DRAFT
|
| EIssuesStoreType.WORKSPACE_DRAFT
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,6 @@ export const useIssuesActions = (storeType: EIssuesStoreType): IssueActions => {
|
||||||
const projectViewIssueActions = useProjectViewIssueActions();
|
const projectViewIssueActions = useProjectViewIssueActions();
|
||||||
const globalIssueActions = useGlobalIssueActions();
|
const globalIssueActions = useGlobalIssueActions();
|
||||||
const profileIssueActions = useProfileIssueActions();
|
const profileIssueActions = useProfileIssueActions();
|
||||||
const draftIssueActions = useDraftIssueActions();
|
|
||||||
const archivedIssueActions = useArchivedIssueActions();
|
const archivedIssueActions = useArchivedIssueActions();
|
||||||
const workspaceDraftIssueActions = useWorkspaceDraftIssueActions();
|
const workspaceDraftIssueActions = useWorkspaceDraftIssueActions();
|
||||||
const teamProjectWorkItemsActions = useTeamProjectWorkItemsActions();
|
const teamProjectWorkItemsActions = useTeamProjectWorkItemsActions();
|
||||||
|
|
@ -68,8 +67,6 @@ export const useIssuesActions = (storeType: EIssuesStoreType): IssueActions => {
|
||||||
return teamIssueActions;
|
return teamIssueActions;
|
||||||
case EIssuesStoreType.ARCHIVED:
|
case EIssuesStoreType.ARCHIVED:
|
||||||
return archivedIssueActions;
|
return archivedIssueActions;
|
||||||
case EIssuesStoreType.DRAFT:
|
|
||||||
return draftIssueActions;
|
|
||||||
case EIssuesStoreType.CYCLE:
|
case EIssuesStoreType.CYCLE:
|
||||||
return cycleIssueActions;
|
return cycleIssueActions;
|
||||||
case EIssuesStoreType.MODULE:
|
case EIssuesStoreType.MODULE:
|
||||||
|
|
@ -644,76 +641,6 @@ const useProjectViewIssueActions = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const useDraftIssueActions = () => {
|
|
||||||
// router
|
|
||||||
const { workspaceSlug: routerWorkspaceSlug, projectId: routerProjectId } = useParams();
|
|
||||||
const workspaceSlug = routerWorkspaceSlug?.toString();
|
|
||||||
const projectId = routerProjectId?.toString();
|
|
||||||
// store hooks
|
|
||||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.DRAFT);
|
|
||||||
|
|
||||||
const fetchIssues = useCallback(
|
|
||||||
async (loadType: TLoader, options: IssuePaginationOptions) => {
|
|
||||||
if (!workspaceSlug || !projectId) return;
|
|
||||||
return issues.fetchIssues(workspaceSlug.toString(), projectId.toString(), loadType, options);
|
|
||||||
},
|
|
||||||
[issues.fetchIssues, workspaceSlug, projectId]
|
|
||||||
);
|
|
||||||
const fetchNextIssues = useCallback(
|
|
||||||
async (groupId?: string, subGroupId?: string) => {
|
|
||||||
if (!workspaceSlug || !projectId) return;
|
|
||||||
return issues.fetchNextIssues(workspaceSlug.toString(), projectId.toString(), groupId, subGroupId);
|
|
||||||
},
|
|
||||||
[issues.fetchIssues, workspaceSlug, projectId]
|
|
||||||
);
|
|
||||||
|
|
||||||
const createIssue = useCallback(
|
|
||||||
async (projectId: string | undefined | null, data: Partial<TIssue>) => {
|
|
||||||
if (!workspaceSlug || !projectId) return;
|
|
||||||
return await issues.createIssue(workspaceSlug, projectId, data);
|
|
||||||
},
|
|
||||||
[issues.createIssue, workspaceSlug]
|
|
||||||
);
|
|
||||||
const updateIssue = useCallback(
|
|
||||||
async (projectId: string | undefined | null, issueId: string, data: Partial<TIssue>) => {
|
|
||||||
if (!workspaceSlug || !projectId) return;
|
|
||||||
return await issues.updateIssue(workspaceSlug, projectId, issueId, data);
|
|
||||||
},
|
|
||||||
[issues.updateIssue, workspaceSlug]
|
|
||||||
);
|
|
||||||
const removeIssue = useCallback(
|
|
||||||
async (projectId: string | undefined | null, issueId: string) => {
|
|
||||||
if (!workspaceSlug || !projectId) return;
|
|
||||||
return await issues.removeIssue(workspaceSlug, projectId, issueId);
|
|
||||||
},
|
|
||||||
[issues.removeIssue, workspaceSlug]
|
|
||||||
);
|
|
||||||
|
|
||||||
const updateFilters = useCallback(
|
|
||||||
async (
|
|
||||||
projectId: string,
|
|
||||||
filterType: EIssueFilterType,
|
|
||||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters
|
|
||||||
) => {
|
|
||||||
if (!workspaceSlug) return;
|
|
||||||
return await issuesFilter.updateFilters(workspaceSlug, projectId, filterType, filters);
|
|
||||||
},
|
|
||||||
[issuesFilter.updateFilters]
|
|
||||||
);
|
|
||||||
|
|
||||||
return useMemo(
|
|
||||||
() => ({
|
|
||||||
fetchIssues,
|
|
||||||
fetchNextIssues,
|
|
||||||
createIssue,
|
|
||||||
updateIssue,
|
|
||||||
removeIssue,
|
|
||||||
updateFilters,
|
|
||||||
}),
|
|
||||||
[fetchIssues, createIssue, updateIssue, removeIssue, updateFilters]
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const useArchivedIssueActions = () => {
|
const useArchivedIssueActions = () => {
|
||||||
// router
|
// router
|
||||||
const { workspaceSlug: routerWorkspaceSlug, projectId: routerProjectId } = useParams();
|
const { workspaceSlug: routerWorkspaceSlug, projectId: routerProjectId } = useParams();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
export * from "./issue_archive.service";
|
export * from "./issue_archive.service";
|
||||||
export * from "./issue.service";
|
export * from "./issue.service";
|
||||||
export * from "./issue_draft.service";
|
|
||||||
export * from "./issue_reaction.service";
|
export * from "./issue_reaction.service";
|
||||||
export * from "./issue_label.service";
|
export * from "./issue_label.service";
|
||||||
export * from "./issue_attachment.service";
|
export * from "./issue_attachment.service";
|
||||||
|
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
import { API_BASE_URL } from "@plane/constants";
|
|
||||||
import { TIssue, TIssuesResponse } from "@plane/types";
|
|
||||||
import { APIService } from "@/services/api.service";
|
|
||||||
// helpers
|
|
||||||
|
|
||||||
export class IssueDraftService extends APIService {
|
|
||||||
constructor() {
|
|
||||||
super(API_BASE_URL);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getDraftIssues(workspaceSlug: string, projectId: string, query?: any, config = {}): Promise<TIssuesResponse> {
|
|
||||||
return this.get(
|
|
||||||
`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-drafts/`,
|
|
||||||
{
|
|
||||||
params: { ...query },
|
|
||||||
},
|
|
||||||
config
|
|
||||||
)
|
|
||||||
.then((response) => response?.data)
|
|
||||||
.catch((error) => {
|
|
||||||
throw error?.response?.data;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async createDraftIssue(workspaceSlug: string, projectId: string, data: any): Promise<TIssue> {
|
|
||||||
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-drafts/`, data)
|
|
||||||
.then((response) => response?.data)
|
|
||||||
.catch((error) => {
|
|
||||||
throw error?.response;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateDraftIssue(workspaceSlug: string, projectId: string, issueId: string, data: any): Promise<void> {
|
|
||||||
return this.patch(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-drafts/${issueId}/`, data)
|
|
||||||
.then((response) => response?.data)
|
|
||||||
.catch((error) => {
|
|
||||||
throw error?.response;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async deleteDraftIssue(workspaceSlug: string, projectId: string, issueId: string): Promise<void> {
|
|
||||||
return this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-drafts/${issueId}/`)
|
|
||||||
.then((response) => response?.data)
|
|
||||||
.catch((error) => {
|
|
||||||
throw error?.response;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async getDraftIssueById(workspaceSlug: string, projectId: string, issueId: string, queries?: any): Promise<TIssue> {
|
|
||||||
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-drafts/${issueId}/`, {
|
|
||||||
params: queries,
|
|
||||||
})
|
|
||||||
.then((response) => response?.data)
|
|
||||||
.catch((error) => {
|
|
||||||
throw error?.response;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,279 +0,0 @@
|
||||||
import isArray from "lodash/isArray";
|
|
||||||
import isEmpty from "lodash/isEmpty";
|
|
||||||
import pickBy from "lodash/pickBy";
|
|
||||||
import set from "lodash/set";
|
|
||||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
|
||||||
// base class
|
|
||||||
import { computedFn } from "mobx-utils";
|
|
||||||
import { EIssueFilterType } from "@plane/constants";
|
|
||||||
import {
|
|
||||||
EIssuesStoreType,
|
|
||||||
IIssueFilterOptions,
|
|
||||||
IIssueDisplayFilterOptions,
|
|
||||||
IIssueDisplayProperties,
|
|
||||||
TIssueKanbanFilters,
|
|
||||||
IIssueFilters,
|
|
||||||
TIssueParams,
|
|
||||||
IssuePaginationOptions,
|
|
||||||
} from "@plane/types";
|
|
||||||
import { handleIssueQueryParamsByLayout } from "@plane/utils";
|
|
||||||
import { IssueFiltersService } from "@/services/issue_filter.service";
|
|
||||||
import { IBaseIssueFilterStore, IssueFilterHelperStore } from "../helpers/issue-filter-helper.store";
|
|
||||||
// helpers
|
|
||||||
// types
|
|
||||||
import { IIssueRootStore } from "../root.store";
|
|
||||||
// constants
|
|
||||||
// services
|
|
||||||
|
|
||||||
export interface IDraftIssuesFilter extends IBaseIssueFilterStore {
|
|
||||||
//helper actions
|
|
||||||
getFilterParams: (
|
|
||||||
options: IssuePaginationOptions,
|
|
||||||
projectId: string,
|
|
||||||
cursor: string | undefined,
|
|
||||||
groupId: string | undefined,
|
|
||||||
subGroupId: string | undefined
|
|
||||||
) => Partial<Record<TIssueParams, string | boolean>>;
|
|
||||||
getIssueFilters(projectId: string): IIssueFilters | undefined;
|
|
||||||
// action
|
|
||||||
fetchFilters: (workspaceSlug: string, projectId: string) => Promise<void>;
|
|
||||||
updateFilters: (
|
|
||||||
workspaceSlug: string,
|
|
||||||
projectId: string,
|
|
||||||
filterType: EIssueFilterType,
|
|
||||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters
|
|
||||||
) => Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DraftIssuesFilter extends IssueFilterHelperStore implements IDraftIssuesFilter {
|
|
||||||
// observables
|
|
||||||
filters: { [projectId: string]: IIssueFilters } = {};
|
|
||||||
// root store
|
|
||||||
rootIssueStore: IIssueRootStore;
|
|
||||||
// services
|
|
||||||
issueFilterService;
|
|
||||||
|
|
||||||
constructor(_rootStore: IIssueRootStore) {
|
|
||||||
super();
|
|
||||||
makeObservable(this, {
|
|
||||||
// observables
|
|
||||||
filters: observable,
|
|
||||||
// computed
|
|
||||||
issueFilters: computed,
|
|
||||||
appliedFilters: computed,
|
|
||||||
// actions
|
|
||||||
fetchFilters: action,
|
|
||||||
updateFilters: action,
|
|
||||||
});
|
|
||||||
// root store
|
|
||||||
this.rootIssueStore = _rootStore;
|
|
||||||
// services
|
|
||||||
this.issueFilterService = new IssueFiltersService();
|
|
||||||
}
|
|
||||||
|
|
||||||
get issueFilters() {
|
|
||||||
const projectId = this.rootIssueStore.projectId;
|
|
||||||
if (!projectId) return undefined;
|
|
||||||
|
|
||||||
return this.getIssueFilters(projectId);
|
|
||||||
}
|
|
||||||
|
|
||||||
get appliedFilters() {
|
|
||||||
const projectId = this.rootIssueStore.projectId;
|
|
||||||
if (!projectId) return undefined;
|
|
||||||
|
|
||||||
return this.getAppliedFilters(projectId);
|
|
||||||
}
|
|
||||||
|
|
||||||
getIssueFilters(projectId: string) {
|
|
||||||
const displayFilters = this.filters[projectId] || undefined;
|
|
||||||
if (!projectId || isEmpty(displayFilters)) return undefined;
|
|
||||||
|
|
||||||
const _filters: IIssueFilters = this.computedIssueFilters(displayFilters);
|
|
||||||
|
|
||||||
return _filters;
|
|
||||||
}
|
|
||||||
|
|
||||||
getAppliedFilters(projectId: string) {
|
|
||||||
const userFilters = this.getIssueFilters(projectId);
|
|
||||||
if (!userFilters) return undefined;
|
|
||||||
|
|
||||||
const filteredParams = handleIssueQueryParamsByLayout(userFilters?.displayFilters?.layout, "issues");
|
|
||||||
if (!filteredParams) return undefined;
|
|
||||||
|
|
||||||
const filteredRouteParams: Partial<Record<TIssueParams, string | boolean>> = this.computedFilteredParams(
|
|
||||||
userFilters?.filters as IIssueFilterOptions,
|
|
||||||
userFilters?.displayFilters as IIssueDisplayFilterOptions,
|
|
||||||
filteredParams
|
|
||||||
);
|
|
||||||
|
|
||||||
return filteredRouteParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
getFilterParams = computedFn(
|
|
||||||
(
|
|
||||||
options: IssuePaginationOptions,
|
|
||||||
projectId: string,
|
|
||||||
cursor: string | undefined,
|
|
||||||
groupId: string | undefined,
|
|
||||||
subGroupId: string | undefined
|
|
||||||
) => {
|
|
||||||
const filterParams = this.getAppliedFilters(projectId);
|
|
||||||
|
|
||||||
const paginationParams = this.getPaginationParams(filterParams, options, cursor, groupId, subGroupId);
|
|
||||||
return paginationParams;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
fetchFilters = async (workspaceSlug: string, projectId: string) => {
|
|
||||||
const _filters = this.handleIssuesLocalFilters.get(EIssuesStoreType.DRAFT, workspaceSlug, projectId, undefined);
|
|
||||||
|
|
||||||
const filters: IIssueFilterOptions = this.computedFilters(_filters?.filters);
|
|
||||||
const displayFilters: IIssueDisplayFilterOptions = this.computedDisplayFilters(_filters?.display_filters);
|
|
||||||
const displayProperties: IIssueDisplayProperties = this.computedDisplayProperties(_filters?.display_properties);
|
|
||||||
const kanbanFilters = {
|
|
||||||
group_by: [],
|
|
||||||
sub_group_by: [],
|
|
||||||
};
|
|
||||||
kanbanFilters.group_by = _filters?.kanban_filters?.group_by || [];
|
|
||||||
kanbanFilters.sub_group_by = _filters?.kanban_filters?.sub_group_by || [];
|
|
||||||
|
|
||||||
runInAction(() => {
|
|
||||||
set(this.filters, [projectId, "filters"], filters);
|
|
||||||
set(this.filters, [projectId, "displayFilters"], displayFilters);
|
|
||||||
set(this.filters, [projectId, "displayProperties"], displayProperties);
|
|
||||||
set(this.filters, [projectId, "kanbanFilters"], kanbanFilters);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
updateFilters = async (
|
|
||||||
workspaceSlug: string,
|
|
||||||
projectId: string,
|
|
||||||
type: EIssueFilterType,
|
|
||||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters
|
|
||||||
) => {
|
|
||||||
try {
|
|
||||||
if (isEmpty(this.filters) || isEmpty(this.filters[projectId]) || isEmpty(filters)) return;
|
|
||||||
|
|
||||||
const _filters = {
|
|
||||||
filters: this.filters[projectId].filters as IIssueFilterOptions,
|
|
||||||
displayFilters: this.filters[projectId].displayFilters as IIssueDisplayFilterOptions,
|
|
||||||
displayProperties: this.filters[projectId].displayProperties as IIssueDisplayProperties,
|
|
||||||
kanbanFilters: this.filters[projectId].kanbanFilters as TIssueKanbanFilters,
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case EIssueFilterType.FILTERS: {
|
|
||||||
const updatedFilters = filters as IIssueFilterOptions;
|
|
||||||
_filters.filters = { ..._filters.filters, ...updatedFilters };
|
|
||||||
|
|
||||||
runInAction(() => {
|
|
||||||
Object.keys(updatedFilters).forEach((_key) => {
|
|
||||||
set(this.filters, [projectId, "filters", _key], updatedFilters[_key as keyof IIssueFilterOptions]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
const appliedFilters = _filters.filters || {};
|
|
||||||
const filteredFilters = pickBy(appliedFilters, (value) => value && isArray(value) && value.length > 0);
|
|
||||||
this.rootIssueStore.draftIssues.fetchIssuesWithExistingPagination(
|
|
||||||
workspaceSlug,
|
|
||||||
projectId,
|
|
||||||
isEmpty(filteredFilters) ? "init-loader" : "mutation"
|
|
||||||
);
|
|
||||||
this.handleIssuesLocalFilters.set(EIssuesStoreType.DRAFT, type, workspaceSlug, projectId, undefined, {
|
|
||||||
filters: _filters.filters,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EIssueFilterType.DISPLAY_FILTERS: {
|
|
||||||
const updatedDisplayFilters = filters as IIssueDisplayFilterOptions;
|
|
||||||
_filters.displayFilters = { ..._filters.displayFilters, ...updatedDisplayFilters };
|
|
||||||
|
|
||||||
// set sub_group_by to null if group_by is set to null
|
|
||||||
if (_filters.displayFilters.group_by === null) {
|
|
||||||
_filters.displayFilters.sub_group_by = null;
|
|
||||||
updatedDisplayFilters.sub_group_by = null;
|
|
||||||
}
|
|
||||||
// set sub_group_by to null if layout is switched to kanban group_by and sub_group_by are same
|
|
||||||
if (
|
|
||||||
_filters.displayFilters.layout === "kanban" &&
|
|
||||||
_filters.displayFilters.group_by === _filters.displayFilters.sub_group_by
|
|
||||||
) {
|
|
||||||
_filters.displayFilters.sub_group_by = null;
|
|
||||||
updatedDisplayFilters.sub_group_by = null;
|
|
||||||
}
|
|
||||||
// set group_by to state if layout is switched to kanban and group_by is null
|
|
||||||
if (_filters.displayFilters.layout === "kanban" && _filters.displayFilters.group_by === null) {
|
|
||||||
_filters.displayFilters.group_by = "state";
|
|
||||||
updatedDisplayFilters.group_by = "state";
|
|
||||||
}
|
|
||||||
|
|
||||||
runInAction(() => {
|
|
||||||
Object.keys(updatedDisplayFilters).forEach((_key) => {
|
|
||||||
set(
|
|
||||||
this.filters,
|
|
||||||
[projectId, "displayFilters", _key],
|
|
||||||
updatedDisplayFilters[_key as keyof IIssueDisplayFilterOptions]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.getShouldReFetchIssues(updatedDisplayFilters)) {
|
|
||||||
this.rootIssueStore.draftIssues.fetchIssuesWithExistingPagination(workspaceSlug, projectId, "mutation");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.handleIssuesLocalFilters.set(EIssuesStoreType.DRAFT, type, workspaceSlug, projectId, undefined, {
|
|
||||||
display_filters: _filters.displayFilters,
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EIssueFilterType.DISPLAY_PROPERTIES: {
|
|
||||||
const updatedDisplayProperties = filters as IIssueDisplayProperties;
|
|
||||||
_filters.displayProperties = { ..._filters.displayProperties, ...updatedDisplayProperties };
|
|
||||||
|
|
||||||
runInAction(() => {
|
|
||||||
Object.keys(updatedDisplayProperties).forEach((_key) => {
|
|
||||||
set(
|
|
||||||
this.filters,
|
|
||||||
[projectId, "displayProperties", _key],
|
|
||||||
updatedDisplayProperties[_key as keyof IIssueDisplayProperties]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
this.handleIssuesLocalFilters.set(EIssuesStoreType.DRAFT, type, workspaceSlug, projectId, undefined, {
|
|
||||||
display_properties: _filters.displayProperties,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case EIssueFilterType.KANBAN_FILTERS: {
|
|
||||||
const updatedKanbanFilters = filters as TIssueKanbanFilters;
|
|
||||||
_filters.kanbanFilters = { ..._filters.kanbanFilters, ...updatedKanbanFilters };
|
|
||||||
|
|
||||||
const currentUserId = this.rootIssueStore.currentUserId;
|
|
||||||
if (currentUserId)
|
|
||||||
this.handleIssuesLocalFilters.set(EIssuesStoreType.DRAFT, type, workspaceSlug, projectId, undefined, {
|
|
||||||
kanban_filters: _filters.kanbanFilters,
|
|
||||||
});
|
|
||||||
|
|
||||||
runInAction(() => {
|
|
||||||
Object.keys(updatedKanbanFilters).forEach((_key) => {
|
|
||||||
set(
|
|
||||||
this.filters,
|
|
||||||
[projectId, "kanbanFilters", _key],
|
|
||||||
updatedKanbanFilters[_key as keyof TIssueKanbanFilters]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.fetchFilters(workspaceSlug, projectId);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
export * from "./filter.store";
|
|
||||||
export * from "./issue.store";
|
|
||||||
|
|
@ -1,186 +0,0 @@
|
||||||
import { action, makeObservable, runInAction } from "mobx";
|
|
||||||
// base class
|
|
||||||
// services
|
|
||||||
// types
|
|
||||||
import {
|
|
||||||
TIssue,
|
|
||||||
TLoader,
|
|
||||||
ViewFlags,
|
|
||||||
IssuePaginationOptions,
|
|
||||||
TIssuesResponse,
|
|
||||||
TBulkOperationsPayload,
|
|
||||||
} from "@plane/types";
|
|
||||||
import { BaseIssuesStore, IBaseIssuesStore } from "../helpers/base-issues.store";
|
|
||||||
import { IIssueRootStore } from "../root.store";
|
|
||||||
import { IDraftIssuesFilter } from "./filter.store";
|
|
||||||
|
|
||||||
export interface IDraftIssues extends IBaseIssuesStore {
|
|
||||||
// observable
|
|
||||||
|
|
||||||
viewFlags: ViewFlags;
|
|
||||||
// actions
|
|
||||||
fetchIssues: (
|
|
||||||
workspaceSlug: string,
|
|
||||||
projectId: string,
|
|
||||||
loadType: TLoader,
|
|
||||||
option: IssuePaginationOptions
|
|
||||||
) => Promise<TIssuesResponse | undefined>;
|
|
||||||
fetchIssuesWithExistingPagination: (
|
|
||||||
workspaceSlug: string,
|
|
||||||
projectId: string,
|
|
||||||
loadType: TLoader
|
|
||||||
) => Promise<TIssuesResponse | undefined>;
|
|
||||||
|
|
||||||
fetchNextIssues: (
|
|
||||||
workspaceSlug: string,
|
|
||||||
projectId: string,
|
|
||||||
groupId?: string,
|
|
||||||
subGroupId?: string
|
|
||||||
) => Promise<TIssuesResponse | undefined>;
|
|
||||||
createIssue: (workspaceSlug: string, projectId: string, data: Partial<TIssue>) => Promise<TIssue>;
|
|
||||||
updateIssue: (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>;
|
|
||||||
removeBulkIssues: (workspaceSlug: string, projectId: string, issueIds: string[]) => Promise<void>;
|
|
||||||
bulkUpdateProperties: (workspaceSlug: string, projectId: string, data: TBulkOperationsPayload) => Promise<void>;
|
|
||||||
|
|
||||||
archiveBulkIssues: undefined;
|
|
||||||
quickAddIssue: undefined;
|
|
||||||
archiveIssue: undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DraftIssues extends BaseIssuesStore implements IDraftIssues {
|
|
||||||
viewFlags = {
|
|
||||||
enableQuickAdd: false,
|
|
||||||
enableIssueCreation: true,
|
|
||||||
enableInlineEditing: true,
|
|
||||||
};
|
|
||||||
// filter store
|
|
||||||
issueFilterStore: IDraftIssuesFilter;
|
|
||||||
|
|
||||||
constructor(_rootStore: IIssueRootStore, issueFilterStore: IDraftIssuesFilter) {
|
|
||||||
super(_rootStore, issueFilterStore);
|
|
||||||
makeObservable(this, {
|
|
||||||
// action
|
|
||||||
fetchIssues: action,
|
|
||||||
fetchNextIssues: action,
|
|
||||||
fetchIssuesWithExistingPagination: action,
|
|
||||||
});
|
|
||||||
// filter store
|
|
||||||
this.issueFilterStore = issueFilterStore;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches the project details
|
|
||||||
* @param workspaceSlug
|
|
||||||
* @param projectId
|
|
||||||
*/
|
|
||||||
fetchParentStats = async (workspaceSlug: string, projectId?: string) => {
|
|
||||||
projectId && this.rootIssueStore.rootStore.projectRoot.project.fetchProjectDetails(workspaceSlug, projectId);
|
|
||||||
};
|
|
||||||
|
|
||||||
/** */
|
|
||||||
updateParentStats = () => {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called to fetch the first issues of pagination
|
|
||||||
* @param workspaceSlug
|
|
||||||
* @param projectId
|
|
||||||
* @param loadType
|
|
||||||
* @param options
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
fetchIssues = async (
|
|
||||||
workspaceSlug: string,
|
|
||||||
projectId: string,
|
|
||||||
loadType: TLoader = "init-loader",
|
|
||||||
options: IssuePaginationOptions,
|
|
||||||
isExistingPaginationOptions: boolean = false
|
|
||||||
) => {
|
|
||||||
try {
|
|
||||||
// set loader and clear store
|
|
||||||
runInAction(() => {
|
|
||||||
this.setLoader(loadType);
|
|
||||||
});
|
|
||||||
this.clear(!isExistingPaginationOptions);
|
|
||||||
|
|
||||||
// get params from pagination options
|
|
||||||
const params = this.issueFilterStore?.getFilterParams(options, projectId, undefined, undefined, undefined);
|
|
||||||
// call the fetch issues API with the params
|
|
||||||
const response = await this.issueDraftService.getDraftIssues(workspaceSlug, projectId, params, {
|
|
||||||
signal: this.controller.signal,
|
|
||||||
});
|
|
||||||
|
|
||||||
// after fetching issues, call the base method to process the response further
|
|
||||||
this.onfetchIssues(response, options, workspaceSlug, projectId, undefined, !isExistingPaginationOptions);
|
|
||||||
return response;
|
|
||||||
} catch (error) {
|
|
||||||
// set loader to undefined if errored out
|
|
||||||
this.setLoader(undefined);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called subsequent pages of pagination
|
|
||||||
* if groupId/subgroupId is provided, only that specific group's next page is fetched
|
|
||||||
* else all the groups' next page is fetched
|
|
||||||
* @param workspaceSlug
|
|
||||||
* @param projectId
|
|
||||||
* @param groupId
|
|
||||||
* @param subGroupId
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
fetchNextIssues = async (workspaceSlug: string, projectId: string, groupId?: string, subGroupId?: string) => {
|
|
||||||
const cursorObject = this.getPaginationData(groupId, subGroupId);
|
|
||||||
// if there are no pagination options and the next page results do not exist the return
|
|
||||||
if (!this.paginationOptions || (cursorObject && !cursorObject?.nextPageResults)) return;
|
|
||||||
try {
|
|
||||||
// set Loader
|
|
||||||
this.setLoader("pagination", groupId, subGroupId);
|
|
||||||
|
|
||||||
// get params from stored pagination options
|
|
||||||
const params = this.issueFilterStore?.getFilterParams(
|
|
||||||
this.paginationOptions,
|
|
||||||
projectId,
|
|
||||||
this.getNextCursor(groupId, subGroupId),
|
|
||||||
groupId,
|
|
||||||
subGroupId
|
|
||||||
);
|
|
||||||
// call the fetch issues API with the params for next page in issues
|
|
||||||
const response = await this.issueDraftService.getDraftIssues(workspaceSlug, projectId, params);
|
|
||||||
|
|
||||||
// after the next page of issues are fetched, call the base method to process the response
|
|
||||||
this.onfetchNexIssues(response, groupId, subGroupId);
|
|
||||||
return response;
|
|
||||||
} catch (error) {
|
|
||||||
// set Loader as undefined if errored out
|
|
||||||
this.setLoader(undefined, groupId, subGroupId);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This Method exists to fetch the first page of the issues with the existing stored pagination
|
|
||||||
* This is useful for refetching when filters, groupBy, orderBy etc changes
|
|
||||||
* @param workspaceSlug
|
|
||||||
* @param projectId
|
|
||||||
* @param loadType
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
fetchIssuesWithExistingPagination = async (
|
|
||||||
workspaceSlug: string,
|
|
||||||
projectId: string,
|
|
||||||
loadType: TLoader = "mutation"
|
|
||||||
) => {
|
|
||||||
if (!this.paginationOptions) return;
|
|
||||||
return await this.fetchIssues(workspaceSlug, projectId, loadType, this.paginationOptions, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Using aliased names as they cannot be overridden in other stores
|
|
||||||
createIssue = this.createDraftIssue;
|
|
||||||
updateIssue = this.updateDraftIssue;
|
|
||||||
|
|
||||||
// Setting them as undefined as they can not performed on draft issues
|
|
||||||
archiveBulkIssues = undefined;
|
|
||||||
quickAddIssue = undefined;
|
|
||||||
archiveIssue = undefined;
|
|
||||||
}
|
|
||||||
|
|
@ -41,7 +41,7 @@ import { updatePersistentLayer } from "@/local-db/utils/utils";
|
||||||
import { workItemSortWithOrderByExtended } from "@/plane-web/store/issue/helpers/base-issue.store";
|
import { workItemSortWithOrderByExtended } from "@/plane-web/store/issue/helpers/base-issue.store";
|
||||||
// services
|
// services
|
||||||
import { CycleService } from "@/services/cycle.service";
|
import { CycleService } from "@/services/cycle.service";
|
||||||
import { IssueArchiveService, IssueDraftService, IssueService } from "@/services/issue";
|
import { IssueArchiveService, IssueService } from "@/services/issue";
|
||||||
import { ModuleService } from "@/services/module.service";
|
import { ModuleService } from "@/services/module.service";
|
||||||
//
|
//
|
||||||
import { IIssueRootStore } from "../root.store";
|
import { IIssueRootStore } from "../root.store";
|
||||||
|
|
@ -194,7 +194,6 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||||
// services
|
// services
|
||||||
issueService;
|
issueService;
|
||||||
issueArchiveService;
|
issueArchiveService;
|
||||||
issueDraftService;
|
|
||||||
moduleService;
|
moduleService;
|
||||||
cycleService;
|
cycleService;
|
||||||
// root store
|
// root store
|
||||||
|
|
@ -238,8 +237,6 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||||
|
|
||||||
createIssue: action,
|
createIssue: action,
|
||||||
issueUpdate: action,
|
issueUpdate: action,
|
||||||
createDraftIssue: action,
|
|
||||||
updateDraftIssue: action,
|
|
||||||
updateIssueDates: action,
|
updateIssueDates: action,
|
||||||
issueQuickAdd: action.bound,
|
issueQuickAdd: action.bound,
|
||||||
removeIssue: action.bound,
|
removeIssue: action.bound,
|
||||||
|
|
@ -264,7 +261,6 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||||
|
|
||||||
this.issueService = new IssueService(serviceType);
|
this.issueService = new IssueService(serviceType);
|
||||||
this.issueArchiveService = new IssueArchiveService();
|
this.issueArchiveService = new IssueArchiveService();
|
||||||
this.issueDraftService = new IssueDraftService();
|
|
||||||
this.moduleService = new ModuleService();
|
this.moduleService = new ModuleService();
|
||||||
this.cycleService = new CycleService();
|
this.cycleService = new CycleService();
|
||||||
|
|
||||||
|
|
@ -613,54 +609,6 @@ export abstract class BaseIssuesStore implements IBaseIssuesStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Similar to Create Issue but for creating Draft issues
|
|
||||||
* @param workspaceSlug
|
|
||||||
* @param projectId
|
|
||||||
* @param data draft issue data
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async createDraftIssue(workspaceSlug: string, projectId: string, data: Partial<TIssue>) {
|
|
||||||
// call API to create a Draft issue
|
|
||||||
const response = await this.issueDraftService.createDraftIssue(workspaceSlug, projectId, data);
|
|
||||||
// call Fetch parent stats
|
|
||||||
this.fetchParentStats(workspaceSlug, projectId);
|
|
||||||
// Add issue to store
|
|
||||||
this.addIssue(response);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Similar to update issue but for draft issues.
|
|
||||||
* @param workspaceSlug
|
|
||||||
* @param projectId
|
|
||||||
* @param issueId
|
|
||||||
* @param data Partial Issue Data to be updated
|
|
||||||
*/
|
|
||||||
async updateDraftIssue(workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) {
|
|
||||||
// Store Before state of the issue
|
|
||||||
const issueBeforeUpdate = clone(this.rootIssueStore.issues.getIssueById(issueId));
|
|
||||||
try {
|
|
||||||
// Update the Respective Stores
|
|
||||||
this.rootIssueStore.issues.updateIssue(issueId, data);
|
|
||||||
this.updateIssueList({ ...issueBeforeUpdate, ...data } as TIssue, issueBeforeUpdate);
|
|
||||||
|
|
||||||
// call API to update the issue
|
|
||||||
await this.issueDraftService.updateDraftIssue(workspaceSlug, projectId, issueId, data);
|
|
||||||
|
|
||||||
// call Fetch parent stats
|
|
||||||
this.fetchParentStats(workspaceSlug, projectId);
|
|
||||||
|
|
||||||
// If the issue is updated to not a draft issue anymore remove from the store list
|
|
||||||
if (!isNil(data.is_draft) && !data.is_draft) this.removeIssueFromList(issueId);
|
|
||||||
} catch (error) {
|
|
||||||
// If errored out update store again to revert the change
|
|
||||||
this.rootIssueStore.issues.updateIssue(issueId, issueBeforeUpdate ?? {});
|
|
||||||
this.updateIssueList(issueBeforeUpdate, { ...issueBeforeUpdate, ...data } as TIssue);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called to delete an issue
|
* This method is called to delete an issue
|
||||||
* @param workspaceSlug
|
* @param workspaceSlug
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,13 @@ import { EIssueServiceType, TIssue, TIssueServiceType } from "@plane/types";
|
||||||
// local
|
// local
|
||||||
import { persistence } from "@/local-db/storage.sqlite";
|
import { persistence } from "@/local-db/storage.sqlite";
|
||||||
// services
|
// services
|
||||||
import { IssueArchiveService, IssueDraftService, IssueService } from "@/services/issue";
|
import { IssueArchiveService, WorkspaceDraftService, IssueService } from "@/services/issue";
|
||||||
// types
|
// types
|
||||||
import { IIssueDetail } from "./root.store";
|
import { IIssueDetail } from "./root.store";
|
||||||
|
|
||||||
export interface IIssueStoreActions {
|
export interface IIssueStoreActions {
|
||||||
// actions
|
// actions
|
||||||
fetchIssue: (
|
fetchIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<TIssue>;
|
||||||
workspaceSlug: string,
|
|
||||||
projectId: string,
|
|
||||||
issueId: string,
|
|
||||||
issueStatus?: "DEFAULT" | "DRAFT"
|
|
||||||
) => Promise<TIssue>;
|
|
||||||
updateIssue: (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>;
|
updateIssue: (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>;
|
||||||
removeIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
removeIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||||
archiveIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
archiveIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||||
|
|
@ -52,7 +47,7 @@ export class IssueStore implements IIssueStore {
|
||||||
issueService;
|
issueService;
|
||||||
epicService;
|
epicService;
|
||||||
issueArchiveService;
|
issueArchiveService;
|
||||||
issueDraftService;
|
draftWorkItemService;
|
||||||
|
|
||||||
constructor(rootStore: IIssueDetail, serviceType: TIssueServiceType) {
|
constructor(rootStore: IIssueDetail, serviceType: TIssueServiceType) {
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
|
|
@ -66,7 +61,7 @@ export class IssueStore implements IIssueStore {
|
||||||
this.issueService = new IssueService(serviceType);
|
this.issueService = new IssueService(serviceType);
|
||||||
this.epicService = new IssueService(EIssueServiceType.EPICS);
|
this.epicService = new IssueService(EIssueServiceType.EPICS);
|
||||||
this.issueArchiveService = new IssueArchiveService(serviceType);
|
this.issueArchiveService = new IssueArchiveService(serviceType);
|
||||||
this.issueDraftService = new IssueDraftService();
|
this.draftWorkItemService = new WorkspaceDraftService();
|
||||||
}
|
}
|
||||||
|
|
||||||
getIsFetchingIssueDetails = computedFn((issueId: string | undefined) => {
|
getIsFetchingIssueDetails = computedFn((issueId: string | undefined) => {
|
||||||
|
|
@ -93,7 +88,7 @@ export class IssueStore implements IIssueStore {
|
||||||
});
|
});
|
||||||
|
|
||||||
// actions
|
// actions
|
||||||
fetchIssue = async (workspaceSlug: string, projectId: string, issueId: string, issueStatus = "DEFAULT") => {
|
fetchIssue = async (workspaceSlug: string, projectId: string, issueId: string) => {
|
||||||
const query = {
|
const query = {
|
||||||
expand: "issue_reactions,issue_attachments,issue_link,parent",
|
expand: "issue_reactions,issue_attachments,issue_link,parent",
|
||||||
};
|
};
|
||||||
|
|
@ -112,9 +107,7 @@ export class IssueStore implements IIssueStore {
|
||||||
this.localDBIssueDescription = issueId;
|
this.localDBIssueDescription = issueId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (issueStatus === "DRAFT")
|
await this.issueService.retrieve(workspaceSlug, projectId, issueId, query);
|
||||||
issue = await this.issueDraftService.getDraftIssueById(workspaceSlug, projectId, issueId, query);
|
|
||||||
else issue = await this.issueService.retrieve(workspaceSlug, projectId, issueId, query);
|
|
||||||
|
|
||||||
if (!issue) throw new Error("Work item not found");
|
if (!issue) throw new Error("Work item not found");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -272,12 +272,8 @@ export abstract class IssueDetail implements IIssueDetail {
|
||||||
setIssueLinkData = (issueLinkData: TIssueLink | null) => (this.issueLinkData = issueLinkData);
|
setIssueLinkData = (issueLinkData: TIssueLink | null) => (this.issueLinkData = issueLinkData);
|
||||||
|
|
||||||
// issue
|
// issue
|
||||||
fetchIssue = async (
|
fetchIssue = async (workspaceSlug: string, projectId: string, issueId: string) =>
|
||||||
workspaceSlug: string,
|
this.issue.fetchIssue(workspaceSlug, projectId, issueId);
|
||||||
projectId: string,
|
|
||||||
issueId: string,
|
|
||||||
issueStatus: "DEFAULT" | "DRAFT" = "DEFAULT"
|
|
||||||
) => this.issue.fetchIssue(workspaceSlug, projectId, issueId, issueStatus);
|
|
||||||
fetchIssueWithIdentifier = async (workspaceSlug: string, projectIdentifier: string, sequenceId: string) =>
|
fetchIssueWithIdentifier = async (workspaceSlug: string, projectIdentifier: string, sequenceId: string) =>
|
||||||
this.issue.fetchIssueWithIdentifier(workspaceSlug, projectIdentifier, sequenceId);
|
this.issue.fetchIssueWithIdentifier(workspaceSlug, projectIdentifier, sequenceId);
|
||||||
updateIssue = async (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) =>
|
updateIssue = async (workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>) =>
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ import { IWorkspaceMembership } from "@/store/member/workspace-member.store";
|
||||||
// issues data store
|
// issues data store
|
||||||
import { IArchivedIssuesFilter, ArchivedIssuesFilter, IArchivedIssues, ArchivedIssues } from "./archived";
|
import { IArchivedIssuesFilter, ArchivedIssuesFilter, IArchivedIssues, ArchivedIssues } from "./archived";
|
||||||
import { ICycleIssuesFilter, CycleIssuesFilter, ICycleIssues, CycleIssues } from "./cycle";
|
import { ICycleIssuesFilter, CycleIssuesFilter, ICycleIssues, CycleIssues } from "./cycle";
|
||||||
import { IDraftIssuesFilter, DraftIssuesFilter, IDraftIssues, DraftIssues } from "./draft";
|
|
||||||
import { IIssueStore, IssueStore } from "./issue.store";
|
import { IIssueStore, IssueStore } from "./issue.store";
|
||||||
import { ICalendarStore, CalendarStore } from "./issue_calendar_view.store";
|
import { ICalendarStore, CalendarStore } from "./issue_calendar_view.store";
|
||||||
import { IIssueKanBanViewStore, IssueKanBanViewStore } from "./issue_kanban_view.store";
|
import { IIssueKanBanViewStore, IssueKanBanViewStore } from "./issue_kanban_view.store";
|
||||||
|
|
@ -115,9 +114,6 @@ export interface IIssueRootStore {
|
||||||
archivedIssuesFilter: IArchivedIssuesFilter;
|
archivedIssuesFilter: IArchivedIssuesFilter;
|
||||||
archivedIssues: IArchivedIssues;
|
archivedIssues: IArchivedIssues;
|
||||||
|
|
||||||
draftIssuesFilter: IDraftIssuesFilter;
|
|
||||||
draftIssues: IDraftIssues;
|
|
||||||
|
|
||||||
issueKanBanView: IIssueKanBanViewStore;
|
issueKanBanView: IIssueKanBanViewStore;
|
||||||
issueCalendarView: ICalendarStore;
|
issueCalendarView: ICalendarStore;
|
||||||
|
|
||||||
|
|
@ -186,9 +182,6 @@ export class IssueRootStore implements IIssueRootStore {
|
||||||
archivedIssuesFilter: IArchivedIssuesFilter;
|
archivedIssuesFilter: IArchivedIssuesFilter;
|
||||||
archivedIssues: IArchivedIssues;
|
archivedIssues: IArchivedIssues;
|
||||||
|
|
||||||
draftIssuesFilter: IDraftIssuesFilter;
|
|
||||||
draftIssues: IDraftIssues;
|
|
||||||
|
|
||||||
issueKanBanView: IIssueKanBanViewStore;
|
issueKanBanView: IIssueKanBanViewStore;
|
||||||
issueCalendarView: ICalendarStore;
|
issueCalendarView: ICalendarStore;
|
||||||
|
|
||||||
|
|
@ -280,9 +273,6 @@ export class IssueRootStore implements IIssueRootStore {
|
||||||
this.archivedIssuesFilter = new ArchivedIssuesFilter(this);
|
this.archivedIssuesFilter = new ArchivedIssuesFilter(this);
|
||||||
this.archivedIssues = new ArchivedIssues(this, this.archivedIssuesFilter);
|
this.archivedIssues = new ArchivedIssues(this, this.archivedIssuesFilter);
|
||||||
|
|
||||||
this.draftIssuesFilter = new DraftIssuesFilter(this);
|
|
||||||
this.draftIssues = new DraftIssues(this, this.draftIssuesFilter);
|
|
||||||
|
|
||||||
this.issueKanBanView = new IssueKanBanViewStore(this);
|
this.issueKanBanView = new IssueKanBanViewStore(this);
|
||||||
this.issueCalendarView = new CalendarStore();
|
this.issueCalendarView = new CalendarStore();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ export enum EIssuesStoreType {
|
||||||
TEAM_VIEW = "TEAM_VIEW",
|
TEAM_VIEW = "TEAM_VIEW",
|
||||||
PROJECT_VIEW = "PROJECT_VIEW",
|
PROJECT_VIEW = "PROJECT_VIEW",
|
||||||
ARCHIVED = "ARCHIVED",
|
ARCHIVED = "ARCHIVED",
|
||||||
DRAFT = "DRAFT",
|
|
||||||
DEFAULT = "DEFAULT",
|
DEFAULT = "DEFAULT",
|
||||||
WORKSPACE_DRAFT = "WORKSPACE_DRAFT",
|
WORKSPACE_DRAFT = "WORKSPACE_DRAFT",
|
||||||
EPIC = "EPIC",
|
EPIC = "EPIC",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue