forked from github/plane
[WEB-626] chore: fix sentry issues and refactor issue actions logic for issue layouts (#3650)
* restructure the logic to avoid throwing error if any dat is not found * updated files for previous commit * fix build errors * remove throwing error if userId is undefined * optionally chain display_name property to fix sentry issues * add ooptional check * change issue action logic to increase code maintainability and make sure to send only the updated date while updating the issue * fix issue updation bugs * fix module issues build error * fix runtime errors
This commit is contained in:
parent
a852e3cc52
commit
c16a5b9b71
@ -11,7 +11,6 @@ import { FullScreenPeekView, SidePeekView } from "components/issues/peek-overvie
|
||||
// lib
|
||||
import { useMobxStore } from "lib/mobx/store-provider";
|
||||
|
||||
|
||||
export const IssuePeekOverview: React.FC = observer(() => {
|
||||
// states
|
||||
const [isSidePeekOpen, setIsSidePeekOpen] = useState(false);
|
||||
|
@ -24,7 +24,7 @@ export const AnalyticsLeaderBoard: React.FC<Props> = ({ users, title, emptyState
|
||||
<div className="mt-3 space-y-3">
|
||||
{users.map((user) => (
|
||||
<a
|
||||
key={user.display_name ?? "None"}
|
||||
key={user?.display_name ?? "None"}
|
||||
href={`/${workspaceSlug}/profile/${user.id}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
@ -36,16 +36,16 @@ export const AnalyticsLeaderBoard: React.FC<Props> = ({ users, title, emptyState
|
||||
<img
|
||||
src={user.avatar}
|
||||
className="absolute left-0 top-0 h-full w-full rounded-full object-cover"
|
||||
alt={user.display_name ?? "None"}
|
||||
alt={user?.display_name ?? "None"}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid h-4 w-4 flex-shrink-0 place-items-center rounded-full bg-gray-700 text-[11px] capitalize text-white">
|
||||
{user.display_name !== "" ? user?.display_name?.[0] : "?"}
|
||||
{user?.display_name !== "" ? user?.display_name?.[0] : "?"}
|
||||
</div>
|
||||
)}
|
||||
<span className="break-words text-custom-text-200">
|
||||
{user.display_name !== "" ? `${user.display_name}` : "No assignee"}
|
||||
{user?.display_name !== "" ? `${user?.display_name}` : "No assignee"}
|
||||
</span>
|
||||
</div>
|
||||
<span className="flex-shrink-0">{user.count}</span>
|
||||
|
@ -137,8 +137,8 @@ export const SidebarProgressStats: React.FC<Props> = ({
|
||||
key={assignee.assignee_id}
|
||||
title={
|
||||
<div className="flex items-center gap-2">
|
||||
<Avatar name={assignee.display_name ?? undefined} src={assignee?.avatar ?? undefined} />
|
||||
<span>{assignee.display_name}</span>
|
||||
<Avatar name={assignee?.display_name ?? undefined} src={assignee?.avatar ?? undefined} />
|
||||
<span>{assignee?.display_name ?? ""}</span>
|
||||
</div>
|
||||
}
|
||||
completed={assignee.completed_issues}
|
||||
|
@ -82,7 +82,7 @@ export const ActiveCycleProgressStats: React.FC<Props> = ({ cycle }) => {
|
||||
<div className="flex items-center gap-2">
|
||||
<Avatar name={assignee?.display_name ?? undefined} src={assignee?.avatar ?? undefined} />
|
||||
|
||||
<span>{assignee.display_name}</span>
|
||||
<span>{assignee?.display_name ?? ""}</span>
|
||||
</div>
|
||||
}
|
||||
completed={assignee.completed_issues}
|
||||
|
@ -21,11 +21,7 @@ export const CycleMobileHeader = () => {
|
||||
{ key: "calendar", title: "Calendar", icon: Calendar },
|
||||
];
|
||||
|
||||
const { workspaceSlug, projectId, cycleId } = router.query as {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
cycleId: string;
|
||||
};
|
||||
const { workspaceSlug, projectId, cycleId } = router.query;
|
||||
const cycleDetails = cycleId ? getCycleById(cycleId.toString()) : undefined;
|
||||
// store hooks
|
||||
const {
|
||||
@ -35,8 +31,14 @@ export const CycleMobileHeader = () => {
|
||||
|
||||
const handleLayoutChange = useCallback(
|
||||
(layout: TIssueLayouts) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_FILTERS, { layout: layout }, cycleId);
|
||||
if (!workspaceSlug || !projectId || !cycleId) return;
|
||||
updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.DISPLAY_FILTERS,
|
||||
{ layout: layout },
|
||||
cycleId.toString()
|
||||
);
|
||||
},
|
||||
[workspaceSlug, projectId, cycleId, updateFilters]
|
||||
);
|
||||
@ -49,7 +51,7 @@ export const CycleMobileHeader = () => {
|
||||
|
||||
const handleFiltersUpdate = useCallback(
|
||||
(key: keyof IIssueFilterOptions, value: string | string[]) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
if (!workspaceSlug || !projectId || !cycleId) return;
|
||||
const newValues = issueFilters?.filters?.[key] ?? [];
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
@ -61,23 +63,41 @@ export const CycleMobileHeader = () => {
|
||||
else newValues.push(value);
|
||||
}
|
||||
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.FILTERS, { [key]: newValues }, cycleId);
|
||||
updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.FILTERS,
|
||||
{ [key]: newValues },
|
||||
cycleId.toString()
|
||||
);
|
||||
},
|
||||
[workspaceSlug, projectId, cycleId, issueFilters, updateFilters]
|
||||
);
|
||||
|
||||
const handleDisplayFilters = useCallback(
|
||||
(updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_FILTERS, updatedDisplayFilter, cycleId);
|
||||
if (!workspaceSlug || !projectId || !cycleId) return;
|
||||
updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.DISPLAY_FILTERS,
|
||||
updatedDisplayFilter,
|
||||
cycleId.toString()
|
||||
);
|
||||
},
|
||||
[workspaceSlug, projectId, cycleId, updateFilters]
|
||||
);
|
||||
|
||||
const handleDisplayProperties = useCallback(
|
||||
(property: Partial<IIssueDisplayProperties>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_PROPERTIES, property, cycleId);
|
||||
if (!workspaceSlug || !projectId || !cycleId) return;
|
||||
updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.DISPLAY_PROPERTIES,
|
||||
property,
|
||||
cycleId.toString()
|
||||
);
|
||||
},
|
||||
[workspaceSlug, projectId, cycleId, updateFilters]
|
||||
);
|
||||
|
@ -71,7 +71,7 @@ export const RecentActivityWidget: React.FC<WidgetProps> = observer((props) => {
|
||||
<div className="-mt-1 break-words">
|
||||
<p className="text-sm text-custom-text-200">
|
||||
<span className="font-medium text-custom-text-100">
|
||||
{currentUser?.id === activity.actor_detail.id ? "You" : activity.actor_detail.display_name}{" "}
|
||||
{currentUser?.id === activity.actor_detail.id ? "You" : activity.actor_detail?.display_name}{" "}
|
||||
</span>
|
||||
{activity.field ? (
|
||||
<ActivityMessage activity={activity} showIssue />
|
||||
|
@ -100,14 +100,14 @@ export const CollaboratorsList: React.FC<CollaboratorsListProps> = (props) => {
|
||||
|
||||
updateIsLoading?.(false);
|
||||
updateTotalPages(widgetStats.total_pages);
|
||||
updateResultsCount(widgetStats.results.length);
|
||||
updateResultsCount(widgetStats.results?.length);
|
||||
}, [updateIsLoading, updateResultsCount, updateTotalPages, widgetStats]);
|
||||
|
||||
if (!widgetStats) return <WidgetLoader widgetKey={WIDGET_KEY} />;
|
||||
if (!widgetStats || !widgetStats?.results) return <WidgetLoader widgetKey={WIDGET_KEY} />;
|
||||
|
||||
return (
|
||||
<>
|
||||
{widgetStats?.results.map((user) => (
|
||||
{widgetStats?.results?.map((user) => (
|
||||
<CollaboratorListItem
|
||||
key={user.user_id}
|
||||
issueCount={user.active_issue_count}
|
||||
|
@ -25,6 +25,7 @@ import {
|
||||
useUser,
|
||||
useIssues,
|
||||
} from "hooks/store";
|
||||
import { useIssuesActions } from "hooks/use-issues-actions";
|
||||
import useLocalStorage from "hooks/use-local-storage";
|
||||
// components
|
||||
// ui
|
||||
@ -67,8 +68,9 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
||||
const { workspaceSlug, projectId, moduleId } = router.query;
|
||||
// store hooks
|
||||
const {
|
||||
issuesFilter: { issueFilters, updateFilters },
|
||||
issuesFilter: { issueFilters },
|
||||
} = useIssues(EIssuesStoreType.MODULE);
|
||||
const { updateFilters } = useIssuesActions(EIssuesStoreType.MODULE);
|
||||
const { projectModuleIds, getModuleById } = useModule();
|
||||
const {
|
||||
commandPalette: { toggleCreateIssueModal },
|
||||
@ -95,21 +97,15 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
||||
|
||||
const handleLayoutChange = useCallback(
|
||||
(layout: TIssueLayouts) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.DISPLAY_FILTERS,
|
||||
{ layout: layout },
|
||||
moduleId?.toString()
|
||||
);
|
||||
if (!projectId) return;
|
||||
updateFilters(projectId.toString(), EIssueFilterType.DISPLAY_FILTERS, { layout: layout });
|
||||
},
|
||||
[workspaceSlug, projectId, moduleId, updateFilters]
|
||||
[projectId, moduleId, updateFilters]
|
||||
);
|
||||
|
||||
const handleFiltersUpdate = useCallback(
|
||||
(key: keyof IIssueFilterOptions, value: string | string[]) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
if (!projectId) return;
|
||||
const newValues = issueFilters?.filters?.[key] ?? [];
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
@ -121,43 +117,25 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
|
||||
else newValues.push(value);
|
||||
}
|
||||
|
||||
updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.FILTERS,
|
||||
{ [key]: newValues },
|
||||
moduleId?.toString()
|
||||
);
|
||||
updateFilters(projectId.toString(), EIssueFilterType.FILTERS, { [key]: newValues });
|
||||
},
|
||||
[workspaceSlug, projectId, moduleId, issueFilters, updateFilters]
|
||||
[projectId, moduleId, issueFilters, updateFilters]
|
||||
);
|
||||
|
||||
const handleDisplayFilters = useCallback(
|
||||
(updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.DISPLAY_FILTERS,
|
||||
updatedDisplayFilter,
|
||||
moduleId?.toString()
|
||||
);
|
||||
if (!projectId) return;
|
||||
updateFilters(projectId.toString(), EIssueFilterType.DISPLAY_FILTERS, updatedDisplayFilter);
|
||||
},
|
||||
[workspaceSlug, projectId, moduleId, updateFilters]
|
||||
[projectId, moduleId, updateFilters]
|
||||
);
|
||||
|
||||
const handleDisplayProperties = useCallback(
|
||||
(property: Partial<IIssueDisplayProperties>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.DISPLAY_PROPERTIES,
|
||||
property,
|
||||
moduleId?.toString()
|
||||
);
|
||||
if (!projectId) return;
|
||||
updateFilters(projectId.toString(), EIssueFilterType.DISPLAY_PROPERTIES, property);
|
||||
},
|
||||
[workspaceSlug, projectId, moduleId, updateFilters]
|
||||
[projectId, moduleId, updateFilters]
|
||||
);
|
||||
|
||||
// derived values
|
||||
|
@ -33,11 +33,7 @@ import { ProjectLogo } from "components/project";
|
||||
export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, viewId } = router.query as {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
viewId: string;
|
||||
};
|
||||
const { workspaceSlug, projectId, viewId } = router.query;
|
||||
// store hooks
|
||||
const {
|
||||
issuesFilter: { issueFilters, updateFilters },
|
||||
@ -61,15 +57,21 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
||||
|
||||
const handleLayoutChange = useCallback(
|
||||
(layout: TIssueLayouts) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_FILTERS, { layout: layout }, viewId);
|
||||
if (!workspaceSlug || !projectId || !viewId) return;
|
||||
updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.DISPLAY_FILTERS,
|
||||
{ layout: layout },
|
||||
viewId.toString()
|
||||
);
|
||||
},
|
||||
[workspaceSlug, projectId, viewId, updateFilters]
|
||||
);
|
||||
|
||||
const handleFiltersUpdate = useCallback(
|
||||
(key: keyof IIssueFilterOptions, value: string | string[]) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
if (!workspaceSlug || !projectId || !viewId) return;
|
||||
const newValues = issueFilters?.filters?.[key] ?? [];
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
@ -81,23 +83,41 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
|
||||
else newValues.push(value);
|
||||
}
|
||||
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.FILTERS, { [key]: newValues }, viewId);
|
||||
updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.FILTERS,
|
||||
{ [key]: newValues },
|
||||
viewId.toString()
|
||||
);
|
||||
},
|
||||
[workspaceSlug, projectId, viewId, issueFilters, updateFilters]
|
||||
);
|
||||
|
||||
const handleDisplayFilters = useCallback(
|
||||
(updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_FILTERS, updatedDisplayFilter, viewId);
|
||||
if (!workspaceSlug || !projectId || !viewId) return;
|
||||
updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.DISPLAY_FILTERS,
|
||||
updatedDisplayFilter,
|
||||
viewId.toString()
|
||||
);
|
||||
},
|
||||
[workspaceSlug, projectId, viewId, updateFilters]
|
||||
);
|
||||
|
||||
const handleDisplayProperties = useCallback(
|
||||
(property: Partial<IIssueDisplayProperties>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.DISPLAY_PROPERTIES, property, viewId);
|
||||
if (!workspaceSlug || !projectId || !viewId) return;
|
||||
updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.DISPLAY_PROPERTIES,
|
||||
property,
|
||||
viewId.toString()
|
||||
);
|
||||
},
|
||||
[workspaceSlug, projectId, viewId, updateFilters]
|
||||
);
|
||||
|
@ -44,16 +44,27 @@ export const SingleUserSelect: React.FC<Props> = ({ collaborator, index, users,
|
||||
workspaceSlug ? () => workspaceService.fetchWorkspaceMembers(workspaceSlug.toString()) : null
|
||||
);
|
||||
|
||||
const options = members?.map((member) => ({
|
||||
value: member.member.display_name,
|
||||
query: member.member.display_name ?? "",
|
||||
content: (
|
||||
<div className="flex items-center gap-2">
|
||||
<Avatar name={member?.member.display_name} src={member?.member.avatar} />
|
||||
{member.member.display_name}
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
const options = members
|
||||
?.map((member) => {
|
||||
if (!member?.member) return;
|
||||
return {
|
||||
value: member.member?.display_name,
|
||||
query: member.member?.display_name ?? "",
|
||||
content: (
|
||||
<div className="flex items-center gap-2">
|
||||
<Avatar name={member?.member?.display_name} src={member?.member?.avatar} />
|
||||
{member.member?.display_name}
|
||||
</div>
|
||||
),
|
||||
};
|
||||
})
|
||||
.filter((member) => !!member) as
|
||||
| {
|
||||
value: string;
|
||||
query: string;
|
||||
content: JSX.Element;
|
||||
}[]
|
||||
| undefined;
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-3 items-center gap-2 rounded-md bg-custom-background-80 px-2 py-3">
|
||||
|
@ -33,16 +33,27 @@ export const JiraImportUsers: FC = () => {
|
||||
workspaceSlug ? () => workspaceService.fetchWorkspaceMembers(workspaceSlug?.toString() ?? "") : null
|
||||
);
|
||||
|
||||
const options = members?.map((member) => ({
|
||||
value: member.member.email,
|
||||
query: member.member.display_name ?? "",
|
||||
content: (
|
||||
<div className="flex items-center gap-2">
|
||||
<Avatar name={member?.member.display_name} src={member?.member.avatar} />
|
||||
{member.member.display_name}
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
const options = members
|
||||
?.map((member) => {
|
||||
if (!member?.member) return;
|
||||
return {
|
||||
value: member.member.email,
|
||||
query: member.member.display_name ?? "",
|
||||
content: (
|
||||
<div className="flex items-center gap-2">
|
||||
<Avatar name={member?.member.display_name} src={member?.member.avatar} />
|
||||
{member.member.display_name}
|
||||
</div>
|
||||
),
|
||||
};
|
||||
})
|
||||
.filter((member) => !!member) as
|
||||
| {
|
||||
value: string;
|
||||
query: string;
|
||||
content: JSX.Element;
|
||||
}[]
|
||||
| undefined;
|
||||
|
||||
return (
|
||||
<div className="h-full w-full space-y-10 divide-y-2 divide-custom-border-200 overflow-y-auto">
|
||||
|
@ -40,7 +40,7 @@ export const SingleImport: React.FC<Props> = ({ service, refreshing, handleDelet
|
||||
</h4>
|
||||
<div className="mt-2 flex items-center gap-2 text-xs text-custom-text-200">
|
||||
<span>{renderFormattedDate(service.created_at)}</span>|
|
||||
<span>Imported by {service.initiated_by_detail.display_name}</span>
|
||||
<span>Imported by {service.initiated_by_detail?.display_name}</span>
|
||||
</div>
|
||||
</div>
|
||||
<CustomMenu ellipsis>
|
||||
|
@ -217,10 +217,10 @@ export const IssueDetailRoot: FC<TIssueDetailRoot> = observer((props) => {
|
||||
message: () => "Cycle remove from issue failed",
|
||||
},
|
||||
});
|
||||
const response = await removeFromCyclePromise;
|
||||
await removeFromCyclePromise;
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { ...response, state: "SUCCESS", element: "Issue detail page" },
|
||||
payload: { issueId, state: "SUCCESS", element: "Issue detail page" },
|
||||
updates: {
|
||||
changed_property: "cycle_id",
|
||||
change_details: "",
|
||||
|
@ -1,34 +1,30 @@
|
||||
import { FC, useCallback } from "react";
|
||||
import { FC } from "react";
|
||||
import { DragDropContext, DropResult } from "@hello-pangea/dnd";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// components
|
||||
import { TOAST_TYPE, setToast } from "@plane/ui";
|
||||
import { CalendarChart } from "components/issues";
|
||||
// hooks
|
||||
import { useIssues, useUser } from "hooks/store";
|
||||
import { useIssuesActions } from "hooks/use-issues-actions";
|
||||
// ui
|
||||
// types
|
||||
import { ICycleIssues, ICycleIssuesFilter } from "store/issue/cycle";
|
||||
import { IModuleIssues, IModuleIssuesFilter } from "store/issue/module";
|
||||
import { IProjectIssues, IProjectIssuesFilter } from "store/issue/project";
|
||||
import { IProjectViewIssues, IProjectViewIssuesFilter } from "store/issue/project-views";
|
||||
import { TGroupedIssues, TIssue } from "@plane/types";
|
||||
import { TGroupedIssues } from "@plane/types";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { IQuickActionProps } from "../list/list-view-types";
|
||||
import { EIssueActions } from "../types";
|
||||
import { handleDragDrop } from "./utils";
|
||||
import { useIssues, useUser } from "hooks/store";
|
||||
import { EUserProjectRoles } from "constants/project";
|
||||
|
||||
type CalendarStoreType =
|
||||
| EIssuesStoreType.PROJECT
|
||||
| EIssuesStoreType.MODULE
|
||||
| EIssuesStoreType.CYCLE
|
||||
| EIssuesStoreType.PROJECT_VIEW;
|
||||
|
||||
interface IBaseCalendarRoot {
|
||||
issueStore: IProjectIssues | IModuleIssues | ICycleIssues | IProjectViewIssues;
|
||||
issuesFilterStore: IProjectIssuesFilter | IModuleIssuesFilter | ICycleIssuesFilter | IProjectViewIssuesFilter;
|
||||
QuickActions: FC<IQuickActionProps>;
|
||||
issueActions: {
|
||||
[EIssueActions.DELETE]: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.UPDATE]?: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.REMOVE]?: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.ARCHIVE]?: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.RESTORE]?: (issue: TIssue) => Promise<void>;
|
||||
};
|
||||
storeType: CalendarStoreType;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<any>;
|
||||
viewId?: string;
|
||||
isCompletedCycle?: boolean;
|
||||
@ -36,10 +32,8 @@ interface IBaseCalendarRoot {
|
||||
|
||||
export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
|
||||
const {
|
||||
issueStore,
|
||||
issuesFilterStore,
|
||||
QuickActions,
|
||||
issueActions,
|
||||
storeType,
|
||||
addIssuesToView,
|
||||
viewId,
|
||||
isCompletedCycle = false,
|
||||
@ -50,16 +44,18 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
|
||||
// hooks
|
||||
const { issueMap } = useIssues();
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { issues, issuesFilter, issueMap } = useIssues(storeType);
|
||||
const { updateIssue, removeIssue, removeIssueFromView, archiveIssue, restoreIssue, updateFilters } =
|
||||
useIssuesActions(storeType);
|
||||
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
|
||||
const displayFilters = issuesFilterStore.issueFilters?.displayFilters;
|
||||
const displayFilters = issuesFilter.issueFilters?.displayFilters;
|
||||
|
||||
const groupedIssueIds = (issueStore.groupedIssueIds ?? {}) as TGroupedIssues;
|
||||
const groupedIssueIds = (issues.groupedIssueIds ?? {}) as TGroupedIssues;
|
||||
|
||||
const onDragEnd = async (result: DropResult) => {
|
||||
if (!result) return;
|
||||
@ -76,10 +72,9 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
|
||||
result.destination,
|
||||
workspaceSlug?.toString(),
|
||||
projectId?.toString(),
|
||||
issueStore,
|
||||
issueMap,
|
||||
groupedIssueIds,
|
||||
viewId
|
||||
updateIssue
|
||||
).catch((err) => {
|
||||
setToast({
|
||||
title: "Error",
|
||||
@ -90,21 +85,12 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleIssues = useCallback(
|
||||
async (date: string, issue: TIssue, action: EIssueActions) => {
|
||||
if (issueActions[action]) {
|
||||
await issueActions[action]!(issue);
|
||||
}
|
||||
},
|
||||
[issueActions]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="h-full w-full overflow-hidden bg-custom-background-100 pt-4">
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
<CalendarChart
|
||||
issuesFilterStore={issuesFilterStore}
|
||||
issuesFilterStore={issuesFilter}
|
||||
issues={issueMap}
|
||||
groupedIssueIds={groupedIssueIds}
|
||||
layout={displayFilters?.calendar?.layout}
|
||||
@ -113,34 +99,21 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
|
||||
<QuickActions
|
||||
customActionButton={customActionButton}
|
||||
issue={issue}
|
||||
handleDelete={async () => handleIssues(issue.target_date ?? "", issue, EIssueActions.DELETE)}
|
||||
handleUpdate={
|
||||
issueActions[EIssueActions.UPDATE]
|
||||
? async (data) => handleIssues(issue.target_date ?? "", data, EIssueActions.UPDATE)
|
||||
: undefined
|
||||
}
|
||||
handleRemoveFromView={
|
||||
issueActions[EIssueActions.REMOVE]
|
||||
? async () => handleIssues(issue.target_date ?? "", issue, EIssueActions.REMOVE)
|
||||
: undefined
|
||||
}
|
||||
handleArchive={
|
||||
issueActions[EIssueActions.ARCHIVE]
|
||||
? async () => handleIssues(issue.target_date ?? "", issue, EIssueActions.ARCHIVE)
|
||||
: undefined
|
||||
}
|
||||
handleRestore={
|
||||
issueActions[EIssueActions.RESTORE]
|
||||
? async () => handleIssues(issue.target_date ?? "", issue, EIssueActions.RESTORE)
|
||||
: undefined
|
||||
handleDelete={async () => removeIssue(issue.project_id, issue.id)}
|
||||
handleUpdate={async (data) => updateIssue && updateIssue(issue.project_id, issue.id, data)}
|
||||
handleRemoveFromView={async () =>
|
||||
removeIssueFromView && removeIssueFromView(issue.project_id, issue.id)
|
||||
}
|
||||
handleArchive={async () => archiveIssue && archiveIssue(issue.project_id, issue.id)}
|
||||
handleRestore={async () => restoreIssue && restoreIssue(issue.project_id, issue.id)}
|
||||
readOnly={!isEditingAllowed || isCompletedCycle}
|
||||
/>
|
||||
)}
|
||||
addIssuesToView={addIssuesToView}
|
||||
quickAddCallback={issueStore.quickAddIssue}
|
||||
quickAddCallback={issues.quickAddIssue}
|
||||
viewId={viewId}
|
||||
readOnly={!isEditingAllowed || isCompletedCycle}
|
||||
updateFilters={updateFilters}
|
||||
/>
|
||||
</DragDropContext>
|
||||
</div>
|
||||
|
@ -5,8 +5,10 @@ import { observer } from "mobx-react-lite";
|
||||
import { Spinner } from "@plane/ui";
|
||||
import { CalendarHeader, CalendarWeekDays, CalendarWeekHeader } from "components/issues";
|
||||
// types
|
||||
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TGroupedIssues, TIssue, TIssueKanbanFilters, TIssueMap } from "@plane/types";
|
||||
import { ICalendarWeek } from "./types";
|
||||
// constants
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { EIssueFilterType, EIssuesStoreType } from "constants/issue";
|
||||
import { EUserProjectRoles } from "constants/project";
|
||||
import { useIssues, useUser } from "hooks/store";
|
||||
import { useCalendarView } from "hooks/store/use-calendar-view";
|
||||
@ -14,8 +16,6 @@ import { ICycleIssuesFilter } from "store/issue/cycle";
|
||||
import { IModuleIssuesFilter } from "store/issue/module";
|
||||
import { IProjectIssuesFilter } from "store/issue/project";
|
||||
import { IProjectViewIssuesFilter } from "store/issue/project-views";
|
||||
import { TGroupedIssues, TIssue, TIssueMap } from "@plane/types";
|
||||
import { ICalendarWeek } from "./types";
|
||||
|
||||
type Props = {
|
||||
issuesFilterStore: IProjectIssuesFilter | IModuleIssuesFilter | ICycleIssuesFilter | IProjectViewIssuesFilter;
|
||||
@ -33,6 +33,11 @@ type Props = {
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<any>;
|
||||
viewId?: string;
|
||||
readOnly?: boolean;
|
||||
updateFilters?: (
|
||||
projectId: string,
|
||||
filterType: EIssueFilterType,
|
||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters
|
||||
) => Promise<void>;
|
||||
};
|
||||
|
||||
export const CalendarChart: React.FC<Props> = observer((props) => {
|
||||
@ -46,6 +51,7 @@ export const CalendarChart: React.FC<Props> = observer((props) => {
|
||||
quickAddCallback,
|
||||
addIssuesToView,
|
||||
viewId,
|
||||
updateFilters,
|
||||
readOnly = false,
|
||||
} = props;
|
||||
// store hooks
|
||||
@ -74,7 +80,7 @@ export const CalendarChart: React.FC<Props> = observer((props) => {
|
||||
return (
|
||||
<>
|
||||
<div className="flex h-full w-full flex-col overflow-hidden">
|
||||
<CalendarHeader issuesFilterStore={issuesFilterStore} viewId={viewId} />
|
||||
<CalendarHeader issuesFilterStore={issuesFilterStore} updateFilters={updateFilters} />
|
||||
<div className="flex h-full w-full vertical-scrollbar scrollbar-lg flex-col">
|
||||
<CalendarWeekHeader isLoading={!issues} showWeekends={showWeekends} />
|
||||
<div className="h-full w-full">
|
||||
|
@ -9,6 +9,7 @@ import { Popover, Transition } from "@headlessui/react";
|
||||
import { Check, ChevronUp } from "lucide-react";
|
||||
import { ToggleSwitch } from "@plane/ui";
|
||||
// types
|
||||
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TCalendarLayouts, TIssueKanbanFilters } from "@plane/types";
|
||||
// constants
|
||||
import { CALENDAR_LAYOUTS } from "constants/calendar";
|
||||
import { EIssueFilterType } from "constants/issue";
|
||||
@ -17,18 +18,21 @@ import { ICycleIssuesFilter } from "store/issue/cycle";
|
||||
import { IModuleIssuesFilter } from "store/issue/module";
|
||||
import { IProjectIssuesFilter } from "store/issue/project";
|
||||
import { IProjectViewIssuesFilter } from "store/issue/project-views";
|
||||
import { TCalendarLayouts } from "@plane/types";
|
||||
|
||||
interface ICalendarHeader {
|
||||
issuesFilterStore: IProjectIssuesFilter | IModuleIssuesFilter | ICycleIssuesFilter | IProjectViewIssuesFilter;
|
||||
viewId?: string;
|
||||
updateFilters?: (
|
||||
projectId: string,
|
||||
filterType: EIssueFilterType,
|
||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters
|
||||
) => Promise<void>;
|
||||
}
|
||||
|
||||
export const CalendarOptionsDropdown: React.FC<ICalendarHeader> = observer((props) => {
|
||||
const { issuesFilterStore, viewId } = props;
|
||||
const { issuesFilterStore, updateFilters } = props;
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
const { projectId } = router.query;
|
||||
|
||||
const issueCalendarView = useCalendarView();
|
||||
|
||||
@ -51,20 +55,14 @@ export const CalendarOptionsDropdown: React.FC<ICalendarHeader> = observer((prop
|
||||
const showWeekends = issuesFilterStore.issueFilters?.displayFilters?.calendar?.show_weekends ?? false;
|
||||
|
||||
const handleLayoutChange = (layout: TCalendarLayouts) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
if (!projectId || !updateFilters) return;
|
||||
|
||||
issuesFilterStore.updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.DISPLAY_FILTERS,
|
||||
{
|
||||
calendar: {
|
||||
...issuesFilterStore.issueFilters?.displayFilters?.calendar,
|
||||
layout,
|
||||
},
|
||||
updateFilters(projectId.toString(), EIssueFilterType.DISPLAY_FILTERS, {
|
||||
calendar: {
|
||||
...issuesFilterStore.issueFilters?.displayFilters?.calendar,
|
||||
layout,
|
||||
},
|
||||
viewId
|
||||
);
|
||||
});
|
||||
|
||||
issueCalendarView.updateCalendarPayload(
|
||||
layout === "month"
|
||||
@ -76,20 +74,14 @@ export const CalendarOptionsDropdown: React.FC<ICalendarHeader> = observer((prop
|
||||
const handleToggleWeekends = () => {
|
||||
const showWeekends = issuesFilterStore.issueFilters?.displayFilters?.calendar?.show_weekends ?? false;
|
||||
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
if (!projectId || !updateFilters) return;
|
||||
|
||||
issuesFilterStore.updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.DISPLAY_FILTERS,
|
||||
{
|
||||
calendar: {
|
||||
...issuesFilterStore.issueFilters?.displayFilters?.calendar,
|
||||
show_weekends: !showWeekends,
|
||||
},
|
||||
updateFilters(projectId.toString(), EIssueFilterType.DISPLAY_FILTERS, {
|
||||
calendar: {
|
||||
...issuesFilterStore.issueFilters?.displayFilters?.calendar,
|
||||
show_weekends: !showWeekends,
|
||||
},
|
||||
viewId
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -9,14 +9,25 @@ import { ICycleIssuesFilter } from "store/issue/cycle";
|
||||
import { IModuleIssuesFilter } from "store/issue/module";
|
||||
import { IProjectIssuesFilter } from "store/issue/project";
|
||||
import { IProjectViewIssuesFilter } from "store/issue/project-views";
|
||||
import { EIssueFilterType } from "constants/issue";
|
||||
import {
|
||||
IIssueDisplayFilterOptions,
|
||||
IIssueDisplayProperties,
|
||||
IIssueFilterOptions,
|
||||
TIssueKanbanFilters,
|
||||
} from "@plane/types";
|
||||
|
||||
interface ICalendarHeader {
|
||||
issuesFilterStore: IProjectIssuesFilter | IModuleIssuesFilter | ICycleIssuesFilter | IProjectViewIssuesFilter;
|
||||
viewId?: string;
|
||||
updateFilters?: (
|
||||
projectId: string,
|
||||
filterType: EIssueFilterType,
|
||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters
|
||||
) => Promise<void>;
|
||||
}
|
||||
|
||||
export const CalendarHeader: React.FC<ICalendarHeader> = observer((props) => {
|
||||
const { issuesFilterStore, viewId } = props;
|
||||
const { issuesFilterStore, updateFilters } = props;
|
||||
|
||||
const issueCalendarView = useCalendarView();
|
||||
|
||||
@ -101,7 +112,7 @@ export const CalendarHeader: React.FC<ICalendarHeader> = observer((props) => {
|
||||
>
|
||||
Today
|
||||
</button>
|
||||
<CalendarOptionsDropdown issuesFilterStore={issuesFilterStore} viewId={viewId} />
|
||||
<CalendarOptionsDropdown issuesFilterStore={issuesFilterStore} updateFilters={updateFilters} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { useCallback } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
//hooks
|
||||
@ -7,40 +7,15 @@ import { useCycle, useIssues } from "hooks/store";
|
||||
import { CycleIssueQuickActions } from "components/issues";
|
||||
import { BaseCalendarRoot } from "../base-calendar-root";
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../../types";
|
||||
// constants
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
|
||||
export const CycleCalendarLayout: React.FC = observer(() => {
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.CYCLE);
|
||||
const { currentProjectCompletedCycleIds } = useCycle();
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, cycleId } = router.query;
|
||||
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
|
||||
await issues.updateIssue(workspaceSlug.toString(), issue.project_id, issue.id, issue, cycleId.toString());
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
await issues.removeIssue(workspaceSlug.toString(), issue.project_id, issue.id, cycleId.toString());
|
||||
},
|
||||
[EIssueActions.REMOVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId || !projectId) return;
|
||||
await issues.removeIssueFromCycle(workspaceSlug.toString(), issue.project_id, cycleId.toString(), issue.id);
|
||||
},
|
||||
[EIssueActions.ARCHIVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
await issues.archiveIssue(workspaceSlug.toString(), issue.project_id, issue.id, cycleId.toString());
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug, cycleId, projectId]
|
||||
);
|
||||
const { issues } = useIssues(EIssuesStoreType.CYCLE);
|
||||
|
||||
if (!cycleId) return null;
|
||||
|
||||
@ -57,13 +32,11 @@ export const CycleCalendarLayout: React.FC = observer(() => {
|
||||
|
||||
return (
|
||||
<BaseCalendarRoot
|
||||
issueStore={issues}
|
||||
issuesFilterStore={issuesFilter}
|
||||
QuickActions={CycleIssueQuickActions}
|
||||
addIssuesToView={addIssuesToView}
|
||||
issueActions={issueActions}
|
||||
viewId={cycleId.toString()}
|
||||
isCompletedCycle={isCompletedCycle}
|
||||
storeType={EIssuesStoreType.CYCLE}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -1,47 +1,22 @@
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { useCallback } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// hooks
|
||||
import { useIssues } from "hooks/store";
|
||||
// components
|
||||
import { ModuleIssueQuickActions } from "components/issues";
|
||||
import { BaseCalendarRoot } from "../base-calendar-root";
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../../types";
|
||||
// constants
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { useIssues } from "hooks/store";
|
||||
|
||||
export const ModuleCalendarLayout: React.FC = observer(() => {
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.MODULE);
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, moduleId } = router.query as {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
moduleId: string;
|
||||
};
|
||||
const { workspaceSlug, projectId, moduleId } = router.query ;
|
||||
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
await issues.updateIssue(workspaceSlug, issue.project_id, issue.id, issue, moduleId);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
await issues.removeIssue(workspaceSlug, issue.project_id, issue.id, moduleId);
|
||||
},
|
||||
[EIssueActions.REMOVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
await issues.removeIssueFromModule(workspaceSlug, issue.project_id, moduleId, issue.id);
|
||||
},
|
||||
[EIssueActions.ARCHIVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
await issues.archiveIssue(workspaceSlug, issue.project_id, issue.id, moduleId);
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug, moduleId]
|
||||
);
|
||||
const {issues} = useIssues(EIssuesStoreType.MODULE)
|
||||
|
||||
if (!moduleId) return null;
|
||||
|
||||
const addIssuesToView = useCallback(
|
||||
(issueIds: string[]) => {
|
||||
@ -53,12 +28,10 @@ export const ModuleCalendarLayout: React.FC = observer(() => {
|
||||
|
||||
return (
|
||||
<BaseCalendarRoot
|
||||
issueStore={issues}
|
||||
issuesFilterStore={issuesFilter}
|
||||
QuickActions={ModuleIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
storeType={EIssuesStoreType.MODULE}
|
||||
addIssuesToView={addIssuesToView}
|
||||
viewId={moduleId}
|
||||
viewId={moduleId.toString()}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -1,48 +1,10 @@
|
||||
import { useMemo } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// hooks
|
||||
import { ProjectIssueQuickActions } from "components/issues";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { useIssues } from "hooks/store";
|
||||
// components
|
||||
import { TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../../types";
|
||||
import { BaseCalendarRoot } from "../base-calendar-root";
|
||||
|
||||
export const CalendarLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT);
|
||||
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
await issues.updateIssue(workspaceSlug.toString(), issue.project_id, issue.id, issue);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
await issues.removeIssue(workspaceSlug.toString(), issue.project_id, issue.id);
|
||||
},
|
||||
[EIssueActions.ARCHIVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
await issues.archiveIssue(workspaceSlug.toString(), issue.project_id, issue.id);
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug]
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseCalendarRoot
|
||||
issueStore={issues}
|
||||
issuesFilterStore={issuesFilter}
|
||||
QuickActions={ProjectIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
/>
|
||||
);
|
||||
});
|
||||
export const CalendarLayout: React.FC = observer(() => (
|
||||
<BaseCalendarRoot QuickActions={ProjectIssueQuickActions} storeType={EIssuesStoreType.PROJECT} />
|
||||
));
|
||||
|
@ -3,38 +3,21 @@ import { useRouter } from "next/router";
|
||||
// hooks
|
||||
import { ProjectIssueQuickActions } from "components/issues";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { useIssues } from "hooks/store";
|
||||
// components
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../../types";
|
||||
import { BaseCalendarRoot } from "../base-calendar-root";
|
||||
// constants
|
||||
|
||||
export interface IViewCalendarLayout {
|
||||
issueActions: {
|
||||
[EIssueActions.DELETE]: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.UPDATE]?: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.REMOVE]?: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.ARCHIVE]?: (issue: TIssue) => Promise<void>;
|
||||
};
|
||||
}
|
||||
|
||||
export const ProjectViewCalendarLayout: React.FC<IViewCalendarLayout> = observer((props) => {
|
||||
const { issueActions } = props;
|
||||
// store
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT_VIEW);
|
||||
export const ProjectViewCalendarLayout: React.FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { viewId } = router.query;
|
||||
|
||||
return (
|
||||
<BaseCalendarRoot
|
||||
issueStore={issues}
|
||||
issuesFilterStore={issuesFilter}
|
||||
QuickActions={ProjectIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
viewId={viewId?.toString()}
|
||||
storeType={EIssuesStoreType.PROJECT_VIEW}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -1,21 +1,16 @@
|
||||
import { DraggableLocation } from "@hello-pangea/dnd";
|
||||
import { ICycleIssues } from "store/issue/cycle";
|
||||
import { IModuleIssues } from "store/issue/module";
|
||||
import { IProjectIssues } from "store/issue/project";
|
||||
import { IProjectViewIssues } from "store/issue/project-views";
|
||||
import { TGroupedIssues, IIssueMap } from "@plane/types";
|
||||
import { TGroupedIssues, IIssueMap, TIssue } from "@plane/types";
|
||||
|
||||
export const handleDragDrop = async (
|
||||
source: DraggableLocation,
|
||||
destination: DraggableLocation,
|
||||
workspaceSlug: string | undefined,
|
||||
projectId: string | undefined,
|
||||
store: IProjectIssues | IModuleIssues | ICycleIssues | IProjectViewIssues,
|
||||
issueMap: IIssueMap,
|
||||
issueWithIds: TGroupedIssues,
|
||||
viewId: string | null = null // it can be moduleId, cycleId
|
||||
updateIssue?: (projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>
|
||||
) => {
|
||||
if (!issueMap || !issueWithIds || !workspaceSlug || !projectId) return;
|
||||
if (!issueMap || !issueWithIds || !workspaceSlug || !projectId || !updateIssue) return;
|
||||
|
||||
const sourceColumnId = source?.droppableId || null;
|
||||
const destinationColumnId = destination?.droppableId || null;
|
||||
@ -31,12 +26,11 @@ export const handleDragDrop = async (
|
||||
const [removed] = sourceIssues.splice(source.index, 1);
|
||||
const removedIssueDetail = issueMap[removed];
|
||||
|
||||
const updateIssue = {
|
||||
const updatedIssue = {
|
||||
id: removedIssueDetail?.id,
|
||||
target_date: destinationColumnId,
|
||||
};
|
||||
|
||||
if (viewId) return await store?.updateIssue(workspaceSlug, projectId, updateIssue.id, updateIssue, viewId);
|
||||
else return await store?.updateIssue(workspaceSlug, projectId, updateIssue.id, updateIssue);
|
||||
return await updateIssue(projectId, updatedIssue.id, updatedIssue);
|
||||
}
|
||||
};
|
||||
|
@ -11,11 +11,7 @@ import { IIssueFilterOptions } from "@plane/types";
|
||||
export const CycleAppliedFiltersRoot: React.FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, cycleId } = router.query as {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
cycleId: string;
|
||||
};
|
||||
const { workspaceSlug, projectId, cycleId } = router.query;
|
||||
// store hooks
|
||||
const {
|
||||
issuesFilter: { issueFilters, updateFilters },
|
||||
@ -37,13 +33,13 @@ export const CycleAppliedFiltersRoot: React.FC = observer(() => {
|
||||
if (!workspaceSlug || !projectId || !cycleId) return;
|
||||
if (!value) {
|
||||
updateFilters(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.FILTERS,
|
||||
{
|
||||
[key]: null,
|
||||
},
|
||||
cycleId
|
||||
cycleId.toString()
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -52,13 +48,13 @@ export const CycleAppliedFiltersRoot: React.FC = observer(() => {
|
||||
newValues = newValues.filter((val) => val !== value);
|
||||
|
||||
updateFilters(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.FILTERS,
|
||||
{
|
||||
[key]: newValues,
|
||||
},
|
||||
cycleId
|
||||
cycleId.toString()
|
||||
);
|
||||
};
|
||||
|
||||
@ -68,11 +64,17 @@ export const CycleAppliedFiltersRoot: React.FC = observer(() => {
|
||||
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
||||
});
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.FILTERS, { ...newFilters }, cycleId);
|
||||
updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.FILTERS,
|
||||
{ ...newFilters },
|
||||
cycleId.toString()
|
||||
);
|
||||
};
|
||||
|
||||
// return if no filters are applied
|
||||
if (Object.keys(appliedFilters).length === 0) return null;
|
||||
if (Object.keys(appliedFilters).length === 0 || !workspaceSlug || !projectId) return null;
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between p-4">
|
||||
@ -84,7 +86,11 @@ export const CycleAppliedFiltersRoot: React.FC = observer(() => {
|
||||
states={projectStates}
|
||||
/>
|
||||
|
||||
<SaveFilterView workspaceSlug={workspaceSlug} projectId={projectId} filterParams={appliedFilters} />
|
||||
<SaveFilterView
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
projectId={projectId.toString()}
|
||||
filterParams={appliedFilters}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
@ -11,11 +11,7 @@ import { IIssueFilterOptions } from "@plane/types";
|
||||
export const ModuleAppliedFiltersRoot: React.FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, moduleId } = router.query as {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
moduleId: string;
|
||||
};
|
||||
const { workspaceSlug, projectId, moduleId } = router.query;
|
||||
// store hooks
|
||||
const {
|
||||
issuesFilter: { issueFilters, updateFilters },
|
||||
@ -36,13 +32,13 @@ export const ModuleAppliedFiltersRoot: React.FC = observer(() => {
|
||||
if (!workspaceSlug || !projectId || !moduleId) return;
|
||||
if (!value) {
|
||||
updateFilters(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.FILTERS,
|
||||
{
|
||||
[key]: null,
|
||||
},
|
||||
moduleId
|
||||
moduleId.toString()
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -51,13 +47,13 @@ export const ModuleAppliedFiltersRoot: React.FC = observer(() => {
|
||||
newValues = newValues.filter((val) => val !== value);
|
||||
|
||||
updateFilters(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.FILTERS,
|
||||
{
|
||||
[key]: newValues,
|
||||
},
|
||||
moduleId
|
||||
moduleId.toString()
|
||||
);
|
||||
};
|
||||
|
||||
@ -67,11 +63,17 @@ export const ModuleAppliedFiltersRoot: React.FC = observer(() => {
|
||||
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
||||
});
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.FILTERS, { ...newFilters }, moduleId);
|
||||
updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.FILTERS,
|
||||
{ ...newFilters },
|
||||
moduleId.toString()
|
||||
);
|
||||
};
|
||||
|
||||
// return if no filters are applied
|
||||
if (Object.keys(appliedFilters).length === 0) return null;
|
||||
if (!workspaceSlug || !projectId || Object.keys(appliedFilters).length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between p-4">
|
||||
@ -83,7 +85,11 @@ export const ModuleAppliedFiltersRoot: React.FC = observer(() => {
|
||||
states={projectStates}
|
||||
/>
|
||||
|
||||
<SaveFilterView workspaceSlug={workspaceSlug} projectId={projectId} filterParams={appliedFilters} />
|
||||
<SaveFilterView
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
projectId={projectId.toString()}
|
||||
filterParams={appliedFilters}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
@ -14,11 +14,7 @@ import { IIssueFilterOptions } from "@plane/types";
|
||||
export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, viewId } = router.query as {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
viewId: string;
|
||||
};
|
||||
const { workspaceSlug, projectId, viewId } = router.query;
|
||||
// store hooks
|
||||
const {
|
||||
issuesFilter: { issueFilters, updateFilters },
|
||||
@ -39,16 +35,16 @@ export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
|
||||
});
|
||||
|
||||
const handleRemoveFilter = (key: keyof IIssueFilterOptions, value: string | null) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
if (!workspaceSlug || !projectId || !viewId) return;
|
||||
if (!value) {
|
||||
updateFilters(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.FILTERS,
|
||||
{
|
||||
[key]: null,
|
||||
},
|
||||
viewId
|
||||
viewId.toString()
|
||||
);
|
||||
return;
|
||||
}
|
||||
@ -57,23 +53,29 @@ export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
|
||||
newValues = newValues.filter((val) => val !== value);
|
||||
|
||||
updateFilters(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.FILTERS,
|
||||
{
|
||||
[key]: newValues,
|
||||
},
|
||||
viewId
|
||||
viewId.toString()
|
||||
);
|
||||
};
|
||||
|
||||
const handleClearAllFilters = () => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
if (!workspaceSlug || !projectId || !viewId) return;
|
||||
const newFilters: IIssueFilterOptions = {};
|
||||
Object.keys(userFilters ?? {}).forEach((key) => {
|
||||
newFilters[key as keyof IIssueFilterOptions] = null;
|
||||
});
|
||||
updateFilters(workspaceSlug, projectId, EIssueFilterType.FILTERS, { ...newFilters }, viewId);
|
||||
updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
projectId.toString(),
|
||||
EIssueFilterType.FILTERS,
|
||||
{ ...newFilters },
|
||||
viewId.toString()
|
||||
);
|
||||
};
|
||||
|
||||
const areFiltersEqual = isEqual(appliedFilters, viewDetails?.filters);
|
||||
|
@ -7,44 +7,43 @@ import { GanttQuickAddIssueForm, IssueGanttBlock } from "components/issues";
|
||||
import { EUserProjectRoles } from "constants/project";
|
||||
import { renderIssueBlocksStructure } from "helpers/issue.helper";
|
||||
import { useIssues, useUser } from "hooks/store";
|
||||
import { useIssuesActions } from "hooks/use-issues-actions";
|
||||
// components
|
||||
// helpers
|
||||
// types
|
||||
import { ICycleIssues, ICycleIssuesFilter } from "store/issue/cycle";
|
||||
import { IModuleIssues, IModuleIssuesFilter } from "store/issue/module";
|
||||
import { IProjectIssues, IProjectIssuesFilter } from "store/issue/project";
|
||||
import { IProjectViewIssues, IProjectViewIssuesFilter } from "store/issue/project-views";
|
||||
import { TIssue, TUnGroupedIssues } from "@plane/types";
|
||||
// constants
|
||||
import { EIssueActions } from "../types";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
|
||||
type GanttStoreType =
|
||||
| EIssuesStoreType.PROJECT
|
||||
| EIssuesStoreType.MODULE
|
||||
| EIssuesStoreType.CYCLE
|
||||
| EIssuesStoreType.PROJECT_VIEW;
|
||||
interface IBaseGanttRoot {
|
||||
issueFiltersStore: IProjectIssuesFilter | IModuleIssuesFilter | ICycleIssuesFilter | IProjectViewIssuesFilter;
|
||||
issueStore: IProjectIssues | IModuleIssues | ICycleIssues | IProjectViewIssues;
|
||||
viewId?: string;
|
||||
issueActions: {
|
||||
[EIssueActions.DELETE]: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.UPDATE]?: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.REMOVE]?: (issue: TIssue) => Promise<void>;
|
||||
};
|
||||
storeType: GanttStoreType;
|
||||
}
|
||||
|
||||
export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGanttRoot) => {
|
||||
const { issueFiltersStore, issueStore, viewId } = props;
|
||||
const { viewId, storeType } = props;
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
const { issues, issuesFilter } = useIssues(storeType);
|
||||
const { updateIssue } = useIssuesActions(storeType);
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { issueMap } = useIssues();
|
||||
const appliedDisplayFilters = issueFiltersStore.issueFilters?.displayFilters;
|
||||
const appliedDisplayFilters = issuesFilter.issueFilters?.displayFilters;
|
||||
|
||||
const issueIds = (issueStore.groupedIssueIds ?? []) as TUnGroupedIssues;
|
||||
const { enableIssueCreation } = issueStore?.viewFlags || {};
|
||||
const issueIds = (issues.groupedIssueIds ?? []) as TUnGroupedIssues;
|
||||
const { enableIssueCreation } = issues?.viewFlags || {};
|
||||
|
||||
const issues = issueIds.map((id) => issueMap?.[id]);
|
||||
const issuesArray = issueIds.map((id) => issueMap?.[id]);
|
||||
|
||||
const updateIssueBlockStructure = async (issue: TIssue, data: IBlockUpdateData) => {
|
||||
if (!workspaceSlug) return;
|
||||
@ -52,7 +51,7 @@ export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGan
|
||||
const payload: any = { ...data };
|
||||
if (data.sort_order) payload.sort_order = data.sort_order.newSortOrder;
|
||||
|
||||
await issueStore.updateIssue(workspaceSlug.toString(), issue.project_id, issue.id, payload, viewId);
|
||||
updateIssue && (await updateIssue(issue.project_id, issue.id, payload));
|
||||
};
|
||||
|
||||
const isAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
@ -64,7 +63,7 @@ export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGan
|
||||
border={false}
|
||||
title="Issues"
|
||||
loaderTitle="Issues"
|
||||
blocks={issues ? renderIssueBlocksStructure(issues as TIssue[]) : null}
|
||||
blocks={issues ? renderIssueBlocksStructure(issuesArray) : null}
|
||||
blockUpdateHandler={updateIssueBlockStructure}
|
||||
blockToRender={(data: TIssue) => <IssueGanttBlock issueId={data.id} />}
|
||||
sidebarToRender={(props) => <IssueGanttSidebar {...props} showAllBlocks />}
|
||||
@ -75,7 +74,7 @@ export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGan
|
||||
enableAddBlock={isAllowed}
|
||||
quickAdd={
|
||||
enableIssueCreation && isAllowed ? (
|
||||
<GanttQuickAddIssueForm quickAddCallback={issueStore.quickAddIssue} viewId={viewId} />
|
||||
<GanttQuickAddIssueForm quickAddCallback={issues.quickAddIssue} viewId={viewId} />
|
||||
) : undefined
|
||||
}
|
||||
showAllBlocks
|
||||
|
@ -2,47 +2,13 @@ import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// hooks
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { useCycle, useIssues } from "hooks/store";
|
||||
// components
|
||||
import { TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../types";
|
||||
import { BaseGanttRoot } from "./base-gantt-root";
|
||||
|
||||
export const CycleGanttLayout: React.FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, cycleId } = router.query;
|
||||
// store hooks
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.CYCLE);
|
||||
const { fetchCycleDetails } = useCycle();
|
||||
const { cycleId } = router.query;
|
||||
|
||||
const issueActions = {
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
|
||||
await issues.updateIssue(workspaceSlug.toString(), issue.project_id, issue.id, issue, cycleId.toString());
|
||||
fetchCycleDetails(workspaceSlug.toString(), issue.project_id, cycleId.toString());
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
|
||||
await issues.removeIssue(workspaceSlug.toString(), issue.project_id, issue.id, cycleId.toString());
|
||||
fetchCycleDetails(workspaceSlug.toString(), issue.project_id, cycleId.toString());
|
||||
},
|
||||
[EIssueActions.REMOVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId || !issue.id) return;
|
||||
|
||||
await issues.removeIssueFromCycle(workspaceSlug.toString(), issue.project_id, cycleId.toString(), issue.id);
|
||||
fetchCycleDetails(workspaceSlug.toString(), issue.project_id, cycleId.toString());
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseGanttRoot
|
||||
issueActions={issueActions}
|
||||
issueFiltersStore={issuesFilter}
|
||||
issueStore={issues}
|
||||
viewId={cycleId?.toString()}
|
||||
/>
|
||||
);
|
||||
return <BaseGanttRoot viewId={cycleId?.toString()} storeType={EIssuesStoreType.CYCLE} />;
|
||||
});
|
||||
|
@ -2,47 +2,13 @@ import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// hooks
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { useIssues, useModule } from "hooks/store";
|
||||
// components
|
||||
import { TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../types";
|
||||
import { BaseGanttRoot } from "./base-gantt-root";
|
||||
|
||||
export const ModuleGanttLayout: React.FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, moduleId } = router.query;
|
||||
// store hooks
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.MODULE);
|
||||
const { fetchModuleDetails } = useModule();
|
||||
const { moduleId } = router.query;
|
||||
|
||||
const issueActions = {
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
|
||||
await issues.updateIssue(workspaceSlug.toString(), issue.project_id, issue.id, issue, moduleId.toString());
|
||||
fetchModuleDetails(workspaceSlug.toString(), issue.project_id, moduleId.toString());
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
|
||||
await issues.removeIssue(workspaceSlug.toString(), issue.project_id, issue.id, moduleId.toString());
|
||||
fetchModuleDetails(workspaceSlug.toString(), issue.project_id, moduleId.toString());
|
||||
},
|
||||
[EIssueActions.REMOVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId || !issue.id) return;
|
||||
|
||||
await issues.removeIssueFromModule(workspaceSlug.toString(), issue.project_id, moduleId.toString(), issue.id);
|
||||
fetchModuleDetails(workspaceSlug.toString(), issue.project_id, moduleId.toString());
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseGanttRoot
|
||||
issueActions={issueActions}
|
||||
issueFiltersStore={issuesFilter}
|
||||
issueStore={issues}
|
||||
viewId={moduleId?.toString()}
|
||||
/>
|
||||
);
|
||||
return <BaseGanttRoot viewId={moduleId?.toString()} storeType={EIssuesStoreType.MODULE} />;
|
||||
});
|
||||
|
@ -1,33 +1,8 @@
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// hooks
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { useIssues } from "hooks/store";
|
||||
// components
|
||||
import { TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../types";
|
||||
import { BaseGanttRoot } from "./base-gantt-root";
|
||||
|
||||
export const GanttLayout: React.FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
// store hooks
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT);
|
||||
|
||||
const issueActions = {
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
await issues.updateIssue(workspaceSlug.toString(), issue.project_id, issue.id, issue);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
await issues.removeIssue(workspaceSlug.toString(), issue.project_id, issue.id);
|
||||
},
|
||||
};
|
||||
|
||||
return <BaseGanttRoot issueFiltersStore={issuesFilter} issueStore={issues} issueActions={issueActions} />;
|
||||
});
|
||||
export const GanttLayout: React.FC = observer(() =>( <BaseGanttRoot storeType={EIssuesStoreType.PROJECT} />));
|
||||
|
@ -2,36 +2,15 @@ import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// hooks
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { useIssues } from "hooks/store";
|
||||
// components
|
||||
import { TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../types";
|
||||
import { BaseGanttRoot } from "./base-gantt-root";
|
||||
// constants
|
||||
// types
|
||||
|
||||
export interface IViewGanttLayout {
|
||||
issueActions: {
|
||||
[EIssueActions.DELETE]: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.UPDATE]?: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.REMOVE]?: (issue: TIssue) => Promise<void>;
|
||||
};
|
||||
}
|
||||
|
||||
export const ProjectViewGanttLayout: React.FC<IViewGanttLayout> = observer((props) => {
|
||||
const { issueActions } = props;
|
||||
// store
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT_VIEW);
|
||||
export const ProjectViewGanttLayout: React.FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { viewId } = router.query;
|
||||
|
||||
return (
|
||||
<BaseGanttRoot
|
||||
issueFiltersStore={issuesFilter}
|
||||
issueStore={issues}
|
||||
issueActions={issueActions}
|
||||
viewId={viewId?.toString()}
|
||||
/>
|
||||
);
|
||||
return <BaseGanttRoot viewId={viewId?.toString()} storeType={EIssuesStoreType.PROJECT_VIEW} />;
|
||||
});
|
||||
|
@ -6,45 +6,31 @@ import { useRouter } from "next/router";
|
||||
import { Spinner, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
import { DeleteIssueModal } from "components/issues";
|
||||
import { ISSUE_DELETED } from "constants/event-tracker";
|
||||
import { EIssueFilterType, TCreateModalStoreTypes } from "constants/issue";
|
||||
import { EIssueFilterType, EIssuesStoreType } from "constants/issue";
|
||||
import { EUserProjectRoles } from "constants/project";
|
||||
import { useEventTracker, useIssues, useUser } from "hooks/store";
|
||||
import { useIssuesActions } from "hooks/use-issues-actions";
|
||||
// ui
|
||||
// types
|
||||
import { ICycleIssues, ICycleIssuesFilter } from "store/issue/cycle";
|
||||
import { IDraftIssues, IDraftIssuesFilter } from "store/issue/draft";
|
||||
import { IModuleIssues, IModuleIssuesFilter } from "store/issue/module";
|
||||
import { IProfileIssues, IProfileIssuesFilter } from "store/issue/profile";
|
||||
import { IProjectIssues, IProjectIssuesFilter } from "store/issue/project";
|
||||
import { IProjectViewIssues, IProjectViewIssuesFilter } from "store/issue/project-views";
|
||||
import { TIssue } from "@plane/types";
|
||||
import { IQuickActionProps } from "../list/list-view-types";
|
||||
import { EIssueActions } from "../types";
|
||||
//components
|
||||
import { KanBan } from "./default";
|
||||
import { KanBanSwimLanes } from "./swimlanes";
|
||||
import { handleDragDrop } from "./utils";
|
||||
|
||||
export type KanbanStoreType =
|
||||
| EIssuesStoreType.PROJECT
|
||||
| EIssuesStoreType.MODULE
|
||||
| EIssuesStoreType.CYCLE
|
||||
| EIssuesStoreType.PROJECT_VIEW
|
||||
| EIssuesStoreType.DRAFT
|
||||
| EIssuesStoreType.PROFILE;
|
||||
export interface IBaseKanBanLayout {
|
||||
issues: IProjectIssues | ICycleIssues | IDraftIssues | IModuleIssues | IProjectViewIssues | IProfileIssues;
|
||||
issuesFilter:
|
||||
| IProjectIssuesFilter
|
||||
| IModuleIssuesFilter
|
||||
| ICycleIssuesFilter
|
||||
| IDraftIssuesFilter
|
||||
| IProjectViewIssuesFilter
|
||||
| IProfileIssuesFilter;
|
||||
QuickActions: FC<IQuickActionProps>;
|
||||
issueActions: {
|
||||
[EIssueActions.DELETE]: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.UPDATE]?: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.REMOVE]?: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.ARCHIVE]?: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.RESTORE]?: (issue: TIssue) => Promise<void>;
|
||||
};
|
||||
showLoader?: boolean;
|
||||
viewId?: string;
|
||||
storeType?: TCreateModalStoreTypes;
|
||||
storeType: KanbanStoreType;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<any>;
|
||||
canEditPropertiesBasedOnProject?: (projectId: string) => boolean;
|
||||
isCompletedCycle?: boolean;
|
||||
@ -58,10 +44,7 @@ type KanbanDragState = {
|
||||
|
||||
export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBaseKanBanLayout) => {
|
||||
const {
|
||||
issues,
|
||||
issuesFilter,
|
||||
QuickActions,
|
||||
issueActions,
|
||||
showLoader,
|
||||
viewId,
|
||||
storeType,
|
||||
@ -77,7 +60,9 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { captureIssueEvent } = useEventTracker();
|
||||
const { issueMap } = useIssues();
|
||||
const { issueMap, issuesFilter, issues } = useIssues(storeType);
|
||||
const { updateIssue, removeIssue, removeIssueFromView, archiveIssue, restoreIssue, updateFilters } =
|
||||
useIssuesActions(storeType);
|
||||
|
||||
const issueIds = issues?.groupedIssueIds || [];
|
||||
|
||||
@ -148,12 +133,12 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
||||
result.destination,
|
||||
workspaceSlug?.toString(),
|
||||
projectId?.toString(),
|
||||
issues,
|
||||
sub_group_by,
|
||||
group_by,
|
||||
issueMap,
|
||||
issueIds,
|
||||
viewId
|
||||
updateIssue,
|
||||
removeIssue
|
||||
).catch((err) => {
|
||||
setToast({
|
||||
title: "Error",
|
||||
@ -165,55 +150,39 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
||||
}
|
||||
};
|
||||
|
||||
const handleIssues = useCallback(
|
||||
async (issue: TIssue, action: EIssueActions) => {
|
||||
if (issueActions[action]) {
|
||||
await issueActions[action]!(issue);
|
||||
}
|
||||
},
|
||||
[issueActions]
|
||||
);
|
||||
|
||||
const renderQuickActions = useCallback(
|
||||
(issue: TIssue, customActionButton?: React.ReactElement) => (
|
||||
<QuickActions
|
||||
customActionButton={customActionButton}
|
||||
issue={issue}
|
||||
handleDelete={async () => handleIssues(issue, EIssueActions.DELETE)}
|
||||
handleUpdate={
|
||||
issueActions[EIssueActions.UPDATE] ? async (data) => handleIssues(data, EIssueActions.UPDATE) : undefined
|
||||
}
|
||||
handleRemoveFromView={
|
||||
issueActions[EIssueActions.REMOVE] ? async () => handleIssues(issue, EIssueActions.REMOVE) : undefined
|
||||
}
|
||||
handleArchive={
|
||||
issueActions[EIssueActions.ARCHIVE] ? async () => handleIssues(issue, EIssueActions.ARCHIVE) : undefined
|
||||
}
|
||||
handleRestore={
|
||||
issueActions[EIssueActions.RESTORE] ? async () => handleIssues(issue, EIssueActions.RESTORE) : undefined
|
||||
}
|
||||
handleDelete={async () => removeIssue(issue.project_id, issue.id)}
|
||||
handleUpdate={async (data) => updateIssue && updateIssue(issue.project_id, issue.id, data)}
|
||||
handleRemoveFromView={async () => removeIssueFromView && removeIssueFromView(issue.project_id, issue.id)}
|
||||
handleArchive={async () => archiveIssue && archiveIssue(issue.project_id, issue.id)}
|
||||
handleRestore={async () => restoreIssue && restoreIssue(issue.project_id, issue.id)}
|
||||
readOnly={!isEditingAllowed || isCompletedCycle}
|
||||
/>
|
||||
),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[issueActions, handleIssues]
|
||||
[isEditingAllowed, isCompletedCycle, removeIssue, updateIssue, removeIssueFromView, archiveIssue, restoreIssue]
|
||||
);
|
||||
|
||||
const handleDeleteIssue = async () => {
|
||||
if (!handleDragDrop) return;
|
||||
if (!handleDragDrop || !dragState.draggedIssueId) return;
|
||||
await handleDragDrop(
|
||||
dragState.source,
|
||||
dragState.destination,
|
||||
workspaceSlug?.toString(),
|
||||
projectId?.toString(),
|
||||
issues,
|
||||
sub_group_by,
|
||||
group_by,
|
||||
issueMap,
|
||||
issueIds,
|
||||
viewId
|
||||
updateIssue,
|
||||
removeIssue
|
||||
).finally(() => {
|
||||
handleIssues(issueMap[dragState.draggedIssueId!], EIssueActions.DELETE);
|
||||
const draggedIssue = issueMap[dragState.draggedIssueId!];
|
||||
removeIssue(draggedIssue.project_id, draggedIssue.id);
|
||||
setDeleteIssueModal(false);
|
||||
setDragState({});
|
||||
captureIssueEvent({
|
||||
@ -229,14 +198,12 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
||||
let kanbanFilters = issuesFilter?.issueFilters?.kanbanFilters?.[toggle] || [];
|
||||
if (kanbanFilters.includes(value)) kanbanFilters = kanbanFilters.filter((_value) => _value != value);
|
||||
else kanbanFilters.push(value);
|
||||
issuesFilter.updateFilters(
|
||||
workspaceSlug.toString(),
|
||||
updateFilters(
|
||||
projectId.toString(),
|
||||
EIssueFilterType.KANBAN_FILTERS,
|
||||
{
|
||||
[toggle]: kanbanFilters,
|
||||
},
|
||||
viewId
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -294,7 +261,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
||||
displayProperties={displayProperties}
|
||||
sub_group_by={sub_group_by}
|
||||
group_by={group_by}
|
||||
handleIssues={handleIssues}
|
||||
updateIssue={updateIssue}
|
||||
quickActions={renderQuickActions}
|
||||
handleKanbanFilters={handleKanbanFilters}
|
||||
kanbanFilters={kanbanFilters}
|
||||
|
@ -12,7 +12,6 @@ import { IssueProperties } from "../properties/all-properties";
|
||||
import { WithDisplayPropertiesHOC } from "../properties/with-display-properties-HOC";
|
||||
// ui
|
||||
// types
|
||||
import { EIssueActions } from "../types";
|
||||
// helper
|
||||
|
||||
interface IssueBlockProps {
|
||||
@ -23,7 +22,7 @@ interface IssueBlockProps {
|
||||
isDragDisabled: boolean;
|
||||
draggableId: string;
|
||||
index: number;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => void;
|
||||
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
||||
quickActions: (issue: TIssue) => React.ReactNode;
|
||||
canEditProperties: (projectId: string | undefined) => boolean;
|
||||
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
|
||||
@ -34,13 +33,13 @@ interface IssueBlockProps {
|
||||
interface IssueDetailsBlockProps {
|
||||
issue: TIssue;
|
||||
displayProperties: IIssueDisplayProperties | undefined;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => void;
|
||||
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
||||
quickActions: (issue: TIssue) => React.ReactNode;
|
||||
isReadOnly: boolean;
|
||||
}
|
||||
|
||||
const KanbanIssueDetailsBlock: React.FC<IssueDetailsBlockProps> = observer((props: IssueDetailsBlockProps) => {
|
||||
const { issue, handleIssues, quickActions, isReadOnly, displayProperties } = props;
|
||||
const { issue, updateIssue, quickActions, isReadOnly, displayProperties } = props;
|
||||
// hooks
|
||||
const { getProjectIdentifierById } = useProject();
|
||||
const {
|
||||
@ -48,10 +47,6 @@ const KanbanIssueDetailsBlock: React.FC<IssueDetailsBlockProps> = observer((prop
|
||||
} = useApplication();
|
||||
const { setPeekIssue } = useIssueDetail();
|
||||
|
||||
const updateIssue = async (issueToUpdate: TIssue) => {
|
||||
if (issueToUpdate) await handleIssues(issueToUpdate, EIssueActions.UPDATE);
|
||||
};
|
||||
|
||||
const handleIssuePeekOverview = (issue: TIssue) =>
|
||||
workspaceSlug &&
|
||||
issue &&
|
||||
@ -95,7 +90,7 @@ const KanbanIssueDetailsBlock: React.FC<IssueDetailsBlockProps> = observer((prop
|
||||
issue={issue}
|
||||
displayProperties={displayProperties}
|
||||
activeLayout="Kanban"
|
||||
handleIssues={updateIssue}
|
||||
updateIssue={updateIssue}
|
||||
isReadOnly={isReadOnly}
|
||||
/>
|
||||
</>
|
||||
@ -111,7 +106,7 @@ export const KanbanIssueBlock: React.FC<IssueBlockProps> = memo((props) => {
|
||||
isDragDisabled,
|
||||
draggableId,
|
||||
index,
|
||||
handleIssues,
|
||||
updateIssue,
|
||||
quickActions,
|
||||
canEditProperties,
|
||||
scrollableContainerRef,
|
||||
@ -159,7 +154,7 @@ export const KanbanIssueBlock: React.FC<IssueBlockProps> = memo((props) => {
|
||||
<KanbanIssueDetailsBlock
|
||||
issue={issue}
|
||||
displayProperties={displayProperties}
|
||||
handleIssues={handleIssues}
|
||||
updateIssue={updateIssue}
|
||||
quickActions={quickActions}
|
||||
isReadOnly={!canEditIssueProperties}
|
||||
/>
|
||||
|
@ -2,7 +2,6 @@ import { MutableRefObject, memo } from "react";
|
||||
//types
|
||||
import { KanbanIssueBlock } from "components/issues";
|
||||
import { TIssue, IIssueDisplayProperties, IIssueMap } from "@plane/types";
|
||||
import { EIssueActions } from "../types";
|
||||
// components
|
||||
|
||||
interface IssueBlocksListProps {
|
||||
@ -13,7 +12,7 @@ interface IssueBlocksListProps {
|
||||
issueIds: string[];
|
||||
displayProperties: IIssueDisplayProperties | undefined;
|
||||
isDragDisabled: boolean;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => void;
|
||||
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
||||
quickActions: (issue: TIssue, customActionButton?: React.ReactElement) => React.ReactNode;
|
||||
canEditProperties: (projectId: string | undefined) => boolean;
|
||||
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
|
||||
@ -29,7 +28,7 @@ const KanbanIssueBlocksListMemo: React.FC<IssueBlocksListProps> = (props) => {
|
||||
issueIds,
|
||||
displayProperties,
|
||||
isDragDisabled,
|
||||
handleIssues,
|
||||
updateIssue,
|
||||
quickActions,
|
||||
canEditProperties,
|
||||
scrollableContainerRef,
|
||||
@ -54,7 +53,7 @@ const KanbanIssueBlocksListMemo: React.FC<IssueBlocksListProps> = (props) => {
|
||||
issueId={issueId}
|
||||
issuesMap={issuesMap}
|
||||
displayProperties={displayProperties}
|
||||
handleIssues={handleIssues}
|
||||
updateIssue={updateIssue}
|
||||
quickActions={quickActions}
|
||||
draggableId={draggableId}
|
||||
index={index}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { MutableRefObject } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// constants
|
||||
import { TCreateModalStoreTypes } from "constants/issue";
|
||||
// hooks
|
||||
import {
|
||||
useCycle,
|
||||
@ -26,11 +25,11 @@ import {
|
||||
TIssueKanbanFilters,
|
||||
} from "@plane/types";
|
||||
// parent components
|
||||
import { EIssueActions } from "../types";
|
||||
import { getGroupByColumns } from "../utils";
|
||||
// components
|
||||
import { HeaderGroupByCard } from "./headers/group-by-card";
|
||||
import { KanbanGroup } from "./kanban-group";
|
||||
import { KanbanStoreType } from "./base-kanban-root";
|
||||
|
||||
export interface IGroupByKanBan {
|
||||
issuesMap: IIssueMap;
|
||||
@ -40,7 +39,7 @@ export interface IGroupByKanBan {
|
||||
group_by: string | null;
|
||||
sub_group_id: string;
|
||||
isDragDisabled: boolean;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => void;
|
||||
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
||||
quickActions: (issue: TIssue, customActionButton?: React.ReactElement) => React.ReactNode;
|
||||
kanbanFilters: TIssueKanbanFilters;
|
||||
handleKanbanFilters: any;
|
||||
@ -53,7 +52,7 @@ export interface IGroupByKanBan {
|
||||
) => Promise<TIssue | undefined>;
|
||||
viewId?: string;
|
||||
disableIssueCreation?: boolean;
|
||||
storeType?: TCreateModalStoreTypes;
|
||||
storeType: KanbanStoreType;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<TIssue>;
|
||||
canEditProperties: (projectId: string | undefined) => boolean;
|
||||
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
|
||||
@ -70,7 +69,7 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
||||
group_by,
|
||||
sub_group_id = "null",
|
||||
isDragDisabled,
|
||||
handleIssues,
|
||||
updateIssue,
|
||||
quickActions,
|
||||
kanbanFilters,
|
||||
handleKanbanFilters,
|
||||
@ -164,7 +163,7 @@ const GroupByKanBan: React.FC<IGroupByKanBan> = observer((props) => {
|
||||
group_by={group_by}
|
||||
sub_group_id={sub_group_id}
|
||||
isDragDisabled={isDragDisabled}
|
||||
handleIssues={handleIssues}
|
||||
updateIssue={updateIssue}
|
||||
quickActions={quickActions}
|
||||
enableQuickIssueCreate={enableQuickIssueCreate}
|
||||
quickAddCallback={quickAddCallback}
|
||||
@ -190,7 +189,7 @@ export interface IKanBan {
|
||||
sub_group_by: string | null;
|
||||
group_by: string | null;
|
||||
sub_group_id?: string;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => void;
|
||||
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
||||
quickActions: (issue: TIssue, customActionButton?: React.ReactElement) => React.ReactNode;
|
||||
kanbanFilters: TIssueKanbanFilters;
|
||||
handleKanbanFilters: (toggle: "group_by" | "sub_group_by", value: string) => void;
|
||||
@ -204,7 +203,7 @@ export interface IKanBan {
|
||||
) => Promise<TIssue | undefined>;
|
||||
viewId?: string;
|
||||
disableIssueCreation?: boolean;
|
||||
storeType?: TCreateModalStoreTypes;
|
||||
storeType: KanbanStoreType;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<TIssue>;
|
||||
canEditProperties: (projectId: string | undefined) => boolean;
|
||||
scrollableContainerRef?: MutableRefObject<HTMLDivElement | null>;
|
||||
@ -219,7 +218,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
||||
sub_group_by,
|
||||
group_by,
|
||||
sub_group_id = "null",
|
||||
handleIssues,
|
||||
updateIssue,
|
||||
quickActions,
|
||||
kanbanFilters,
|
||||
handleKanbanFilters,
|
||||
@ -246,7 +245,7 @@ export const KanBan: React.FC<IKanBan> = observer((props) => {
|
||||
sub_group_by={sub_group_by}
|
||||
sub_group_id={sub_group_id}
|
||||
isDragDisabled={!issueKanBanView?.getCanUserDragDrop(group_by, sub_group_by)}
|
||||
handleIssues={handleIssues}
|
||||
updateIssue={updateIssue}
|
||||
quickActions={quickActions}
|
||||
kanbanFilters={kanbanFilters}
|
||||
handleKanbanFilters={handleKanbanFilters}
|
||||
|
@ -9,11 +9,11 @@ import { CustomMenu, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
import { ExistingIssuesListModal } from "components/core";
|
||||
import { CreateUpdateIssueModal } from "components/issues";
|
||||
// constants
|
||||
import { TCreateModalStoreTypes } from "constants/issue";
|
||||
// hooks
|
||||
import { useEventTracker } from "hooks/store";
|
||||
// types
|
||||
import { TIssue, ISearchIssueResponse, TIssueKanbanFilters } from "@plane/types";
|
||||
import { KanbanStoreType } from "../base-kanban-root";
|
||||
|
||||
interface IHeaderGroupByCard {
|
||||
sub_group_by: string | null;
|
||||
@ -26,7 +26,7 @@ interface IHeaderGroupByCard {
|
||||
handleKanbanFilters: any;
|
||||
issuePayload: Partial<TIssue>;
|
||||
disableIssueCreation?: boolean;
|
||||
storeType?: TCreateModalStoreTypes;
|
||||
storeType: KanbanStoreType;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<TIssue>;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@ import {
|
||||
TSubGroupedIssues,
|
||||
TUnGroupedIssues,
|
||||
} from "@plane/types";
|
||||
import { EIssueActions } from "../types";
|
||||
import { KanbanIssueBlocksList, KanBanQuickAddIssueForm } from ".";
|
||||
|
||||
interface IKanbanGroup {
|
||||
@ -25,7 +24,7 @@ interface IKanbanGroup {
|
||||
group_by: string | null;
|
||||
sub_group_id: string;
|
||||
isDragDisabled: boolean;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => void;
|
||||
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
||||
quickActions: (issue: TIssue, customActionButton?: React.ReactElement) => React.ReactNode;
|
||||
enableQuickIssueCreate?: boolean;
|
||||
quickAddCallback?: (
|
||||
@ -53,7 +52,7 @@ export const KanbanGroup = (props: IKanbanGroup) => {
|
||||
issueIds,
|
||||
peekIssueId,
|
||||
isDragDisabled,
|
||||
handleIssues,
|
||||
updateIssue,
|
||||
quickActions,
|
||||
canEditProperties,
|
||||
enableQuickIssueCreate,
|
||||
@ -135,7 +134,7 @@ export const KanbanGroup = (props: IKanbanGroup) => {
|
||||
issueIds={(issueIds as TGroupedIssues)?.[groupId] || []}
|
||||
displayProperties={displayProperties}
|
||||
isDragDisabled={isDragDisabled}
|
||||
handleIssues={handleIssues}
|
||||
updateIssue={updateIssue}
|
||||
quickActions={quickActions}
|
||||
canEditProperties={canEditProperties}
|
||||
scrollableContainerRef={scrollableContainerRef}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useMemo } from "react";
|
||||
import React, { useCallback } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// hooks
|
||||
@ -7,8 +7,6 @@ import { EIssuesStoreType } from "constants/issue";
|
||||
import { useCycle, useIssues } from "hooks/store";
|
||||
// ui
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../../types";
|
||||
// components
|
||||
import { BaseKanBanRoot } from "../base-kanban-root";
|
||||
|
||||
@ -19,35 +17,9 @@ export const CycleKanBanLayout: React.FC = observer(() => {
|
||||
const { workspaceSlug, projectId, cycleId } = router.query;
|
||||
|
||||
// store
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.CYCLE);
|
||||
const { issues } = useIssues(EIssuesStoreType.CYCLE);
|
||||
const { currentProjectCompletedCycleIds } = useCycle();
|
||||
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
|
||||
await issues.updateIssue(workspaceSlug.toString(), issue.project_id, issue.id, issue, cycleId.toString());
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
|
||||
await issues.removeIssue(workspaceSlug.toString(), issue.project_id, issue.id, cycleId.toString());
|
||||
},
|
||||
[EIssueActions.REMOVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
|
||||
await issues.removeIssueFromCycle(workspaceSlug.toString(), issue.project_id, cycleId.toString(), issue.id);
|
||||
},
|
||||
[EIssueActions.ARCHIVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
|
||||
await issues.archiveIssue(workspaceSlug.toString(), issue.project_id, issue.id, cycleId.toString());
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug, cycleId]
|
||||
);
|
||||
|
||||
const isCompletedCycle =
|
||||
cycleId && currentProjectCompletedCycleIds ? currentProjectCompletedCycleIds.includes(cycleId.toString()) : false;
|
||||
|
||||
@ -63,9 +35,6 @@ export const CycleKanBanLayout: React.FC = observer(() => {
|
||||
|
||||
return (
|
||||
<BaseKanBanRoot
|
||||
issueActions={issueActions}
|
||||
issues={issues}
|
||||
issuesFilter={issuesFilter}
|
||||
showLoader
|
||||
QuickActions={CycleIssueQuickActions}
|
||||
viewId={cycleId?.toString() ?? ""}
|
||||
|
@ -1,49 +1,11 @@
|
||||
import { useMemo } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// hooks
|
||||
// components
|
||||
import { ProjectIssueQuickActions } from "components/issues";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { useIssues } from "hooks/store";
|
||||
// components
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
// constants
|
||||
import { EIssueActions } from "../../types";
|
||||
import { BaseKanBanRoot } from "../base-kanban-root";
|
||||
|
||||
export interface IKanBanLayout {}
|
||||
|
||||
export const DraftKanBanLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
// store
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.DRAFT);
|
||||
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
await issues.updateIssue(workspaceSlug.toString(), issue.project_id, issue.id, issue);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
await issues.removeIssue(workspaceSlug.toString(), issue.project_id, issue.id);
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug]
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseKanBanRoot
|
||||
issueActions={issueActions}
|
||||
issuesFilter={issuesFilter}
|
||||
issues={issues}
|
||||
showLoader
|
||||
QuickActions={ProjectIssueQuickActions}
|
||||
/>
|
||||
);
|
||||
});
|
||||
export const DraftKanBanLayout: React.FC = observer(() => (
|
||||
<BaseKanBanRoot showLoader QuickActions={ProjectIssueQuickActions} storeType={EIssuesStoreType.DRAFT} />
|
||||
));
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useMemo } from "react";
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// hook
|
||||
@ -7,9 +7,7 @@ import { EIssuesStoreType } from "constants/issue";
|
||||
import { useIssues } from "hooks/store";
|
||||
// components
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
// constants
|
||||
import { EIssueActions } from "../../types";
|
||||
import { BaseKanBanRoot } from "../base-kanban-root";
|
||||
|
||||
export interface IModuleKanBanLayout {}
|
||||
@ -19,39 +17,10 @@ export const ModuleKanBanLayout: React.FC = observer(() => {
|
||||
const { workspaceSlug, projectId, moduleId } = router.query;
|
||||
|
||||
// store
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.MODULE);
|
||||
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
|
||||
await issues.updateIssue(workspaceSlug.toString(), issue.project_id, issue.id, issue, moduleId.toString());
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
|
||||
await issues.removeIssue(workspaceSlug.toString(), issue.project_id, issue.id, moduleId.toString());
|
||||
},
|
||||
[EIssueActions.REMOVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
|
||||
await issues.removeIssueFromModule(workspaceSlug.toString(), issue.project_id, moduleId.toString(), issue.id);
|
||||
},
|
||||
[EIssueActions.ARCHIVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
|
||||
await issues.archiveIssue(workspaceSlug.toString(), issue.project_id, issue.id, moduleId.toString());
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug, moduleId]
|
||||
);
|
||||
const { issues } = useIssues(EIssuesStoreType.MODULE);
|
||||
|
||||
return (
|
||||
<BaseKanBanRoot
|
||||
issueActions={issueActions}
|
||||
issues={issues}
|
||||
issuesFilter={issuesFilter}
|
||||
showLoader
|
||||
QuickActions={ModuleIssueQuickActions}
|
||||
viewId={moduleId?.toString()}
|
||||
|
@ -1,48 +1,18 @@
|
||||
import { useMemo } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// hooks
|
||||
import { ProjectIssueQuickActions } from "components/issues";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { EUserProjectRoles } from "constants/project";
|
||||
import { useIssues, useUser } from "hooks/store";
|
||||
import { useUser } from "hooks/store";
|
||||
// components
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
// constants
|
||||
import { EIssueActions } from "../../types";
|
||||
import { BaseKanBanRoot } from "../base-kanban-root";
|
||||
|
||||
export const ProfileIssuesKanBanLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, userId } = router.query as { workspaceSlug: string; userId: string };
|
||||
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROFILE);
|
||||
|
||||
const {
|
||||
membership: { currentWorkspaceAllProjectsRole },
|
||||
} = useUser();
|
||||
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !userId) return;
|
||||
|
||||
await issues.updateIssue(workspaceSlug, issue.project_id, issue.id, issue, userId);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !userId) return;
|
||||
|
||||
await issues.removeIssue(workspaceSlug, issue.project_id, issue.id, userId);
|
||||
},
|
||||
[EIssueActions.ARCHIVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !userId) return;
|
||||
|
||||
await issues.archiveIssue(workspaceSlug, issue.project_id, issue.id, userId);
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug, userId]
|
||||
);
|
||||
membership: { currentWorkspaceAllProjectsRole },
|
||||
} = useUser();
|
||||
|
||||
const canEditPropertiesBasedOnProject = (projectId: string) => {
|
||||
const currentProjectRole = currentWorkspaceAllProjectsRole && currentWorkspaceAllProjectsRole[projectId];
|
||||
@ -52,9 +22,6 @@ export const ProfileIssuesKanBanLayout: React.FC = observer(() => {
|
||||
|
||||
return (
|
||||
<BaseKanBanRoot
|
||||
issueActions={issueActions}
|
||||
issuesFilter={issuesFilter}
|
||||
issues={issues}
|
||||
showLoader
|
||||
QuickActions={ProjectIssueQuickActions}
|
||||
storeType={EIssuesStoreType.PROFILE}
|
||||
|
@ -1,54 +1,12 @@
|
||||
import { useMemo } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// mobx store
|
||||
import { ProjectIssueQuickActions } from "components/issues";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { useIssues } from "hooks/store/use-issues";
|
||||
// components
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
// constants
|
||||
import { EIssueActions } from "../../types";
|
||||
import { BaseKanBanRoot } from "../base-kanban-root";
|
||||
|
||||
export interface IKanBanLayout {}
|
||||
|
||||
export const KanBanLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query as { workspaceSlug: string; projectId: string };
|
||||
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT);
|
||||
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
await issues.updateIssue(workspaceSlug, issue.project_id, issue.id, issue);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
await issues.removeIssue(workspaceSlug, issue.project_id, issue.id);
|
||||
},
|
||||
[EIssueActions.ARCHIVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
await issues.archiveIssue(workspaceSlug, issue.project_id, issue.id);
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug]
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseKanBanRoot
|
||||
issueActions={issueActions}
|
||||
issues={issues}
|
||||
issuesFilter={issuesFilter}
|
||||
showLoader
|
||||
QuickActions={ProjectIssueQuickActions}
|
||||
storeType={EIssuesStoreType.PROJECT}
|
||||
/>
|
||||
);
|
||||
});
|
||||
export const KanBanLayout: React.FC = observer(() => (
|
||||
<BaseKanBanRoot showLoader QuickActions={ProjectIssueQuickActions} storeType={EIssuesStoreType.PROJECT} />
|
||||
));
|
||||
|
@ -3,37 +3,19 @@ import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// hooks
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { useIssues } from "hooks/store";
|
||||
// constant
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
import { ProjectIssueQuickActions } from "../../quick-action-dropdowns";
|
||||
import { EIssueActions } from "../../types";
|
||||
// components
|
||||
import { BaseKanBanRoot } from "../base-kanban-root";
|
||||
|
||||
export interface IViewKanBanLayout {
|
||||
issueActions: {
|
||||
[EIssueActions.DELETE]: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.UPDATE]?: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.REMOVE]?: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.ARCHIVE]?: (issue: TIssue) => Promise<void>;
|
||||
};
|
||||
}
|
||||
|
||||
export const ProjectViewKanBanLayout: React.FC<IViewKanBanLayout> = observer((props) => {
|
||||
const { issueActions } = props;
|
||||
export const ProjectViewKanBanLayout: React.FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { viewId } = router.query;
|
||||
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT_VIEW);
|
||||
|
||||
return (
|
||||
<BaseKanBanRoot
|
||||
issueActions={issueActions}
|
||||
issuesFilter={issuesFilter}
|
||||
issues={issues}
|
||||
showLoader
|
||||
QuickActions={ProjectIssueQuickActions}
|
||||
storeType={EIssuesStoreType.PROJECT_VIEW}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { MutableRefObject } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// components
|
||||
import { TCreateModalStoreTypes } from "constants/issue";
|
||||
import { useCycle, useLabel, useMember, useModule, useProject, useProjectState } from "hooks/store";
|
||||
import {
|
||||
GroupByColumnTypes,
|
||||
@ -14,11 +13,11 @@ import {
|
||||
TUnGroupedIssues,
|
||||
TIssueKanbanFilters,
|
||||
} from "@plane/types";
|
||||
import { EIssueActions } from "../types";
|
||||
import { getGroupByColumns } from "../utils";
|
||||
import { KanBan } from "./default";
|
||||
import { HeaderGroupByCard } from "./headers/group-by-card";
|
||||
import { HeaderSubGroupByCard } from "./headers/sub-group-by-card";
|
||||
import { KanbanStoreType } from "./base-kanban-root";
|
||||
// types
|
||||
// constants
|
||||
|
||||
@ -29,6 +28,7 @@ interface ISubGroupSwimlaneHeader {
|
||||
list: IGroupByColumn[];
|
||||
kanbanFilters: TIssueKanbanFilters;
|
||||
handleKanbanFilters: (toggle: "group_by" | "sub_group_by", value: string) => void;
|
||||
storeType: KanbanStoreType;
|
||||
}
|
||||
|
||||
const getSubGroupHeaderIssuesCount = (issueIds: TSubGroupedIssues, groupById: string) => {
|
||||
@ -43,6 +43,7 @@ const SubGroupSwimlaneHeader: React.FC<ISubGroupSwimlaneHeader> = ({
|
||||
issueIds,
|
||||
sub_group_by,
|
||||
group_by,
|
||||
storeType,
|
||||
list,
|
||||
kanbanFilters,
|
||||
handleKanbanFilters,
|
||||
@ -62,6 +63,7 @@ const SubGroupSwimlaneHeader: React.FC<ISubGroupSwimlaneHeader> = ({
|
||||
kanbanFilters={kanbanFilters}
|
||||
handleKanbanFilters={handleKanbanFilters}
|
||||
issuePayload={_list.payload}
|
||||
storeType={storeType}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
@ -73,13 +75,13 @@ interface ISubGroupSwimlane extends ISubGroupSwimlaneHeader {
|
||||
issueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues;
|
||||
showEmptyGroup: boolean;
|
||||
displayProperties: IIssueDisplayProperties | undefined;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => void;
|
||||
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
||||
quickActions: (issue: TIssue, customActionButton?: React.ReactElement) => React.ReactNode;
|
||||
kanbanFilters: TIssueKanbanFilters;
|
||||
handleKanbanFilters: (toggle: "group_by" | "sub_group_by", value: string) => void;
|
||||
isDragStarted?: boolean;
|
||||
disableIssueCreation?: boolean;
|
||||
storeType?: TCreateModalStoreTypes;
|
||||
storeType: KanbanStoreType;
|
||||
enableQuickIssueCreate: boolean;
|
||||
canEditProperties: (projectId: string | undefined) => boolean;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<TIssue>;
|
||||
@ -99,7 +101,8 @@ const SubGroupSwimlane: React.FC<ISubGroupSwimlane> = observer((props) => {
|
||||
sub_group_by,
|
||||
group_by,
|
||||
list,
|
||||
handleIssues,
|
||||
storeType,
|
||||
updateIssue,
|
||||
quickActions,
|
||||
displayProperties,
|
||||
kanbanFilters,
|
||||
@ -153,7 +156,7 @@ const SubGroupSwimlane: React.FC<ISubGroupSwimlane> = observer((props) => {
|
||||
sub_group_by={sub_group_by}
|
||||
group_by={group_by}
|
||||
sub_group_id={_list.id}
|
||||
handleIssues={handleIssues}
|
||||
updateIssue={updateIssue}
|
||||
quickActions={quickActions}
|
||||
kanbanFilters={kanbanFilters}
|
||||
handleKanbanFilters={handleKanbanFilters}
|
||||
@ -165,6 +168,7 @@ const SubGroupSwimlane: React.FC<ISubGroupSwimlane> = observer((props) => {
|
||||
viewId={viewId}
|
||||
scrollableContainerRef={scrollableContainerRef}
|
||||
isDragStarted={isDragStarted}
|
||||
storeType={storeType}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@ -180,14 +184,14 @@ export interface IKanBanSwimLanes {
|
||||
displayProperties: IIssueDisplayProperties | undefined;
|
||||
sub_group_by: string | null;
|
||||
group_by: string | null;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => void;
|
||||
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
||||
quickActions: (issue: TIssue, customActionButton?: React.ReactElement) => React.ReactNode;
|
||||
kanbanFilters: TIssueKanbanFilters;
|
||||
handleKanbanFilters: (toggle: "group_by" | "sub_group_by", value: string) => void;
|
||||
showEmptyGroup: boolean;
|
||||
isDragStarted?: boolean;
|
||||
disableIssueCreation?: boolean;
|
||||
storeType?: TCreateModalStoreTypes;
|
||||
storeType: KanbanStoreType;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<TIssue>;
|
||||
enableQuickIssueCreate: boolean;
|
||||
quickAddCallback?: (
|
||||
@ -208,7 +212,8 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
||||
displayProperties,
|
||||
sub_group_by,
|
||||
group_by,
|
||||
handleIssues,
|
||||
updateIssue,
|
||||
storeType,
|
||||
quickActions,
|
||||
kanbanFilters,
|
||||
handleKanbanFilters,
|
||||
@ -261,6 +266,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
||||
kanbanFilters={kanbanFilters}
|
||||
handleKanbanFilters={handleKanbanFilters}
|
||||
list={groupByList}
|
||||
storeType={storeType}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -272,7 +278,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
||||
displayProperties={displayProperties}
|
||||
group_by={group_by}
|
||||
sub_group_by={sub_group_by}
|
||||
handleIssues={handleIssues}
|
||||
updateIssue={updateIssue}
|
||||
quickActions={quickActions}
|
||||
kanbanFilters={kanbanFilters}
|
||||
handleKanbanFilters={handleKanbanFilters}
|
||||
@ -285,6 +291,7 @@ export const KanBanSwimLanes: React.FC<IKanBanSwimLanes> = observer((props) => {
|
||||
quickAddCallback={quickAddCallback}
|
||||
viewId={viewId}
|
||||
scrollableContainerRef={scrollableContainerRef}
|
||||
storeType={storeType}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -1,12 +1,5 @@
|
||||
import { DraggableLocation } from "@hello-pangea/dnd";
|
||||
import { ICycleIssues } from "store/issue/cycle";
|
||||
import { IDraftIssues } from "store/issue/draft";
|
||||
import { IModuleIssues } from "store/issue/module";
|
||||
import { IProfileIssues } from "store/issue/profile";
|
||||
import { IProjectIssues } from "store/issue/project";
|
||||
import { IProjectViewIssues } from "store/issue/project-views";
|
||||
import { IWorkspaceIssues } from "store/issue/workspace";
|
||||
import { TGroupedIssues, IIssueMap, TSubGroupedIssues, TUnGroupedIssues } from "@plane/types";
|
||||
import { TGroupedIssues, IIssueMap, TSubGroupedIssues, TUnGroupedIssues, TIssue } from "@plane/types";
|
||||
|
||||
const handleSortOrder = (destinationIssues: string[], destinationIndex: number, issueMap: IIssueMap) => {
|
||||
const sortOrderDefaultValue = 65535;
|
||||
@ -48,24 +41,16 @@ export const handleDragDrop = async (
|
||||
destination: DraggableLocation | null | undefined,
|
||||
workspaceSlug: string | undefined,
|
||||
projectId: string | undefined, // projectId for all views or user id in profile issues
|
||||
store:
|
||||
| IProjectIssues
|
||||
| ICycleIssues
|
||||
| IDraftIssues
|
||||
| IModuleIssues
|
||||
| IDraftIssues
|
||||
| IProjectViewIssues
|
||||
| IProfileIssues
|
||||
| IWorkspaceIssues,
|
||||
subGroupBy: string | null,
|
||||
groupBy: string | null,
|
||||
issueMap: IIssueMap,
|
||||
issueWithIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined,
|
||||
viewId: string | null = null // it can be moduleId, cycleId
|
||||
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined,
|
||||
removeIssue: (projectId: string, issueId: string) => Promise<void> | undefined
|
||||
) => {
|
||||
if (!issueMap || !issueWithIds || !source || !destination || !workspaceSlug || !projectId) return;
|
||||
|
||||
let updateIssue: any = {};
|
||||
let updatedIssue: any = {};
|
||||
|
||||
const sourceDroppableId = source?.droppableId;
|
||||
const destinationDroppableId = destination?.droppableId;
|
||||
@ -100,8 +85,7 @@ export const handleDragDrop = async (
|
||||
const [removed] = sourceIssues.splice(source.index, 1);
|
||||
|
||||
if (removed) {
|
||||
if (viewId) return await store?.removeIssue(workspaceSlug, projectId, removed); //, viewId);
|
||||
else return await store?.removeIssue(workspaceSlug, projectId, removed);
|
||||
return await removeIssue(projectId, removed);
|
||||
}
|
||||
} else {
|
||||
//spreading the array to stop changing the original reference
|
||||
@ -118,14 +102,14 @@ export const handleDragDrop = async (
|
||||
const [removed] = sourceIssues.splice(source.index, 1);
|
||||
const removedIssueDetail = issueMap[removed];
|
||||
|
||||
updateIssue = {
|
||||
updatedIssue = {
|
||||
id: removedIssueDetail?.id,
|
||||
project_id: removedIssueDetail?.project_id,
|
||||
};
|
||||
|
||||
// for both horizontal and vertical dnd
|
||||
updateIssue = {
|
||||
...updateIssue,
|
||||
updatedIssue = {
|
||||
...updatedIssue,
|
||||
...handleSortOrder(
|
||||
sourceDroppableId === destinationDroppableId ? sourceIssues : destinationIssues,
|
||||
destination.index,
|
||||
@ -136,19 +120,19 @@ export const handleDragDrop = async (
|
||||
if (subGroupBy && sourceSubGroupByColumnId && destinationSubGroupByColumnId) {
|
||||
if (sourceSubGroupByColumnId === destinationSubGroupByColumnId) {
|
||||
if (sourceGroupByColumnId != destinationGroupByColumnId) {
|
||||
if (groupBy === "state") updateIssue = { ...updateIssue, state_id: destinationGroupByColumnId };
|
||||
if (groupBy === "priority") updateIssue = { ...updateIssue, priority: destinationGroupByColumnId };
|
||||
if (groupBy === "state") updatedIssue = { ...updatedIssue, state_id: destinationGroupByColumnId };
|
||||
if (groupBy === "priority") updatedIssue = { ...updatedIssue, priority: destinationGroupByColumnId };
|
||||
}
|
||||
} else {
|
||||
if (subGroupBy === "state")
|
||||
updateIssue = {
|
||||
...updateIssue,
|
||||
updatedIssue = {
|
||||
...updatedIssue,
|
||||
state_id: destinationSubGroupByColumnId,
|
||||
priority: destinationGroupByColumnId,
|
||||
};
|
||||
if (subGroupBy === "priority")
|
||||
updateIssue = {
|
||||
...updateIssue,
|
||||
updatedIssue = {
|
||||
...updatedIssue,
|
||||
state_id: destinationGroupByColumnId,
|
||||
priority: destinationSubGroupByColumnId,
|
||||
};
|
||||
@ -156,15 +140,13 @@ export const handleDragDrop = async (
|
||||
} else {
|
||||
// for horizontal dnd
|
||||
if (sourceColumnId != destinationColumnId) {
|
||||
if (groupBy === "state") updateIssue = { ...updateIssue, state_id: destinationGroupByColumnId };
|
||||
if (groupBy === "priority") updateIssue = { ...updateIssue, priority: destinationGroupByColumnId };
|
||||
if (groupBy === "state") updatedIssue = { ...updatedIssue, state_id: destinationGroupByColumnId };
|
||||
if (groupBy === "priority") updatedIssue = { ...updatedIssue, priority: destinationGroupByColumnId };
|
||||
}
|
||||
}
|
||||
|
||||
if (updateIssue && updateIssue?.id) {
|
||||
if (viewId)
|
||||
return await store?.updateIssue(workspaceSlug, updateIssue.project_id, updateIssue.id, updateIssue, viewId);
|
||||
else return await store?.updateIssue(workspaceSlug, updateIssue.project_id, updateIssue.id, updateIssue);
|
||||
if (updatedIssue && updatedIssue?.id) {
|
||||
return updateIssue && (await updateIssue(updatedIssue.project_id, updatedIssue.id, updatedIssue));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,68 +1,46 @@
|
||||
import { FC, useCallback } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
// types
|
||||
import { TCreateModalStoreTypes } from "constants/issue";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { EUserProjectRoles } from "constants/project";
|
||||
import { useIssues, useUser } from "hooks/store";
|
||||
import { IArchivedIssuesFilter, IArchivedIssues } from "store/issue/archived";
|
||||
import { ICycleIssues, ICycleIssuesFilter } from "store/issue/cycle";
|
||||
import { IDraftIssuesFilter, IDraftIssues } from "store/issue/draft";
|
||||
import { IModuleIssues, IModuleIssuesFilter } from "store/issue/module";
|
||||
import { IProfileIssues, IProfileIssuesFilter } from "store/issue/profile";
|
||||
import { IProjectIssues, IProjectIssuesFilter } from "store/issue/project";
|
||||
import { IProjectViewIssues, IProjectViewIssuesFilter } from "store/issue/project-views";
|
||||
import { TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../types";
|
||||
|
||||
import { TIssue } from "@plane/types"
|
||||
// components
|
||||
import { List } from "./default";
|
||||
import { IQuickActionProps } from "./list-view-types";
|
||||
import { useIssuesActions } from "hooks/use-issues-actions";
|
||||
// constants
|
||||
// hooks
|
||||
|
||||
type ListStoreType =
|
||||
| EIssuesStoreType.PROJECT
|
||||
| EIssuesStoreType.MODULE
|
||||
| EIssuesStoreType.CYCLE
|
||||
| EIssuesStoreType.PROJECT_VIEW
|
||||
| EIssuesStoreType.ARCHIVED
|
||||
| EIssuesStoreType.DRAFT
|
||||
| EIssuesStoreType.PROFILE;
|
||||
interface IBaseListRoot {
|
||||
issuesFilter:
|
||||
| IProjectIssuesFilter
|
||||
| IModuleIssuesFilter
|
||||
| ICycleIssuesFilter
|
||||
| IProjectViewIssuesFilter
|
||||
| IProfileIssuesFilter
|
||||
| IDraftIssuesFilter
|
||||
| IArchivedIssuesFilter;
|
||||
issues:
|
||||
| IProjectIssues
|
||||
| ICycleIssues
|
||||
| IModuleIssues
|
||||
| IProjectViewIssues
|
||||
| IProfileIssues
|
||||
| IDraftIssues
|
||||
| IArchivedIssues;
|
||||
QuickActions: FC<IQuickActionProps>;
|
||||
issueActions: {
|
||||
[EIssueActions.DELETE]: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.UPDATE]?: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.REMOVE]?: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.ARCHIVE]?: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.RESTORE]?: (issue: TIssue) => Promise<void>;
|
||||
};
|
||||
viewId?: string;
|
||||
storeType: TCreateModalStoreTypes;
|
||||
storeType: ListStoreType;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<any>;
|
||||
canEditPropertiesBasedOnProject?: (projectId: string) => boolean;
|
||||
isCompletedCycle?: boolean;
|
||||
}
|
||||
|
||||
export const BaseListRoot = observer((props: IBaseListRoot) => {
|
||||
const {
|
||||
issuesFilter,
|
||||
issues,
|
||||
QuickActions,
|
||||
issueActions,
|
||||
viewId,
|
||||
storeType,
|
||||
addIssuesToView,
|
||||
canEditPropertiesBasedOnProject,
|
||||
isCompletedCycle = false,
|
||||
} = props;
|
||||
|
||||
const { issuesFilter, issues } = useIssues(storeType);
|
||||
const { updateIssue, removeIssue, removeIssueFromView, archiveIssue, restoreIssue } = useIssuesActions(storeType);
|
||||
// mobx store
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
@ -80,7 +58,7 @@ export const BaseListRoot = observer((props: IBaseListRoot) => {
|
||||
const isEditingAllowedBasedOnProject =
|
||||
canEditPropertiesBasedOnProject && projectId ? canEditPropertiesBasedOnProject(projectId) : isEditingAllowed;
|
||||
|
||||
return enableInlineEditing && isEditingAllowedBasedOnProject;
|
||||
return !!enableInlineEditing && isEditingAllowedBasedOnProject;
|
||||
},
|
||||
[canEditPropertiesBasedOnProject, enableInlineEditing, isEditingAllowed]
|
||||
);
|
||||
@ -91,37 +69,20 @@ export const BaseListRoot = observer((props: IBaseListRoot) => {
|
||||
const group_by = displayFilters?.group_by || null;
|
||||
const showEmptyGroup = displayFilters?.show_empty_groups ?? false;
|
||||
|
||||
const handleIssues = useCallback(
|
||||
async (issue: TIssue, action: EIssueActions) => {
|
||||
if (issueActions[action]) {
|
||||
await issueActions[action]!(issue);
|
||||
}
|
||||
},
|
||||
[issueActions]
|
||||
);
|
||||
|
||||
const renderQuickActions = useCallback(
|
||||
(issue: TIssue) => (
|
||||
<QuickActions
|
||||
issue={issue}
|
||||
handleDelete={async () => handleIssues(issue, EIssueActions.DELETE)}
|
||||
handleUpdate={
|
||||
issueActions[EIssueActions.UPDATE] ? async (data) => handleIssues(data, EIssueActions.UPDATE) : undefined
|
||||
}
|
||||
handleRemoveFromView={
|
||||
issueActions[EIssueActions.REMOVE] ? async () => handleIssues(issue, EIssueActions.REMOVE) : undefined
|
||||
}
|
||||
handleArchive={
|
||||
issueActions[EIssueActions.ARCHIVE] ? async () => handleIssues(issue, EIssueActions.ARCHIVE) : undefined
|
||||
}
|
||||
handleRestore={
|
||||
issueActions[EIssueActions.RESTORE] ? async () => handleIssues(issue, EIssueActions.RESTORE) : undefined
|
||||
}
|
||||
handleDelete={async () => removeIssue(issue.project_id, issue.id)}
|
||||
handleUpdate={async (data) => updateIssue && updateIssue(issue.project_id, issue.id, data)}
|
||||
handleRemoveFromView={async () => removeIssueFromView && removeIssueFromView(issue.project_id, issue.id)}
|
||||
handleArchive={async () => archiveIssue && archiveIssue(issue.project_id, issue.id)}
|
||||
handleRestore={async () => restoreIssue && restoreIssue(issue.project_id, issue.id)}
|
||||
readOnly={!isEditingAllowed || isCompletedCycle}
|
||||
/>
|
||||
),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[handleIssues]
|
||||
[isEditingAllowed, isCompletedCycle, removeIssue, updateIssue, removeIssueFromView, archiveIssue, restoreIssue]
|
||||
);
|
||||
|
||||
return (
|
||||
@ -130,7 +91,7 @@ export const BaseListRoot = observer((props: IBaseListRoot) => {
|
||||
issuesMap={issueMap}
|
||||
displayProperties={displayProperties}
|
||||
group_by={group_by}
|
||||
handleIssues={handleIssues}
|
||||
updateIssue={updateIssue}
|
||||
quickActions={renderQuickActions}
|
||||
issueIds={issueIds}
|
||||
showEmptyGroup={showEmptyGroup}
|
||||
|
@ -9,19 +9,18 @@ import { useApplication, useIssueDetail, useProject } from "hooks/store";
|
||||
// types
|
||||
import { TIssue, IIssueDisplayProperties, TIssueMap } from "@plane/types";
|
||||
import { IssueProperties } from "../properties/all-properties";
|
||||
import { EIssueActions } from "../types";
|
||||
|
||||
interface IssueBlockProps {
|
||||
issueId: string;
|
||||
issuesMap: TIssueMap;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => Promise<void>;
|
||||
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
||||
quickActions: (issue: TIssue) => React.ReactNode;
|
||||
displayProperties: IIssueDisplayProperties | undefined;
|
||||
canEditProperties: (projectId: string | undefined) => boolean;
|
||||
}
|
||||
|
||||
export const IssueBlock: React.FC<IssueBlockProps> = observer((props: IssueBlockProps) => {
|
||||
const { issuesMap, issueId, handleIssues, quickActions, displayProperties, canEditProperties } = props;
|
||||
const { issuesMap, issueId, updateIssue, quickActions, displayProperties, canEditProperties } = props;
|
||||
// hooks
|
||||
const {
|
||||
router: { workspaceSlug },
|
||||
@ -29,10 +28,6 @@ export const IssueBlock: React.FC<IssueBlockProps> = observer((props: IssueBlock
|
||||
const { getProjectIdentifierById } = useProject();
|
||||
const { peekIssue, setPeekIssue } = useIssueDetail();
|
||||
|
||||
const updateIssue = async (issueToUpdate: TIssue) => {
|
||||
await handleIssues(issueToUpdate, EIssueActions.UPDATE);
|
||||
};
|
||||
|
||||
const handleIssuePeekOverview = (issue: TIssue) =>
|
||||
workspaceSlug &&
|
||||
issue &&
|
||||
@ -91,7 +86,7 @@ export const IssueBlock: React.FC<IssueBlockProps> = observer((props: IssueBlock
|
||||
className="relative flex items-center gap-2 whitespace-nowrap"
|
||||
issue={issue}
|
||||
isReadOnly={!canEditIssueProperties}
|
||||
handleIssues={updateIssue}
|
||||
updateIssue={updateIssue}
|
||||
displayProperties={displayProperties}
|
||||
activeLayout="List"
|
||||
/>
|
||||
|
@ -4,20 +4,19 @@ import RenderIfVisible from "components/core/render-if-visible-HOC";
|
||||
import { IssueBlock } from "components/issues";
|
||||
// types
|
||||
import { TGroupedIssues, TIssue, IIssueDisplayProperties, TIssueMap, TUnGroupedIssues } from "@plane/types";
|
||||
import { EIssueActions } from "../types";
|
||||
|
||||
interface Props {
|
||||
issueIds: TGroupedIssues | TUnGroupedIssues | any;
|
||||
issuesMap: TIssueMap;
|
||||
canEditProperties: (projectId: string | undefined) => boolean;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => Promise<void>;
|
||||
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
||||
quickActions: (issue: TIssue) => React.ReactNode;
|
||||
displayProperties: IIssueDisplayProperties | undefined;
|
||||
containerRef: MutableRefObject<HTMLDivElement | null>;
|
||||
}
|
||||
|
||||
export const IssueBlocksList: FC<Props> = (props) => {
|
||||
const { issueIds, issuesMap, handleIssues, quickActions, displayProperties, canEditProperties, containerRef } = props;
|
||||
const { issueIds, issuesMap, updateIssue, quickActions, displayProperties, canEditProperties, containerRef } = props;
|
||||
|
||||
return (
|
||||
<div className="relative h-full w-full">
|
||||
@ -35,7 +34,7 @@ export const IssueBlocksList: FC<Props> = (props) => {
|
||||
<IssueBlock
|
||||
issueId={issueId}
|
||||
issuesMap={issuesMap}
|
||||
handleIssues={handleIssues}
|
||||
updateIssue={updateIssue}
|
||||
quickActions={quickActions}
|
||||
canEditProperties={canEditProperties}
|
||||
displayProperties={displayProperties}
|
||||
|
@ -2,7 +2,6 @@ import { useRef } from "react";
|
||||
// components
|
||||
import { IssueBlocksList, ListQuickAddIssueForm } from "components/issues";
|
||||
// hooks
|
||||
import { TCreateModalStoreTypes } from "constants/issue";
|
||||
import { useCycle, useLabel, useMember, useModule, useProject, useProjectState } from "hooks/store";
|
||||
// constants
|
||||
// types
|
||||
@ -15,15 +14,15 @@ import {
|
||||
TUnGroupedIssues,
|
||||
IGroupByColumn,
|
||||
} from "@plane/types";
|
||||
import { EIssueActions } from "../types";
|
||||
import { getGroupByColumns } from "../utils";
|
||||
import { HeaderGroupByCard } from "./headers/group-by-card";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
|
||||
export interface IGroupByList {
|
||||
issueIds: TGroupedIssues | TUnGroupedIssues | any;
|
||||
issuesMap: TIssueMap;
|
||||
group_by: string | null;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => Promise<void>;
|
||||
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
||||
quickActions: (issue: TIssue) => React.ReactNode;
|
||||
displayProperties: IIssueDisplayProperties | undefined;
|
||||
enableIssueQuickAdd: boolean;
|
||||
@ -36,7 +35,7 @@ export interface IGroupByList {
|
||||
viewId?: string
|
||||
) => Promise<TIssue | undefined>;
|
||||
disableIssueCreation?: boolean;
|
||||
storeType: TCreateModalStoreTypes;
|
||||
storeType: EIssuesStoreType;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<TIssue>;
|
||||
viewId?: string;
|
||||
isCompletedCycle?: boolean;
|
||||
@ -47,7 +46,7 @@ const GroupByList: React.FC<IGroupByList> = (props) => {
|
||||
issueIds,
|
||||
issuesMap,
|
||||
group_by,
|
||||
handleIssues,
|
||||
updateIssue,
|
||||
quickActions,
|
||||
displayProperties,
|
||||
enableIssueQuickAdd,
|
||||
@ -142,7 +141,7 @@ const GroupByList: React.FC<IGroupByList> = (props) => {
|
||||
<IssueBlocksList
|
||||
issueIds={is_list ? issueIds || 0 : issueIds?.[_list.id] || 0}
|
||||
issuesMap={issuesMap}
|
||||
handleIssues={handleIssues}
|
||||
updateIssue={updateIssue}
|
||||
quickActions={quickActions}
|
||||
displayProperties={displayProperties}
|
||||
canEditProperties={canEditProperties}
|
||||
@ -170,7 +169,7 @@ export interface IList {
|
||||
issueIds: TGroupedIssues | TUnGroupedIssues | any;
|
||||
issuesMap: TIssueMap;
|
||||
group_by: string | null;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => Promise<void>;
|
||||
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
||||
quickActions: (issue: TIssue) => React.ReactNode;
|
||||
displayProperties: IIssueDisplayProperties | undefined;
|
||||
showEmptyGroup: boolean;
|
||||
@ -184,7 +183,7 @@ export interface IList {
|
||||
) => Promise<TIssue | undefined>;
|
||||
viewId?: string;
|
||||
disableIssueCreation?: boolean;
|
||||
storeType: TCreateModalStoreTypes;
|
||||
storeType: EIssuesStoreType;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<TIssue>;
|
||||
isCompletedCycle?: boolean;
|
||||
}
|
||||
@ -194,7 +193,7 @@ export const List: React.FC<IList> = (props) => {
|
||||
issueIds,
|
||||
issuesMap,
|
||||
group_by,
|
||||
handleIssues,
|
||||
updateIssue,
|
||||
quickActions,
|
||||
quickAddCallback,
|
||||
viewId,
|
||||
@ -214,7 +213,7 @@ export const List: React.FC<IList> = (props) => {
|
||||
issueIds={issueIds as TUnGroupedIssues}
|
||||
issuesMap={issuesMap}
|
||||
group_by={group_by}
|
||||
handleIssues={handleIssues}
|
||||
updateIssue={updateIssue}
|
||||
quickActions={quickActions}
|
||||
displayProperties={displayProperties}
|
||||
enableIssueQuickAdd={enableIssueQuickAdd}
|
||||
|
@ -10,7 +10,7 @@ import { CreateUpdateIssueModal } from "components/issues";
|
||||
// ui
|
||||
// mobx
|
||||
// hooks
|
||||
import { TCreateModalStoreTypes } from "constants/issue";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { useEventTracker } from "hooks/store";
|
||||
// types
|
||||
import { TIssue, ISearchIssueResponse } from "@plane/types";
|
||||
@ -21,7 +21,7 @@ interface IHeaderGroupByCard {
|
||||
count: number;
|
||||
issuePayload: Partial<TIssue>;
|
||||
disableIssueCreation?: boolean;
|
||||
storeType: TCreateModalStoreTypes;
|
||||
storeType: EIssuesStoreType;
|
||||
addIssuesToView?: (issueIds: string[]) => Promise<TIssue>;
|
||||
}
|
||||
|
||||
|
@ -1,47 +1,20 @@
|
||||
import { FC, useMemo } from "react";
|
||||
import { FC } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// hooks
|
||||
import { ArchivedIssueQuickActions } from "components/issues";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { useIssues } from "hooks/store";
|
||||
// components
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
// constants
|
||||
import { EIssueActions } from "../../types";
|
||||
import { BaseListRoot } from "../base-list-root";
|
||||
|
||||
export const ArchivedIssueListLayout: FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
||||
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.ARCHIVED);
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
await issues.removeIssue(workspaceSlug, projectId, issue.id);
|
||||
},
|
||||
[EIssueActions.RESTORE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
await issues.restoreIssue(workspaceSlug, projectId, issue.id);
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug, projectId]
|
||||
);
|
||||
|
||||
const canEditPropertiesBasedOnProject = () => false;
|
||||
|
||||
return (
|
||||
<BaseListRoot
|
||||
issuesFilter={issuesFilter}
|
||||
issues={issues}
|
||||
QuickActions={ArchivedIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
storeType={EIssuesStoreType.PROJECT}
|
||||
storeType={EIssuesStoreType.ARCHIVED}
|
||||
canEditPropertiesBasedOnProject={canEditPropertiesBasedOnProject}
|
||||
/>
|
||||
);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useMemo } from "react";
|
||||
import React, { useCallback } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// hooks
|
||||
@ -7,9 +7,7 @@ import { EIssuesStoreType } from "constants/issue";
|
||||
import { useCycle, useIssues } from "hooks/store";
|
||||
// components
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
// constants
|
||||
import { EIssueActions } from "../../types";
|
||||
import { BaseListRoot } from "../base-list-root";
|
||||
|
||||
export interface ICycleListLayout {}
|
||||
@ -18,34 +16,9 @@ export const CycleListLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, cycleId } = router.query;
|
||||
// store
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.CYCLE);
|
||||
const { issues } = useIssues(EIssuesStoreType.CYCLE);
|
||||
const { currentProjectCompletedCycleIds } = useCycle();
|
||||
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
|
||||
await issues.updateIssue(workspaceSlug.toString(), issue.project_id, issue.id, issue, cycleId.toString());
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
|
||||
await issues.removeIssue(workspaceSlug.toString(), issue.project_id, issue.id, cycleId.toString());
|
||||
},
|
||||
[EIssueActions.REMOVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
|
||||
await issues.removeIssueFromCycle(workspaceSlug.toString(), issue.project_id, cycleId.toString(), issue.id);
|
||||
},
|
||||
[EIssueActions.ARCHIVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
|
||||
await issues.archiveIssue(workspaceSlug.toString(), issue.project_id, issue.id, cycleId.toString());
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug, cycleId]
|
||||
);
|
||||
const isCompletedCycle =
|
||||
cycleId && currentProjectCompletedCycleIds ? currentProjectCompletedCycleIds.includes(cycleId.toString()) : false;
|
||||
|
||||
@ -61,10 +34,7 @@ export const CycleListLayout: React.FC = observer(() => {
|
||||
|
||||
return (
|
||||
<BaseListRoot
|
||||
issuesFilter={issuesFilter}
|
||||
issues={issues}
|
||||
QuickActions={CycleIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
viewId={cycleId?.toString()}
|
||||
storeType={EIssuesStoreType.CYCLE}
|
||||
addIssuesToView={addIssuesToView}
|
||||
|
@ -1,49 +1,19 @@
|
||||
import { FC, useMemo } from "react";
|
||||
import { FC } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// hooks
|
||||
import { ProjectIssueQuickActions } from "components/issues";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { useIssues } from "hooks/store";
|
||||
// components
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../../types";
|
||||
// constants
|
||||
import { BaseListRoot } from "../base-list-root";
|
||||
|
||||
export const DraftIssueListLayout: FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
|
||||
if (!workspaceSlug || !projectId) return null;
|
||||
|
||||
// store
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.DRAFT);
|
||||
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
await issues.updateIssue(workspaceSlug, projectId, issue.id, issue);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
await issues.removeIssue(workspaceSlug, projectId, issue.id);
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug, projectId]
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseListRoot
|
||||
issuesFilter={issuesFilter}
|
||||
issues={issues}
|
||||
QuickActions={ProjectIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
storeType={EIssuesStoreType.PROJECT}
|
||||
/>
|
||||
);
|
||||
return <BaseListRoot QuickActions={ProjectIssueQuickActions} storeType={EIssuesStoreType.DRAFT} />;
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useMemo } from "react";
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// mobx store
|
||||
@ -7,8 +7,6 @@ import { EIssuesStoreType } from "constants/issue";
|
||||
import { useIssues } from "hooks/store";
|
||||
// components
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../../types";
|
||||
// constants
|
||||
import { BaseListRoot } from "../base-list-root";
|
||||
|
||||
@ -18,40 +16,11 @@ export const ModuleListLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, moduleId } = router.query;
|
||||
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.MODULE);
|
||||
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
|
||||
await issues.updateIssue(workspaceSlug.toString(), issue.project_id, issue.id, issue, moduleId.toString());
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
|
||||
await issues.removeIssue(workspaceSlug.toString(), issue.project_id, issue.id, moduleId.toString());
|
||||
},
|
||||
[EIssueActions.REMOVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
|
||||
await issues.removeIssueFromModule(workspaceSlug.toString(), issue.project_id, moduleId.toString(), issue.id);
|
||||
},
|
||||
[EIssueActions.ARCHIVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
|
||||
await issues.archiveIssue(workspaceSlug.toString(), issue.project_id, issue.id, moduleId.toString());
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug, moduleId]
|
||||
);
|
||||
const { issues } = useIssues(EIssuesStoreType.MODULE);
|
||||
|
||||
return (
|
||||
<BaseListRoot
|
||||
issuesFilter={issuesFilter}
|
||||
issues={issues}
|
||||
QuickActions={ModuleIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
viewId={moduleId?.toString()}
|
||||
storeType={EIssuesStoreType.MODULE}
|
||||
addIssuesToView={(issueIds: string[]) => {
|
||||
|
@ -1,50 +1,20 @@
|
||||
import { FC, useMemo } from "react";
|
||||
import { FC } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// hooks
|
||||
import { ProjectIssueQuickActions } from "components/issues";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { EUserProjectRoles } from "constants/project";
|
||||
import { useIssues, useUser } from "hooks/store";
|
||||
import { useUser } from "hooks/store";
|
||||
// components
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../../types";
|
||||
// constants
|
||||
import { BaseListRoot } from "../base-list-root";
|
||||
|
||||
export const ProfileIssuesListLayout: FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, userId } = router.query as { workspaceSlug: string; userId: string };
|
||||
// store hooks
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROFILE);
|
||||
|
||||
const {
|
||||
membership: { currentWorkspaceAllProjectsRole },
|
||||
} = useUser();
|
||||
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !userId) return;
|
||||
|
||||
await issues.updateIssue(workspaceSlug, issue.project_id, issue.id, issue, userId);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !userId) return;
|
||||
|
||||
await issues.removeIssue(workspaceSlug, issue.project_id, issue.id, userId);
|
||||
},
|
||||
[EIssueActions.ARCHIVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !userId) return;
|
||||
|
||||
await issues.archiveIssue(workspaceSlug, issue.project_id, issue.id, userId);
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug, userId]
|
||||
);
|
||||
|
||||
const canEditPropertiesBasedOnProject = (projectId: string) => {
|
||||
const currentProjectRole = currentWorkspaceAllProjectsRole && currentWorkspaceAllProjectsRole[projectId];
|
||||
|
||||
@ -53,10 +23,7 @@ export const ProfileIssuesListLayout: FC = observer(() => {
|
||||
|
||||
return (
|
||||
<BaseListRoot
|
||||
issuesFilter={issuesFilter}
|
||||
issues={issues}
|
||||
QuickActions={ProjectIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
storeType={EIssuesStoreType.PROFILE}
|
||||
canEditPropertiesBasedOnProject={canEditPropertiesBasedOnProject}
|
||||
/>
|
||||
|
@ -1,55 +1,19 @@
|
||||
import { FC, useMemo } from "react";
|
||||
import { FC } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// hooks
|
||||
import { ProjectIssueQuickActions } from "components/issues";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { useIssues } from "hooks/store";
|
||||
// components
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../../types";
|
||||
// constants
|
||||
import { BaseListRoot } from "../base-list-root";
|
||||
|
||||
export const ListLayout: FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
||||
const { workspaceSlug, projectId } = router.query;
|
||||
|
||||
if (!workspaceSlug || !projectId) return null;
|
||||
|
||||
// store
|
||||
const { issuesFilter, issues } = useIssues(EIssuesStoreType.PROJECT);
|
||||
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
await issues.updateIssue(workspaceSlug, projectId, issue.id, issue);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
await issues.removeIssue(workspaceSlug, projectId, issue.id);
|
||||
},
|
||||
[EIssueActions.ARCHIVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
await issues.archiveIssue(workspaceSlug, projectId, issue.id);
|
||||
},
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[issues]
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseListRoot
|
||||
issuesFilter={issuesFilter}
|
||||
issues={issues}
|
||||
QuickActions={ProjectIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
storeType={EIssuesStoreType.PROJECT}
|
||||
/>
|
||||
);
|
||||
return <BaseListRoot QuickActions={ProjectIssueQuickActions} storeType={EIssuesStoreType.PROJECT} />;
|
||||
});
|
||||
|
@ -3,29 +3,13 @@ import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// store
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { useIssues } from "hooks/store";
|
||||
// constants
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
import { ProjectIssueQuickActions } from "../../quick-action-dropdowns";
|
||||
import { EIssueActions } from "../../types";
|
||||
// components
|
||||
import { BaseListRoot } from "../base-list-root";
|
||||
|
||||
export interface IViewListLayout {
|
||||
issueActions: {
|
||||
[EIssueActions.DELETE]: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.UPDATE]?: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.REMOVE]?: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.ARCHIVE]?: (issue: TIssue) => Promise<void>;
|
||||
};
|
||||
}
|
||||
|
||||
export const ProjectViewListLayout: React.FC<IViewListLayout> = observer((props) => {
|
||||
const { issueActions } = props;
|
||||
// store
|
||||
const { issuesFilter, issues } = useIssues(EIssuesStoreType.PROJECT_VIEW);
|
||||
|
||||
export const ProjectViewListLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId, viewId } = router.query;
|
||||
|
||||
@ -33,10 +17,7 @@ export const ProjectViewListLayout: React.FC<IViewListLayout> = observer((props)
|
||||
|
||||
return (
|
||||
<BaseListRoot
|
||||
issuesFilter={issuesFilter}
|
||||
issues={issues}
|
||||
QuickActions={ProjectIssueQuickActions}
|
||||
issueActions={issueActions}
|
||||
storeType={EIssuesStoreType.PROJECT_VIEW}
|
||||
viewId={viewId?.toString()}
|
||||
/>
|
||||
|
@ -30,7 +30,7 @@ import { WithDisplayPropertiesHOC } from "../properties/with-display-properties-
|
||||
|
||||
export interface IIssueProperties {
|
||||
issue: TIssue;
|
||||
handleIssues: (issue: TIssue) => Promise<void>;
|
||||
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
||||
displayProperties: IIssueDisplayProperties | undefined;
|
||||
isReadOnly: boolean;
|
||||
className: string;
|
||||
@ -38,7 +38,7 @@ export interface IIssueProperties {
|
||||
}
|
||||
|
||||
export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
||||
const { issue, handleIssues, displayProperties, activeLayout, isReadOnly, className } = props;
|
||||
const { issue, updateIssue, displayProperties, activeLayout, isReadOnly, className } = props;
|
||||
// store hooks
|
||||
const { labelMap } = useLabel();
|
||||
const { captureIssueEvent } = useEventTracker();
|
||||
@ -80,59 +80,63 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
||||
);
|
||||
|
||||
const handleState = (stateId: string) => {
|
||||
handleIssues({ ...issue, state_id: stateId }).then(() => {
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { ...issue, state: "SUCCESS", element: currentLayout },
|
||||
path: router.asPath,
|
||||
updates: {
|
||||
changed_property: "state",
|
||||
change_details: stateId,
|
||||
},
|
||||
updateIssue &&
|
||||
updateIssue(issue.project_id, issue.id, { state_id: stateId }).then(() => {
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { ...issue, state: "SUCCESS", element: currentLayout },
|
||||
path: router.asPath,
|
||||
updates: {
|
||||
changed_property: "state",
|
||||
change_details: stateId,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handlePriority = (value: TIssuePriorities) => {
|
||||
handleIssues({ ...issue, priority: value }).then(() => {
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { ...issue, state: "SUCCESS", element: currentLayout },
|
||||
path: router.asPath,
|
||||
updates: {
|
||||
changed_property: "priority",
|
||||
change_details: value,
|
||||
},
|
||||
updateIssue &&
|
||||
updateIssue(issue.project_id, issue.id, { priority: value }).then(() => {
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { ...issue, state: "SUCCESS", element: currentLayout },
|
||||
path: router.asPath,
|
||||
updates: {
|
||||
changed_property: "priority",
|
||||
change_details: value,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handleLabel = (ids: string[]) => {
|
||||
handleIssues({ ...issue, label_ids: ids }).then(() => {
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { ...issue, state: "SUCCESS", element: currentLayout },
|
||||
path: router.asPath,
|
||||
updates: {
|
||||
changed_property: "labels",
|
||||
change_details: ids,
|
||||
},
|
||||
updateIssue &&
|
||||
updateIssue(issue.project_id, issue.id, { label_ids: ids }).then(() => {
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { ...issue, state: "SUCCESS", element: currentLayout },
|
||||
path: router.asPath,
|
||||
updates: {
|
||||
changed_property: "labels",
|
||||
change_details: ids,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handleAssignee = (ids: string[]) => {
|
||||
handleIssues({ ...issue, assignee_ids: ids }).then(() => {
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { ...issue, state: "SUCCESS", element: currentLayout },
|
||||
path: router.asPath,
|
||||
updates: {
|
||||
changed_property: "assignees",
|
||||
change_details: ids,
|
||||
},
|
||||
updateIssue &&
|
||||
updateIssue(issue.project_id, issue.id, { assignee_ids: ids }).then(() => {
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { ...issue, state: "SUCCESS", element: currentLayout },
|
||||
path: router.asPath,
|
||||
updates: {
|
||||
changed_property: "assignees",
|
||||
change_details: ids,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handleModule = useCallback(
|
||||
@ -175,45 +179,52 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
||||
);
|
||||
|
||||
const handleStartDate = (date: Date | null) => {
|
||||
handleIssues({ ...issue, start_date: date ? renderFormattedPayloadDate(date) : null }).then(() => {
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { ...issue, state: "SUCCESS", element: currentLayout },
|
||||
path: router.asPath,
|
||||
updates: {
|
||||
changed_property: "start_date",
|
||||
change_details: date ? renderFormattedPayloadDate(date) : null,
|
||||
},
|
||||
});
|
||||
});
|
||||
updateIssue &&
|
||||
updateIssue(issue.project_id, issue.id, { start_date: date ? renderFormattedPayloadDate(date) : null }).then(
|
||||
() => {
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { ...issue, state: "SUCCESS", element: currentLayout },
|
||||
path: router.asPath,
|
||||
updates: {
|
||||
changed_property: "start_date",
|
||||
change_details: date ? renderFormattedPayloadDate(date) : null,
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const handleTargetDate = (date: Date | null) => {
|
||||
handleIssues({ ...issue, target_date: date ? renderFormattedPayloadDate(date) : null }).then(() => {
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { ...issue, state: "SUCCESS", element: currentLayout },
|
||||
path: router.asPath,
|
||||
updates: {
|
||||
changed_property: "target_date",
|
||||
change_details: date ? renderFormattedPayloadDate(date) : null,
|
||||
},
|
||||
});
|
||||
});
|
||||
updateIssue &&
|
||||
updateIssue(issue.project_id, issue.id, { target_date: date ? renderFormattedPayloadDate(date) : null }).then(
|
||||
() => {
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { ...issue, state: "SUCCESS", element: currentLayout },
|
||||
path: router.asPath,
|
||||
updates: {
|
||||
changed_property: "target_date",
|
||||
change_details: date ? renderFormattedPayloadDate(date) : null,
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const handleEstimate = (value: number | null) => {
|
||||
handleIssues({ ...issue, estimate_point: value }).then(() => {
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { ...issue, state: "SUCCESS", element: currentLayout },
|
||||
path: router.asPath,
|
||||
updates: {
|
||||
changed_property: "estimate_point",
|
||||
change_details: value,
|
||||
},
|
||||
updateIssue &&
|
||||
updateIssue(issue.project_id, issue.id, { estimate_point: value }).then(() => {
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { ...issue, state: "SUCCESS", element: currentLayout },
|
||||
path: router.asPath,
|
||||
updates: {
|
||||
changed_property: "estimate_point",
|
||||
change_details: value,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const redirectToIssueDetail = () => {
|
||||
|
@ -90,7 +90,7 @@ export const AllIssueQuickActions: React.FC<IQuickActionProps> = observer((props
|
||||
}}
|
||||
data={issueToEdit ?? duplicateIssuePayload}
|
||||
onSubmit={async (data) => {
|
||||
if (issueToEdit && handleUpdate) await handleUpdate({ ...issueToEdit, ...data });
|
||||
if (issueToEdit && handleUpdate) await handleUpdate(data);
|
||||
}}
|
||||
storeType={EIssuesStoreType.PROJECT}
|
||||
/>
|
||||
|
@ -101,7 +101,7 @@ export const CycleIssueQuickActions: React.FC<IQuickActionProps> = observer((pro
|
||||
}}
|
||||
data={issueToEdit ?? duplicateIssuePayload}
|
||||
onSubmit={async (data) => {
|
||||
if (issueToEdit && handleUpdate) await handleUpdate({ ...issueToEdit, ...data });
|
||||
if (issueToEdit && handleUpdate) await handleUpdate(data);
|
||||
}}
|
||||
storeType={EIssuesStoreType.CYCLE}
|
||||
/>
|
||||
|
@ -100,7 +100,7 @@ export const ModuleIssueQuickActions: React.FC<IQuickActionProps> = observer((pr
|
||||
}}
|
||||
data={issueToEdit ?? duplicateIssuePayload}
|
||||
onSubmit={async (data) => {
|
||||
if (issueToEdit && handleUpdate) await handleUpdate({ ...issueToEdit, ...data });
|
||||
if (issueToEdit && handleUpdate) await handleUpdate(data);
|
||||
}}
|
||||
storeType={EIssuesStoreType.MODULE}
|
||||
/>
|
||||
|
@ -100,7 +100,7 @@ export const ProjectIssueQuickActions: React.FC<IQuickActionProps> = observer((p
|
||||
}}
|
||||
data={issueToEdit ?? duplicateIssuePayload}
|
||||
onSubmit={async (data) => {
|
||||
if (issueToEdit && handleUpdate) await handleUpdate({ ...issueToEdit, ...data });
|
||||
if (issueToEdit && handleUpdate) await handleUpdate(data);
|
||||
}}
|
||||
storeType={EIssuesStoreType.PROJECT}
|
||||
isDraft={isDraftIssue}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { Fragment, useCallback, useMemo } from "react";
|
||||
import React, { Fragment, useCallback } from "react";
|
||||
import isEmpty from "lodash/isEmpty";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
@ -6,6 +6,7 @@ import useSWR from "swr";
|
||||
// hooks
|
||||
import { useWorkspaceIssueProperties } from "hooks/use-workspace-issue-properties";
|
||||
import { useApplication, useEventTracker, useGlobalView, useIssues, useProject, useUser } from "hooks/store";
|
||||
import { useIssuesActions } from "hooks/use-issues-actions";
|
||||
// components
|
||||
import { GlobalViewsAppliedFiltersRoot, IssuePeekOverview } from "components/issues";
|
||||
import { SpreadsheetView } from "components/issues/issue-layouts";
|
||||
@ -14,7 +15,6 @@ import { EmptyState } from "components/empty-state";
|
||||
import { SpreadsheetLayoutLoader } from "components/ui";
|
||||
// types
|
||||
import { TIssue, IIssueDisplayFilterOptions } from "@plane/types";
|
||||
import { EIssueActions } from "../types";
|
||||
// constants
|
||||
import { EUserProjectRoles } from "constants/project";
|
||||
import { EIssueFilterType, EIssuesStoreType, ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue";
|
||||
@ -30,8 +30,9 @@ export const AllIssueLayoutRoot: React.FC = observer(() => {
|
||||
const { commandPalette: commandPaletteStore } = useApplication();
|
||||
const {
|
||||
issuesFilter: { filters, fetchFilters, updateFilters },
|
||||
issues: { loader, groupedIssueIds, fetchIssues, updateIssue, removeIssue, archiveIssue },
|
||||
issues: { loader, groupedIssueIds, fetchIssues },
|
||||
} = useIssues(EIssuesStoreType.GLOBAL);
|
||||
const { updateIssue, removeIssue, archiveIssue } = useIssuesActions(EIssuesStoreType.GLOBAL);
|
||||
|
||||
const { dataViewId, issueIds } = groupedIssueIds;
|
||||
const {
|
||||
@ -111,41 +112,6 @@ export const AllIssueLayoutRoot: React.FC = observer(() => {
|
||||
|
||||
const issueFilters = globalViewId ? filters?.[globalViewId.toString()] : undefined;
|
||||
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
const projectId = issue.project_id;
|
||||
if (!workspaceSlug || !projectId || !globalViewId) return;
|
||||
|
||||
await updateIssue(workspaceSlug.toString(), projectId, issue.id, issue, globalViewId.toString());
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
const projectId = issue.project_id;
|
||||
if (!workspaceSlug || !projectId || !globalViewId) return;
|
||||
|
||||
await removeIssue(workspaceSlug.toString(), projectId, issue.id, globalViewId.toString());
|
||||
},
|
||||
[EIssueActions.ARCHIVE]: async (issue: TIssue) => {
|
||||
const projectId = issue.project_id;
|
||||
if (!workspaceSlug || !projectId || !globalViewId) return;
|
||||
|
||||
await archiveIssue(workspaceSlug.toString(), projectId, issue.id, globalViewId.toString());
|
||||
},
|
||||
}),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[updateIssue, removeIssue, workspaceSlug]
|
||||
);
|
||||
|
||||
const handleIssues = useCallback(
|
||||
async (issue: TIssue, action: EIssueActions) => {
|
||||
if (action === EIssueActions.UPDATE) await issueActions[action]!(issue);
|
||||
if (action === EIssueActions.DELETE) await issueActions[action]!(issue);
|
||||
if (action === EIssueActions.ARCHIVE) await issueActions[action]!(issue);
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[]
|
||||
);
|
||||
|
||||
const handleDisplayFiltersUpdate = useCallback(
|
||||
(updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => {
|
||||
if (!workspaceSlug || !globalViewId) return;
|
||||
@ -166,14 +132,14 @@ export const AllIssueLayoutRoot: React.FC = observer(() => {
|
||||
<AllIssueQuickActions
|
||||
customActionButton={customActionButton}
|
||||
issue={issue}
|
||||
handleUpdate={async () => handleIssues({ ...issue }, EIssueActions.UPDATE)}
|
||||
handleDelete={async () => handleIssues(issue, EIssueActions.DELETE)}
|
||||
handleArchive={async () => handleIssues(issue, EIssueActions.ARCHIVE)}
|
||||
handleDelete={async () => removeIssue(issue.project_id, issue.id)}
|
||||
handleUpdate={async (data) => updateIssue && updateIssue(issue.project_id, issue.id, data)}
|
||||
handleArchive={async () => archiveIssue && archiveIssue(issue.project_id, issue.id)}
|
||||
portalElement={portalElement}
|
||||
readOnly={!canEditProperties(issue.project_id)}
|
||||
/>
|
||||
),
|
||||
[canEditProperties, handleIssues]
|
||||
[canEditProperties, removeIssue, updateIssue, archiveIssue]
|
||||
);
|
||||
|
||||
if (loader === "init-loader" || !globalViewId || globalViewId !== dataViewId || !issueIds) {
|
||||
@ -213,7 +179,7 @@ export const AllIssueLayoutRoot: React.FC = observer(() => {
|
||||
handleDisplayFilterUpdate={handleDisplayFiltersUpdate}
|
||||
issueIds={issueIds}
|
||||
quickActions={renderQuickActions}
|
||||
handleIssues={handleIssues}
|
||||
updateIssue={updateIssue}
|
||||
canEditProperties={canEditProperties}
|
||||
viewId={globalViewId}
|
||||
/>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { Fragment, useMemo } from "react";
|
||||
import React, { Fragment } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
@ -19,8 +19,6 @@ import { ActiveLoader } from "components/ui";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { useIssues } from "hooks/store";
|
||||
// types
|
||||
import { TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../types";
|
||||
|
||||
export const ProjectViewLayoutRoot: React.FC = observer(() => {
|
||||
// router
|
||||
@ -45,22 +43,6 @@ export const ProjectViewLayoutRoot: React.FC = observer(() => {
|
||||
{ revalidateIfStale: false, revalidateOnFocus: false }
|
||||
);
|
||||
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
await issues.updateIssue(workspaceSlug.toString(), projectId.toString(), issue.id, issue, viewId?.toString());
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
await issues.removeIssue(workspaceSlug.toString(), projectId.toString(), issue.id, viewId?.toString());
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug, projectId, viewId]
|
||||
);
|
||||
|
||||
const activeLayout = issuesFilter?.issueFilters?.displayFilters?.layout;
|
||||
|
||||
if (!workspaceSlug || !projectId || !viewId) return <></>;
|
||||
@ -81,15 +63,15 @@ export const ProjectViewLayoutRoot: React.FC = observer(() => {
|
||||
<Fragment>
|
||||
<div className="relative h-full w-full overflow-auto">
|
||||
{activeLayout === "list" ? (
|
||||
<ProjectViewListLayout issueActions={issueActions} />
|
||||
<ProjectViewListLayout />
|
||||
) : activeLayout === "kanban" ? (
|
||||
<ProjectViewKanBanLayout issueActions={issueActions} />
|
||||
<ProjectViewKanBanLayout />
|
||||
) : activeLayout === "calendar" ? (
|
||||
<ProjectViewCalendarLayout issueActions={issueActions} />
|
||||
<ProjectViewCalendarLayout />
|
||||
) : activeLayout === "gantt_chart" ? (
|
||||
<ProjectViewGanttLayout issueActions={issueActions} />
|
||||
<ProjectViewGanttLayout />
|
||||
) : activeLayout === "spreadsheet" ? (
|
||||
<ProjectViewSpreadsheetLayout issueActions={issueActions} />
|
||||
<ProjectViewSpreadsheetLayout />
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
|
@ -2,56 +2,44 @@ import { FC, useCallback } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// hooks
|
||||
import { EIssueFilterType } from "constants/issue";
|
||||
import { EIssueFilterType, EIssuesStoreType } from "constants/issue";
|
||||
import { EUserProjectRoles } from "constants/project";
|
||||
import { useUser } from "hooks/store";
|
||||
import { useIssues, useUser } from "hooks/store";
|
||||
import { useIssuesActions } from "hooks/use-issues-actions";
|
||||
// views
|
||||
// types
|
||||
// constants
|
||||
import { ICycleIssuesFilter, ICycleIssues } from "store/issue/cycle";
|
||||
import { IModuleIssuesFilter, IModuleIssues } from "store/issue/module";
|
||||
import { IProjectIssuesFilter, IProjectIssues } from "store/issue/project";
|
||||
import { IProjectViewIssuesFilter, IProjectViewIssues } from "store/issue/project-views";
|
||||
import { TIssue, IIssueDisplayFilterOptions, TUnGroupedIssues } from "@plane/types";
|
||||
import { IQuickActionProps } from "../list/list-view-types";
|
||||
import { EIssueActions } from "../types";
|
||||
import { SpreadsheetView } from "./spreadsheet-view";
|
||||
|
||||
export type SpreadsheetStoreType =
|
||||
| EIssuesStoreType.PROJECT
|
||||
| EIssuesStoreType.MODULE
|
||||
| EIssuesStoreType.CYCLE
|
||||
| EIssuesStoreType.PROJECT_VIEW;
|
||||
interface IBaseSpreadsheetRoot {
|
||||
issueFiltersStore: IProjectIssuesFilter | IModuleIssuesFilter | ICycleIssuesFilter | IProjectViewIssuesFilter;
|
||||
issueStore: IProjectIssues | ICycleIssues | IModuleIssues | IProjectViewIssues;
|
||||
viewId?: string;
|
||||
QuickActions: FC<IQuickActionProps>;
|
||||
issueActions: {
|
||||
[EIssueActions.DELETE]: (issue: TIssue) => void;
|
||||
[EIssueActions.UPDATE]?: (issue: TIssue) => void;
|
||||
[EIssueActions.REMOVE]?: (issue: TIssue) => void;
|
||||
[EIssueActions.ARCHIVE]?: (issue: TIssue) => void;
|
||||
[EIssueActions.RESTORE]?: (issue: TIssue) => Promise<void>;
|
||||
};
|
||||
storeType: SpreadsheetStoreType;
|
||||
canEditPropertiesBasedOnProject?: (projectId: string) => boolean;
|
||||
isCompletedCycle?: boolean;
|
||||
}
|
||||
|
||||
export const BaseSpreadsheetRoot = observer((props: IBaseSpreadsheetRoot) => {
|
||||
const {
|
||||
issueFiltersStore,
|
||||
issueStore,
|
||||
viewId,
|
||||
QuickActions,
|
||||
issueActions,
|
||||
canEditPropertiesBasedOnProject,
|
||||
isCompletedCycle = false,
|
||||
} = props;
|
||||
const { viewId, QuickActions, storeType, canEditPropertiesBasedOnProject, isCompletedCycle = false } = props;
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
||||
const { projectId } = router.query;
|
||||
// store hooks
|
||||
const {
|
||||
membership: { currentProjectRole },
|
||||
} = useUser();
|
||||
const { issues, issuesFilter } = useIssues(storeType);
|
||||
const { updateIssue, removeIssue, removeIssueFromView, archiveIssue, restoreIssue, updateFilters } =
|
||||
useIssuesActions(storeType);
|
||||
// derived values
|
||||
const { enableInlineEditing, enableQuickAdd, enableIssueCreation } = issueStore?.viewFlags || {};
|
||||
const { enableInlineEditing, enableQuickAdd, enableIssueCreation } = issues?.viewFlags || {};
|
||||
// user role validation
|
||||
const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserProjectRoles.MEMBER;
|
||||
|
||||
@ -65,32 +53,17 @@ export const BaseSpreadsheetRoot = observer((props: IBaseSpreadsheetRoot) => {
|
||||
[canEditPropertiesBasedOnProject, enableInlineEditing, isEditingAllowed]
|
||||
);
|
||||
|
||||
const issueIds = (issueStore.groupedIssueIds ?? []) as TUnGroupedIssues;
|
||||
|
||||
const handleIssues = useCallback(
|
||||
async (issue: TIssue, action: EIssueActions) => {
|
||||
if (issueActions[action]) {
|
||||
issueActions[action]!(issue);
|
||||
}
|
||||
},
|
||||
[issueActions]
|
||||
);
|
||||
const issueIds = (issues.groupedIssueIds ?? []) as TUnGroupedIssues;
|
||||
|
||||
const handleDisplayFiltersUpdate = useCallback(
|
||||
(updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
if ( !projectId) return;
|
||||
|
||||
issueFiltersStore.updateFilters(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
EIssueFilterType.DISPLAY_FILTERS,
|
||||
{
|
||||
...updatedDisplayFilter,
|
||||
},
|
||||
viewId
|
||||
);
|
||||
updateFilters(projectId.toString(), EIssueFilterType.DISPLAY_FILTERS, {
|
||||
...updatedDisplayFilter,
|
||||
});
|
||||
},
|
||||
[issueFiltersStore, projectId, workspaceSlug, viewId]
|
||||
[ projectId, updateFilters]
|
||||
);
|
||||
|
||||
const renderQuickActions = useCallback(
|
||||
@ -98,37 +71,28 @@ export const BaseSpreadsheetRoot = observer((props: IBaseSpreadsheetRoot) => {
|
||||
<QuickActions
|
||||
customActionButton={customActionButton}
|
||||
issue={issue}
|
||||
handleDelete={async () => handleIssues(issue, EIssueActions.DELETE)}
|
||||
handleUpdate={
|
||||
issueActions[EIssueActions.UPDATE] ? async (data) => handleIssues(data, EIssueActions.UPDATE) : undefined
|
||||
}
|
||||
handleRemoveFromView={
|
||||
issueActions[EIssueActions.REMOVE] ? async () => handleIssues(issue, EIssueActions.REMOVE) : undefined
|
||||
}
|
||||
handleArchive={
|
||||
issueActions[EIssueActions.ARCHIVE] ? async () => handleIssues(issue, EIssueActions.ARCHIVE) : undefined
|
||||
}
|
||||
handleRestore={
|
||||
issueActions[EIssueActions.RESTORE] ? async () => handleIssues(issue, EIssueActions.RESTORE) : undefined
|
||||
}
|
||||
handleDelete={async () => removeIssue(issue.project_id, issue.id)}
|
||||
handleUpdate={async (data) => updateIssue && updateIssue(issue.project_id, issue.id, data)}
|
||||
handleRemoveFromView={async () => removeIssueFromView && removeIssueFromView(issue.project_id, issue.id)}
|
||||
handleArchive={async () => archiveIssue && archiveIssue(issue.project_id, issue.id)}
|
||||
handleRestore={async () => restoreIssue && restoreIssue(issue.project_id, issue.id)}
|
||||
portalElement={portalElement}
|
||||
readOnly={!isEditingAllowed || isCompletedCycle}
|
||||
/>
|
||||
),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[handleIssues]
|
||||
[isEditingAllowed, isCompletedCycle, removeIssue, updateIssue, removeIssueFromView, archiveIssue, restoreIssue]
|
||||
);
|
||||
|
||||
return (
|
||||
<SpreadsheetView
|
||||
displayProperties={issueFiltersStore.issueFilters?.displayProperties ?? {}}
|
||||
displayFilters={issueFiltersStore.issueFilters?.displayFilters ?? {}}
|
||||
displayProperties={issuesFilter.issueFilters?.displayProperties ?? {}}
|
||||
displayFilters={issuesFilter.issueFilters?.displayFilters ?? {}}
|
||||
handleDisplayFilterUpdate={handleDisplayFiltersUpdate}
|
||||
issueIds={issueIds}
|
||||
quickActions={renderQuickActions}
|
||||
handleIssues={handleIssues}
|
||||
updateIssue={updateIssue}
|
||||
canEditProperties={canEditProperties}
|
||||
quickAddCallback={issueStore.quickAddIssue}
|
||||
quickAddCallback={issues.quickAddIssue}
|
||||
viewId={viewId}
|
||||
enableQuickCreateIssue={enableQuickAdd}
|
||||
disableIssueCreation={!enableIssueCreation || !isEditingAllowed || isCompletedCycle}
|
||||
|
@ -6,7 +6,6 @@ import { SPREADSHEET_PROPERTY_DETAILS } from "constants/spreadsheet";
|
||||
import { useEventTracker } from "hooks/store";
|
||||
import { IIssueDisplayProperties, TIssue } from "@plane/types";
|
||||
import { WithDisplayPropertiesHOC } from "../properties/with-display-properties-HOC";
|
||||
import { EIssueActions } from "../types";
|
||||
// constants
|
||||
// components
|
||||
|
||||
@ -15,12 +14,12 @@ type Props = {
|
||||
issueDetail: TIssue;
|
||||
disableUserActions: boolean;
|
||||
property: keyof IIssueDisplayProperties;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => Promise<void>;
|
||||
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
||||
isEstimateEnabled: boolean;
|
||||
};
|
||||
|
||||
export const IssueColumn = observer((props: Props) => {
|
||||
const { displayProperties, issueDetail, disableUserActions, property, handleIssues, isEstimateEnabled } = props;
|
||||
const { displayProperties, issueDetail, disableUserActions, property, updateIssue, isEstimateEnabled } = props;
|
||||
// router
|
||||
const router = useRouter();
|
||||
const tableCellRef = useRef<HTMLTableCellElement | null>(null);
|
||||
@ -44,7 +43,8 @@ export const IssueColumn = observer((props: Props) => {
|
||||
<Column
|
||||
issue={issueDetail}
|
||||
onChange={(issue: TIssue, data: Partial<TIssue>, updates: any) =>
|
||||
handleIssues({ ...issue, ...data }, EIssueActions.UPDATE).then(() => {
|
||||
updateIssue &&
|
||||
updateIssue(issue.project_id, issue.id, data).then(() => {
|
||||
captureIssueEvent({
|
||||
eventName: "Issue updated",
|
||||
payload: {
|
||||
|
@ -18,7 +18,6 @@ import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||
import { IIssueDisplayProperties, TIssue } from "@plane/types";
|
||||
// local components
|
||||
import { WithDisplayPropertiesHOC } from "../properties/with-display-properties-HOC";
|
||||
import { EIssueActions } from "../types";
|
||||
import { IssueColumn } from "./issue-column";
|
||||
|
||||
interface Props {
|
||||
@ -30,7 +29,7 @@ interface Props {
|
||||
portalElement?: HTMLDivElement | null
|
||||
) => React.ReactNode;
|
||||
canEditProperties: (projectId: string | undefined) => boolean;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => Promise<void>;
|
||||
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
||||
portalElement: React.MutableRefObject<HTMLDivElement | null>;
|
||||
nestingLevel: number;
|
||||
issueId: string;
|
||||
@ -46,7 +45,7 @@ export const SpreadsheetIssueRow = observer((props: Props) => {
|
||||
isEstimateEnabled,
|
||||
nestingLevel,
|
||||
portalElement,
|
||||
handleIssues,
|
||||
updateIssue,
|
||||
quickActions,
|
||||
canEditProperties,
|
||||
isScrolled,
|
||||
@ -76,7 +75,7 @@ export const SpreadsheetIssueRow = observer((props: Props) => {
|
||||
canEditProperties={canEditProperties}
|
||||
nestingLevel={nestingLevel}
|
||||
isEstimateEnabled={isEstimateEnabled}
|
||||
handleIssues={handleIssues}
|
||||
updateIssue={updateIssue}
|
||||
portalElement={portalElement}
|
||||
isScrolled={isScrolled}
|
||||
isExpanded={isExpanded}
|
||||
@ -96,7 +95,7 @@ export const SpreadsheetIssueRow = observer((props: Props) => {
|
||||
canEditProperties={canEditProperties}
|
||||
nestingLevel={nestingLevel + 1}
|
||||
isEstimateEnabled={isEstimateEnabled}
|
||||
handleIssues={handleIssues}
|
||||
updateIssue={updateIssue}
|
||||
portalElement={portalElement}
|
||||
isScrolled={isScrolled}
|
||||
containerRef={containerRef}
|
||||
@ -116,7 +115,7 @@ interface IssueRowDetailsProps {
|
||||
portalElement?: HTMLDivElement | null
|
||||
) => React.ReactNode;
|
||||
canEditProperties: (projectId: string | undefined) => boolean;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => Promise<void>;
|
||||
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
||||
portalElement: React.MutableRefObject<HTMLDivElement | null>;
|
||||
nestingLevel: number;
|
||||
issueId: string;
|
||||
@ -132,7 +131,7 @@ const IssueRowDetails = observer((props: IssueRowDetailsProps) => {
|
||||
isEstimateEnabled,
|
||||
nestingLevel,
|
||||
portalElement,
|
||||
handleIssues,
|
||||
updateIssue,
|
||||
quickActions,
|
||||
canEditProperties,
|
||||
isScrolled,
|
||||
@ -261,7 +260,7 @@ const IssueRowDetails = observer((props: IssueRowDetailsProps) => {
|
||||
issueDetail={issueDetail}
|
||||
disableUserActions={disableUserActions}
|
||||
property={property}
|
||||
handleIssues={handleIssues}
|
||||
updateIssue={updateIssue}
|
||||
isEstimateEnabled={isEstimateEnabled}
|
||||
/>
|
||||
))}
|
||||
|
@ -1,59 +1,32 @@
|
||||
import React, { useCallback, useMemo } from "react";
|
||||
import React, { useCallback } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// mobx store
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { useCycle, useIssues } from "hooks/store";
|
||||
import { useCycle } from "hooks/store";
|
||||
// components
|
||||
import { TIssue } from "@plane/types";
|
||||
import { CycleIssueQuickActions } from "../../quick-action-dropdowns";
|
||||
import { EIssueActions } from "../../types";
|
||||
import { BaseSpreadsheetRoot } from "../base-spreadsheet-root";
|
||||
|
||||
export const CycleSpreadsheetLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, cycleId } = router.query as { workspaceSlug: string; cycleId: string };
|
||||
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.CYCLE);
|
||||
const { cycleId } = router.query;
|
||||
const { currentProjectCompletedCycleIds } = useCycle();
|
||||
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
|
||||
issues.updateIssue(workspaceSlug, issue.project_id, issue.id, issue, cycleId);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
issues.removeIssue(workspaceSlug, issue.project_id, issue.id, cycleId);
|
||||
},
|
||||
[EIssueActions.REMOVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
issues.removeIssueFromCycle(workspaceSlug, issue.project_id, cycleId, issue.id);
|
||||
},
|
||||
[EIssueActions.ARCHIVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !cycleId) return;
|
||||
issues.archiveIssue(workspaceSlug, issue.project_id, issue.id, cycleId);
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug, cycleId]
|
||||
);
|
||||
|
||||
const isCompletedCycle =
|
||||
cycleId && currentProjectCompletedCycleIds ? currentProjectCompletedCycleIds.includes(cycleId.toString()) : false;
|
||||
|
||||
const canEditIssueProperties = useCallback(() => !isCompletedCycle, [isCompletedCycle]);
|
||||
|
||||
if (!cycleId) return null;
|
||||
|
||||
return (
|
||||
<BaseSpreadsheetRoot
|
||||
issueStore={issues}
|
||||
issueFiltersStore={issuesFilter}
|
||||
viewId={cycleId}
|
||||
issueActions={issueActions}
|
||||
viewId={cycleId?.toString()}
|
||||
QuickActions={CycleIssueQuickActions}
|
||||
canEditPropertiesBasedOnProject={canEditIssueProperties}
|
||||
isCompletedCycle={isCompletedCycle}
|
||||
storeType={EIssuesStoreType.CYCLE}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -1,51 +1,23 @@
|
||||
import React, { useMemo } from "react";
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// mobx store
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { useIssues } from "hooks/store";
|
||||
// components
|
||||
import { TIssue } from "@plane/types";
|
||||
import { ModuleIssueQuickActions } from "../../quick-action-dropdowns";
|
||||
import { EIssueActions } from "../../types";
|
||||
import { BaseSpreadsheetRoot } from "../base-spreadsheet-root";
|
||||
|
||||
export const ModuleSpreadsheetLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug, moduleId } = router.query as { workspaceSlug: string; moduleId: string };
|
||||
const { moduleId } = router.query;
|
||||
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.MODULE);
|
||||
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
|
||||
issues.updateIssue(workspaceSlug.toString(), issue.project_id, issue.id, issue, moduleId);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
issues.removeIssue(workspaceSlug, issue.project_id, issue.id, moduleId);
|
||||
},
|
||||
[EIssueActions.REMOVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
issues.removeIssueFromModule(workspaceSlug, issue.project_id, moduleId, issue.id);
|
||||
},
|
||||
[EIssueActions.ARCHIVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug || !moduleId) return;
|
||||
issues.archiveIssue(workspaceSlug, issue.project_id, issue.id, moduleId);
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug, moduleId]
|
||||
);
|
||||
if (!moduleId) return null;
|
||||
|
||||
return (
|
||||
<BaseSpreadsheetRoot
|
||||
issueStore={issues}
|
||||
issueFiltersStore={issuesFilter}
|
||||
viewId={moduleId}
|
||||
issueActions={issueActions}
|
||||
viewId={moduleId.toString()}
|
||||
QuickActions={ModuleIssueQuickActions}
|
||||
storeType={EIssuesStoreType.MODULE}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -1,48 +1,10 @@
|
||||
import React, { useMemo } from "react";
|
||||
import React from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// mobx store
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { useIssues } from "hooks/store";
|
||||
|
||||
import { TIssue } from "@plane/types";
|
||||
import { ProjectIssueQuickActions } from "../../quick-action-dropdowns";
|
||||
import { EIssueActions } from "../../types";
|
||||
import { BaseSpreadsheetRoot } from "../base-spreadsheet-root";
|
||||
|
||||
export const ProjectSpreadsheetLayout: React.FC = observer(() => {
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query as { workspaceSlug: string };
|
||||
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT);
|
||||
|
||||
const issueActions = useMemo(
|
||||
() => ({
|
||||
[EIssueActions.UPDATE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
await issues.updateIssue(workspaceSlug, issue.project_id, issue.id, issue);
|
||||
},
|
||||
[EIssueActions.DELETE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
await issues.removeIssue(workspaceSlug, issue.project_id, issue.id);
|
||||
},
|
||||
[EIssueActions.ARCHIVE]: async (issue: TIssue) => {
|
||||
if (!workspaceSlug) return;
|
||||
|
||||
await issues.archiveIssue(workspaceSlug, issue.project_id, issue.id);
|
||||
},
|
||||
}),
|
||||
[issues, workspaceSlug]
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseSpreadsheetRoot
|
||||
issueStore={issues}
|
||||
issueFiltersStore={issuesFilter}
|
||||
issueActions={issueActions}
|
||||
QuickActions={ProjectIssueQuickActions}
|
||||
/>
|
||||
);
|
||||
});
|
||||
export const ProjectSpreadsheetLayout: React.FC = observer(() => (
|
||||
<BaseSpreadsheetRoot QuickActions={ProjectIssueQuickActions} storeType={EIssuesStoreType.PROJECT} />
|
||||
));
|
||||
|
@ -3,39 +3,22 @@ import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/router";
|
||||
// mobx store
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import { useIssues } from "hooks/store";
|
||||
// components
|
||||
import { TIssue } from "@plane/types";
|
||||
import { ProjectIssueQuickActions } from "../../quick-action-dropdowns";
|
||||
import { EIssueActions } from "../../types";
|
||||
import { BaseSpreadsheetRoot } from "../base-spreadsheet-root";
|
||||
// types
|
||||
// constants
|
||||
|
||||
export interface IViewSpreadsheetLayout {
|
||||
issueActions: {
|
||||
[EIssueActions.DELETE]: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.UPDATE]?: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.REMOVE]?: (issue: TIssue) => Promise<void>;
|
||||
[EIssueActions.ARCHIVE]?: (issue: TIssue) => Promise<void>;
|
||||
};
|
||||
}
|
||||
|
||||
export const ProjectViewSpreadsheetLayout: React.FC<IViewSpreadsheetLayout> = observer((props) => {
|
||||
const { issueActions } = props;
|
||||
export const ProjectViewSpreadsheetLayout: React.FC = observer(() => {
|
||||
// router
|
||||
const router = useRouter();
|
||||
const { viewId } = router.query;
|
||||
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT_VIEW);
|
||||
|
||||
return (
|
||||
<BaseSpreadsheetRoot
|
||||
issueStore={issues}
|
||||
issueFiltersStore={issuesFilter}
|
||||
issueActions={issueActions}
|
||||
QuickActions={ProjectIssueQuickActions}
|
||||
viewId={viewId?.toString()}
|
||||
storeType={EIssuesStoreType.PROJECT_VIEW}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
@ -3,7 +3,6 @@ import { observer } from "mobx-react-lite";
|
||||
//types
|
||||
import { useTableKeyboardNavigation } from "hooks/use-table-keyboard-navigation";
|
||||
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, TIssue } from "@plane/types";
|
||||
import { EIssueActions } from "../types";
|
||||
//components
|
||||
import { SpreadsheetIssueRow } from "./issue-row";
|
||||
import { SpreadsheetHeader } from "./spreadsheet-header";
|
||||
@ -19,7 +18,7 @@ type Props = {
|
||||
customActionButton?: React.ReactElement,
|
||||
portalElement?: HTMLDivElement | null
|
||||
) => React.ReactNode;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => Promise<void>;
|
||||
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
||||
canEditProperties: (projectId: string | undefined) => boolean;
|
||||
portalElement: React.MutableRefObject<HTMLDivElement | null>;
|
||||
containerRef: MutableRefObject<HTMLTableElement | null>;
|
||||
@ -34,7 +33,7 @@ export const SpreadsheetTable = observer((props: Props) => {
|
||||
isEstimateEnabled,
|
||||
portalElement,
|
||||
quickActions,
|
||||
handleIssues,
|
||||
updateIssue,
|
||||
canEditProperties,
|
||||
containerRef,
|
||||
} = props;
|
||||
@ -95,7 +94,7 @@ export const SpreadsheetTable = observer((props: Props) => {
|
||||
canEditProperties={canEditProperties}
|
||||
nestingLevel={0}
|
||||
isEstimateEnabled={isEstimateEnabled}
|
||||
handleIssues={handleIssues}
|
||||
updateIssue={updateIssue}
|
||||
portalElement={portalElement}
|
||||
containerRef={containerRef}
|
||||
isScrolled={isScrolled}
|
||||
|
@ -5,7 +5,6 @@ import { Spinner } from "@plane/ui";
|
||||
import { SpreadsheetQuickAddIssueForm } from "components/issues";
|
||||
import { useProject } from "hooks/store";
|
||||
import { TIssue, IIssueDisplayFilterOptions, IIssueDisplayProperties } from "@plane/types";
|
||||
import { EIssueActions } from "../types";
|
||||
import { SpreadsheetTable } from "./spreadsheet-table";
|
||||
// types
|
||||
//hooks
|
||||
@ -20,7 +19,7 @@ type Props = {
|
||||
customActionButton?: React.ReactElement,
|
||||
portalElement?: HTMLDivElement | null
|
||||
) => React.ReactNode;
|
||||
handleIssues: (issue: TIssue, action: EIssueActions) => Promise<void>;
|
||||
updateIssue: ((projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>) | undefined;
|
||||
openIssuesListModal?: (() => void) | null;
|
||||
quickAddCallback?: (
|
||||
workspaceSlug: string,
|
||||
@ -41,7 +40,7 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
||||
handleDisplayFilterUpdate,
|
||||
issueIds,
|
||||
quickActions,
|
||||
handleIssues,
|
||||
updateIssue,
|
||||
quickAddCallback,
|
||||
viewId,
|
||||
canEditProperties,
|
||||
@ -75,7 +74,7 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
||||
isEstimateEnabled={isEstimateEnabled}
|
||||
portalElement={portalRef}
|
||||
quickActions={quickActions}
|
||||
handleIssues={handleIssues}
|
||||
updateIssue={updateIssue}
|
||||
canEditProperties={canEditProperties}
|
||||
containerRef={containerRef}
|
||||
/>
|
||||
|
@ -29,6 +29,7 @@ import { FileService } from "services/file.service";
|
||||
// components
|
||||
// ui
|
||||
// helpers
|
||||
import { getChangedIssuefields } from "helpers/issue.helper";
|
||||
// types
|
||||
import type { TIssue, ISearchIssueResponse } from "@plane/types";
|
||||
|
||||
@ -126,7 +127,7 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
||||
} = useIssueDetail();
|
||||
// form info
|
||||
const {
|
||||
formState: { errors, isDirty, isSubmitting },
|
||||
formState: { errors, isDirty, isSubmitting, dirtyFields },
|
||||
handleSubmit,
|
||||
reset,
|
||||
watch,
|
||||
@ -166,7 +167,15 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
||||
const issueName = watch("name");
|
||||
|
||||
const handleFormSubmit = async (formData: Partial<TIssue>, is_draft_issue = false) => {
|
||||
await onSubmit(formData, is_draft_issue);
|
||||
const submitData = !data?.id
|
||||
? formData
|
||||
: {
|
||||
...getChangedIssuefields(formData, dirtyFields as { [key: string]: boolean | undefined }),
|
||||
project_id: getValues("project_id"),
|
||||
id: data.id,
|
||||
description_html: formData.description_html ?? "<p></p>",
|
||||
};
|
||||
await onSubmit(submitData, is_draft_issue);
|
||||
|
||||
setGptAssistantModal(false);
|
||||
|
||||
@ -761,3 +770,4 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -6,7 +6,7 @@ import { Dialog, Transition } from "@headlessui/react";
|
||||
import { TOAST_TYPE, setToast } from "@plane/ui";
|
||||
|
||||
import { ISSUE_CREATED, ISSUE_UPDATED } from "constants/event-tracker";
|
||||
import { EIssuesStoreType, TCreateModalStoreTypes } from "constants/issue";
|
||||
import { EIssuesStoreType } from "constants/issue";
|
||||
import {
|
||||
useApplication,
|
||||
useEventTracker,
|
||||
@ -17,6 +17,7 @@ import {
|
||||
useIssueDetail,
|
||||
} from "hooks/store";
|
||||
import useLocalStorage from "hooks/use-local-storage";
|
||||
import { useIssuesActions } from "hooks/use-issues-actions";
|
||||
// components
|
||||
import type { TIssue } from "@plane/types";
|
||||
import { DraftIssueLayout } from "./draft-issue-layout";
|
||||
@ -31,7 +32,7 @@ export interface IssuesModalProps {
|
||||
onClose: () => void;
|
||||
onSubmit?: (res: TIssue) => Promise<void>;
|
||||
withDraftIssueWrapper?: boolean;
|
||||
storeType?: TCreateModalStoreTypes;
|
||||
storeType?: EIssuesStoreType;
|
||||
isDraft?: boolean;
|
||||
}
|
||||
|
||||
@ -53,41 +54,15 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
|
||||
// store hooks
|
||||
const { captureIssueEvent } = useEventTracker();
|
||||
const {
|
||||
router: { workspaceSlug, projectId, cycleId, moduleId, viewId: projectViewId },
|
||||
router: { workspaceSlug, projectId, cycleId, moduleId },
|
||||
} = useApplication();
|
||||
const { workspaceProjectIds } = useProject();
|
||||
const { fetchCycleDetails } = useCycle();
|
||||
const { fetchModuleDetails } = useModule();
|
||||
const { issues: projectIssues } = useIssues(EIssuesStoreType.PROJECT);
|
||||
const { issues: moduleIssues } = useIssues(EIssuesStoreType.MODULE);
|
||||
const { issues: cycleIssues } = useIssues(EIssuesStoreType.CYCLE);
|
||||
const { issues: viewIssues } = useIssues(EIssuesStoreType.PROJECT_VIEW);
|
||||
const { issues: profileIssues } = useIssues(EIssuesStoreType.PROFILE);
|
||||
const { issues: draftIssues } = useIssues(EIssuesStoreType.DRAFT);
|
||||
const { fetchIssue } = useIssueDetail();
|
||||
// store mapping based on current store
|
||||
const issueStores = {
|
||||
[EIssuesStoreType.PROJECT]: {
|
||||
store: projectIssues,
|
||||
viewId: undefined,
|
||||
},
|
||||
[EIssuesStoreType.PROJECT_VIEW]: {
|
||||
store: viewIssues,
|
||||
viewId: projectViewId,
|
||||
},
|
||||
[EIssuesStoreType.PROFILE]: {
|
||||
store: profileIssues,
|
||||
viewId: undefined,
|
||||
},
|
||||
[EIssuesStoreType.CYCLE]: {
|
||||
store: cycleIssues,
|
||||
viewId: cycleId,
|
||||
},
|
||||
[EIssuesStoreType.MODULE]: {
|
||||
store: moduleIssues,
|
||||
viewId: moduleId,
|
||||
},
|
||||
};
|
||||
// router
|
||||
const router = useRouter();
|
||||
// local storage
|
||||
@ -95,7 +70,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
|
||||
Record<string, Partial<TIssue>>
|
||||
>("draftedIssue", {});
|
||||
// current store details
|
||||
const { store: currentIssueStore, viewId } = issueStores[storeType];
|
||||
const { createIssue, updateIssue } = useIssuesActions(storeType);
|
||||
|
||||
const fetchIssueDetail = async (issueId: string | undefined) => {
|
||||
if (!workspaceSlug) return;
|
||||
@ -176,11 +151,9 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
|
||||
try {
|
||||
const response = is_draft_issue
|
||||
? await draftIssues.createIssue(workspaceSlug, payload.project_id, payload)
|
||||
: await currentIssueStore.createIssue(workspaceSlug, payload.project_id, payload, viewId);
|
||||
: createIssue && (await createIssue(payload.project_id, payload));
|
||||
if (!response) throw new Error();
|
||||
|
||||
currentIssueStore.fetchIssues(workspaceSlug, payload.project_id, "mutation", viewId);
|
||||
|
||||
if (payload.cycle_id && payload.cycle_id !== "" && storeType !== EIssuesStoreType.CYCLE)
|
||||
await addIssueToCycle(response, payload.cycle_id);
|
||||
if (payload.module_ids && payload.module_ids.length > 0 && storeType !== EIssuesStoreType.MODULE)
|
||||
@ -217,7 +190,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
|
||||
try {
|
||||
isDraft
|
||||
? await draftIssues.updateIssue(workspaceSlug, payload.project_id, data.id, payload)
|
||||
: await currentIssueStore.updateIssue(workspaceSlug, payload.project_id, data.id, payload, viewId);
|
||||
: updateIssue && (await updateIssue(payload.project_id, data.id, payload));
|
||||
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
@ -234,7 +207,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Error!",
|
||||
message: "Issue could not be created. Please try again.",
|
||||
message: "Issue could not be updated. Please try again.",
|
||||
});
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
@ -244,13 +217,8 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
|
||||
}
|
||||
};
|
||||
|
||||
const handleFormSubmit = async (formData: Partial<TIssue>, is_draft_issue: boolean = false) => {
|
||||
if (!workspaceSlug || !formData.project_id || !storeType) return;
|
||||
|
||||
const payload: Partial<TIssue> = {
|
||||
...formData,
|
||||
description_html: formData.description_html ?? "<p></p>",
|
||||
};
|
||||
const handleFormSubmit = async (payload: Partial<TIssue>, is_draft_issue: boolean = false) => {
|
||||
if (!workspaceSlug || !payload.project_id || !storeType) return;
|
||||
|
||||
let response: TIssue | undefined = undefined;
|
||||
if (!data?.id) response = await handleCreateIssue(payload, is_draft_issue);
|
||||
|
@ -234,10 +234,10 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
||||
message: () => "Cycle remove from issue failed",
|
||||
},
|
||||
});
|
||||
const response = await removeFromCyclePromise;
|
||||
await removeFromCyclePromise;
|
||||
captureIssueEvent({
|
||||
eventName: ISSUE_UPDATED,
|
||||
payload: { ...response, state: "SUCCESS", element: "Issue peek-overview" },
|
||||
payload: { issueId, state: "SUCCESS", element: "Issue peek-overview" },
|
||||
updates: {
|
||||
changed_property: "cycle_id",
|
||||
change_details: "",
|
||||
|
@ -46,24 +46,24 @@ export const ProfileActivity = observer(() => {
|
||||
{userProfileActivity.results.map((activity) => (
|
||||
<div key={activity.id} className="flex gap-3">
|
||||
<div className="flex-shrink-0">
|
||||
{activity.actor_detail.avatar && activity.actor_detail.avatar !== "" ? (
|
||||
{activity.actor_detail?.avatar && activity.actor_detail?.avatar !== "" ? (
|
||||
<img
|
||||
src={activity.actor_detail.avatar}
|
||||
alt={activity.actor_detail.display_name}
|
||||
src={activity.actor_detail?.avatar}
|
||||
alt={activity.actor_detail?.display_name}
|
||||
height={24}
|
||||
width={24}
|
||||
className="rounded"
|
||||
/>
|
||||
) : (
|
||||
<div className="grid h-6 w-6 place-items-center rounded border-2 bg-gray-700 text-xs text-white">
|
||||
{activity.actor_detail.display_name?.charAt(0)}
|
||||
{activity.actor_detail?.display_name?.charAt(0)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="-mt-1 w-4/5 break-words">
|
||||
<p className="text-sm text-custom-text-200">
|
||||
<span className="font-medium text-custom-text-100">
|
||||
{currentUser?.id === activity.actor_detail.id ? "You" : activity.actor_detail.display_name}{" "}
|
||||
{currentUser?.id === activity.actor_detail?.id ? "You" : activity.actor_detail?.display_name}{" "}
|
||||
</span>
|
||||
{activity.field ? (
|
||||
<ActivityMessage activity={activity} showIssue />
|
||||
|
@ -96,21 +96,22 @@ export const ProfileSidebar = observer(() => {
|
||||
)}
|
||||
<img
|
||||
src={
|
||||
userProjectsData.user_data.cover_image ?? "https://images.unsplash.com/photo-1506383796573-caf02b4a79ab"
|
||||
userProjectsData.user_data?.cover_image ??
|
||||
"https://images.unsplash.com/photo-1506383796573-caf02b4a79ab"
|
||||
}
|
||||
alt={userProjectsData.user_data.display_name}
|
||||
alt={userProjectsData.user_data?.display_name}
|
||||
className="h-32 w-full object-cover"
|
||||
/>
|
||||
<div className="absolute -bottom-[26px] left-5 h-[52px] w-[52px] rounded">
|
||||
{userProjectsData.user_data.avatar && userProjectsData.user_data.avatar !== "" ? (
|
||||
{userProjectsData.user_data?.avatar && userProjectsData.user_data?.avatar !== "" ? (
|
||||
<img
|
||||
src={userProjectsData.user_data.avatar}
|
||||
alt={userProjectsData.user_data.display_name}
|
||||
src={userProjectsData.user_data?.avatar}
|
||||
alt={userProjectsData.user_data?.display_name}
|
||||
className="h-full w-full rounded object-cover"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex h-[52px] w-[52px] items-center justify-center rounded bg-custom-background-90 capitalize text-custom-text-100">
|
||||
{userProjectsData.user_data.first_name?.[0]}
|
||||
{userProjectsData.user_data?.first_name?.[0]}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@ -118,9 +119,9 @@ export const ProfileSidebar = observer(() => {
|
||||
<div className="px-5">
|
||||
<div className="mt-[38px]">
|
||||
<h4 className="text-lg font-semibold">
|
||||
{userProjectsData.user_data.first_name} {userProjectsData.user_data.last_name}
|
||||
{userProjectsData.user_data?.first_name} {userProjectsData.user_data?.last_name}
|
||||
</h4>
|
||||
<h6 className="text-sm text-custom-text-200">({userProjectsData.user_data.display_name})</h6>
|
||||
<h6 className="text-sm text-custom-text-200">({userProjectsData.user_data?.display_name})</h6>
|
||||
</div>
|
||||
<div className="mt-6 space-y-5">
|
||||
{userDetails.map((detail) => (
|
||||
|
@ -44,7 +44,7 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
|
||||
const handleRemove = async () => {
|
||||
if (!workspaceSlug || !projectId || !userDetails) return;
|
||||
|
||||
if (userDetails.member.id === currentUser?.id) {
|
||||
if (userDetails.member?.id === currentUser?.id) {
|
||||
await leaveProject(workspaceSlug.toString(), projectId.toString())
|
||||
.then(async () => {
|
||||
captureEvent(PROJECT_MEMBER_LEAVE, {
|
||||
@ -62,7 +62,7 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
|
||||
})
|
||||
);
|
||||
} else
|
||||
await removeMemberFromProject(workspaceSlug.toString(), projectId.toString(), userDetails.member.id).catch(
|
||||
await removeMemberFromProject(workspaceSlug.toString(), projectId.toString(), userDetails.member?.id).catch(
|
||||
(err) =>
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
@ -84,12 +84,12 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
|
||||
/>
|
||||
<div className="group flex items-center justify-between px-3 py-4 hover:bg-custom-background-90">
|
||||
<div className="flex items-center gap-x-4 gap-y-2">
|
||||
{userDetails.member.avatar && userDetails.member.avatar !== "" ? (
|
||||
<Link href={`/${workspaceSlug}/profile/${userDetails.member.id}`}>
|
||||
{userDetails.member?.avatar && userDetails.member?.avatar !== "" ? (
|
||||
<Link href={`/${workspaceSlug}/profile/${userDetails.member?.id}`}>
|
||||
<span className="relative flex h-10 w-10 items-center justify-center rounded p-4 capitalize text-white">
|
||||
<img
|
||||
src={userDetails.member.avatar}
|
||||
alt={userDetails.member.display_name || userDetails.member.email}
|
||||
src={userDetails.member?.avatar}
|
||||
alt={userDetails.member?.display_name || userDetails.member?.email}
|
||||
className="absolute left-0 top-0 h-full w-full rounded object-cover"
|
||||
/>
|
||||
</span>
|
||||
@ -97,23 +97,23 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
|
||||
) : (
|
||||
<Link href={`/${workspaceSlug}/profile/${userDetails.id}`}>
|
||||
<span className="relative flex h-10 w-10 items-center justify-center rounded bg-gray-700 p-4 capitalize text-white">
|
||||
{(userDetails.member.display_name ?? userDetails.member.email ?? "?")[0]}
|
||||
{(userDetails.member?.display_name ?? userDetails.member?.email ?? "?")[0]}
|
||||
</span>
|
||||
</Link>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<Link href={`/${workspaceSlug}/profile/${userDetails.member.id}`}>
|
||||
<Link href={`/${workspaceSlug}/profile/${userDetails.member?.id}`}>
|
||||
<span className="text-sm font-medium">
|
||||
{userDetails.member.first_name} {userDetails.member.last_name}
|
||||
{userDetails.member?.first_name} {userDetails.member?.last_name}
|
||||
</span>
|
||||
</Link>
|
||||
<div className="flex items-center">
|
||||
<p className="text-xs text-custom-text-300">{userDetails.member.display_name}</p>
|
||||
<p className="text-xs text-custom-text-300">{userDetails.member?.display_name}</p>
|
||||
{isAdmin && (
|
||||
<>
|
||||
<Dot height={16} width={16} className="text-custom-text-300" />
|
||||
<p className="text-xs text-custom-text-300">{userDetails.member.email}</p>
|
||||
<p className="text-xs text-custom-text-300">{userDetails.member?.email}</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
@ -126,12 +126,12 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
|
||||
<div className="item-center flex gap-1 rounded px-2 py-0.5">
|
||||
<span
|
||||
className={`flex items-center rounded text-xs font-medium ${
|
||||
userDetails.member.id !== currentUser?.id ? "" : "text-custom-text-400"
|
||||
userDetails.member?.id !== currentUser?.id ? "" : "text-custom-text-400"
|
||||
}`}
|
||||
>
|
||||
{ROLE[userDetails.role]}
|
||||
</span>
|
||||
{userDetails.member.id !== currentUser?.id && (
|
||||
{userDetails.member?.id !== currentUser?.id && (
|
||||
<span className="grid place-items-center">
|
||||
<ChevronDown className="h-3 w-3" />
|
||||
</span>
|
||||
@ -142,7 +142,7 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
|
||||
onChange={(value: EUserProjectRoles) => {
|
||||
if (!workspaceSlug || !projectId) return;
|
||||
|
||||
updateMember(workspaceSlug.toString(), projectId.toString(), userDetails.member.id, {
|
||||
updateMember(workspaceSlug.toString(), projectId.toString(), userDetails.member?.id, {
|
||||
role: value,
|
||||
}).catch((err) => {
|
||||
const error = err.error;
|
||||
@ -156,7 +156,7 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
|
||||
});
|
||||
}}
|
||||
disabled={
|
||||
userDetails.member.id === currentUser?.id || !currentProjectRole || currentProjectRole < userDetails.role
|
||||
userDetails.member?.id === currentUser?.id || !currentProjectRole || currentProjectRole < userDetails.role
|
||||
}
|
||||
placement="bottom-end"
|
||||
>
|
||||
@ -170,8 +170,8 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
|
||||
);
|
||||
})}
|
||||
</CustomSelect>
|
||||
{(isAdmin || userDetails.member.id === currentUser?.id) && (
|
||||
<Tooltip tooltipContent={userDetails.member.id === currentUser?.id ? "Leave project" : "Remove member"}>
|
||||
{(isAdmin || userDetails.member?.id === currentUser?.id) && (
|
||||
<Tooltip tooltipContent={userDetails.member?.id === currentUser?.id ? "Leave project" : "Remove member"}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setRemoveMemberModal(true)}
|
||||
|
@ -50,9 +50,9 @@ export const MemberSelect: React.FC<Props> = observer((props) => {
|
||||
value={value}
|
||||
label={
|
||||
<div className="flex items-center gap-2">
|
||||
{selectedOption && <Avatar name={selectedOption.member.display_name} src={selectedOption.member.avatar} />}
|
||||
{selectedOption && <Avatar name={selectedOption.member?.display_name} src={selectedOption.member?.avatar} />}
|
||||
{selectedOption ? (
|
||||
selectedOption.member.display_name
|
||||
selectedOption.member?.display_name
|
||||
) : (
|
||||
<div className="flex items-center gap-2">
|
||||
<Ban className="h-3.5 w-3.5 rotate-90 text-custom-sidebar-text-400" />
|
||||
|
@ -11,7 +11,7 @@ type Props = {
|
||||
export const ProjectLogo: React.FC<Props> = (props) => {
|
||||
const { className, logo } = props;
|
||||
|
||||
if (logo.in_use === "icon" && logo.icon)
|
||||
if (logo && logo.in_use === "icon" && logo.icon)
|
||||
return (
|
||||
<span
|
||||
style={{
|
||||
@ -23,7 +23,7 @@ export const ProjectLogo: React.FC<Props> = (props) => {
|
||||
</span>
|
||||
);
|
||||
|
||||
if (logo.in_use === "emoji" && logo.emoji)
|
||||
if (logo && logo.in_use === "emoji" && logo.emoji)
|
||||
return (
|
||||
<span className={cn("text-base", className)}>
|
||||
{logo.emoji.value?.split("-").map((emoji) => String.fromCodePoint(parseInt(emoji, 10)))}
|
||||
|
@ -135,27 +135,36 @@ export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
|
||||
}
|
||||
}, [fields, append]);
|
||||
|
||||
const options = uninvitedPeople?.map((userId) => {
|
||||
const memberDetails = getWorkspaceMemberDetails(userId);
|
||||
const options = uninvitedPeople
|
||||
?.map((userId) => {
|
||||
const memberDetails = getWorkspaceMemberDetails(userId);
|
||||
|
||||
return {
|
||||
value: `${memberDetails?.member.id}`,
|
||||
query: `${memberDetails?.member.first_name} ${
|
||||
memberDetails?.member.last_name
|
||||
} ${memberDetails?.member.display_name.toLowerCase()}`,
|
||||
content: (
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<div className="flex-shrink-0 pt-0.5">
|
||||
<Avatar name={memberDetails?.member.display_name} src={memberDetails?.member.avatar} />
|
||||
if (!memberDetails?.member) return;
|
||||
return {
|
||||
value: `${memberDetails?.member.id}`,
|
||||
query: `${memberDetails?.member.first_name} ${
|
||||
memberDetails?.member.last_name
|
||||
} ${memberDetails?.member.display_name.toLowerCase()}`,
|
||||
content: (
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<div className="flex-shrink-0 pt-0.5">
|
||||
<Avatar name={memberDetails?.member.display_name} src={memberDetails?.member.avatar} />
|
||||
</div>
|
||||
<div className="truncate">
|
||||
{memberDetails?.member.display_name} (
|
||||
{memberDetails?.member.first_name + " " + memberDetails?.member.last_name})
|
||||
</div>
|
||||
</div>
|
||||
<div className="truncate">
|
||||
{memberDetails?.member.display_name} (
|
||||
{memberDetails?.member.first_name + " " + memberDetails?.member.last_name})
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
});
|
||||
),
|
||||
};
|
||||
})
|
||||
.filter((option) => !!option) as
|
||||
| {
|
||||
value: string;
|
||||
query: string;
|
||||
content: React.JSX.Element;
|
||||
}[]
|
||||
| undefined;
|
||||
|
||||
return (
|
||||
<Transition.Root show={isOpen} as={React.Fragment}>
|
||||
@ -204,6 +213,8 @@ export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
|
||||
render={({ field: { value, onChange } }) => {
|
||||
const selectedMember = getWorkspaceMemberDetails(value);
|
||||
|
||||
if (!selectedMember?.member) return <></>;
|
||||
|
||||
return (
|
||||
<CustomSearchSelect
|
||||
value={value}
|
||||
|
@ -73,7 +73,7 @@ export const ConfirmWorkspaceMemberRemove: React.FC<Props> = observer((props) =>
|
||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-custom-text-100">
|
||||
{currentUser?.id === userDetails.id
|
||||
? "Leave workspace?"
|
||||
: `Remove ${userDetails.display_name}?`}
|
||||
: `Remove ${userDetails?.display_name}?`}
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
{currentUser?.id === userDetails.id ? (
|
||||
@ -84,7 +84,7 @@ export const ConfirmWorkspaceMemberRemove: React.FC<Props> = observer((props) =>
|
||||
) : (
|
||||
<p className="text-sm text-custom-text-200">
|
||||
Are you sure you want to remove member-{" "}
|
||||
<span className="font-bold">{userDetails.display_name}</span>? They will no longer have
|
||||
<span className="font-bold">{userDetails?.display_name}</span>? They will no longer have
|
||||
access to this workspace. This action cannot be undone.
|
||||
</p>
|
||||
)}
|
||||
|
@ -171,3 +171,20 @@ export const renderIssueBlocksStructure = (blocks: TIssue[]): IGanttBlock[] =>
|
||||
start_date: block.start_date ? new Date(block.start_date) : null,
|
||||
target_date: block.target_date ? new Date(block.target_date) : null,
|
||||
}));
|
||||
|
||||
|
||||
export function getChangedIssuefields(
|
||||
formData: Partial<TIssue>,
|
||||
dirtyFields: { [key: string]: boolean | undefined }
|
||||
) {
|
||||
const changedFields: Partial<TIssue> = {};
|
||||
|
||||
const dirtyFieldKeys = Object.keys(dirtyFields) as (keyof TIssue)[];
|
||||
for (const dirtyField of dirtyFieldKeys) {
|
||||
if (!!dirtyFields[dirtyField]) {
|
||||
changedFields[dirtyField] = formData[dirtyField];
|
||||
}
|
||||
}
|
||||
|
||||
return changedFields;
|
||||
}
|
576
web/hooks/use-issues-actions.tsx
Normal file
576
web/hooks/use-issues-actions.tsx
Normal file
@ -0,0 +1,576 @@
|
||||
import { EIssueFilterType, EIssuesStoreType } from "constants/issue";
|
||||
import { useApplication, useIssues } from "./store";
|
||||
import {
|
||||
IIssueDisplayFilterOptions,
|
||||
IIssueDisplayProperties,
|
||||
IIssueFilterOptions,
|
||||
TIssue,
|
||||
TIssueKanbanFilters,
|
||||
TLoader,
|
||||
} from "@plane/types";
|
||||
import { useCallback, useMemo } from "react";
|
||||
|
||||
interface IssueActions {
|
||||
fetchIssues?: (projectId: string, loadType: TLoader) => Promise<TIssue[] | undefined>;
|
||||
removeIssue: (projectId: string, issueId: string) => Promise<void>;
|
||||
createIssue?: (projectId: string, data: Partial<TIssue>) => Promise<TIssue | undefined>;
|
||||
updateIssue?: (projectId: string, issueId: string, data: Partial<TIssue>) => Promise<void>;
|
||||
removeIssueFromView?: (projectId: string, issueId: string) => Promise<void>;
|
||||
archiveIssue?: (projectId: string, issueId: string) => Promise<void>;
|
||||
restoreIssue?: (projectId: string, issueId: string) => Promise<void>;
|
||||
updateFilters: (
|
||||
projectId: string,
|
||||
filterType: EIssueFilterType,
|
||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters
|
||||
) => Promise<void>;
|
||||
}
|
||||
|
||||
export const useIssuesActions = (storeType: EIssuesStoreType): IssueActions => {
|
||||
const projectIssueActions = useProjectIssueActions();
|
||||
const cycleIssueActions = useCycleIssueActions();
|
||||
const moduleIssueActions = useModuleIssueActions();
|
||||
const profileIssueActions = useProfileIssueActions();
|
||||
const projectViewIssueActions = useProjectViewIssueActions();
|
||||
const draftIssueActions = useDraftIssueActions();
|
||||
const archivedIssueActions = useArchivedIssueActions();
|
||||
const globalIssueActions = useGlobalIssueActions();
|
||||
|
||||
switch (storeType) {
|
||||
case EIssuesStoreType.PROJECT_VIEW:
|
||||
return projectViewIssueActions;
|
||||
case EIssuesStoreType.PROFILE:
|
||||
return profileIssueActions;
|
||||
case EIssuesStoreType.CYCLE:
|
||||
return cycleIssueActions;
|
||||
case EIssuesStoreType.MODULE:
|
||||
return moduleIssueActions;
|
||||
case EIssuesStoreType.ARCHIVED:
|
||||
return archivedIssueActions;
|
||||
case EIssuesStoreType.DRAFT:
|
||||
return draftIssueActions;
|
||||
case EIssuesStoreType.GLOBAL:
|
||||
return globalIssueActions;
|
||||
case EIssuesStoreType.PROJECT:
|
||||
default:
|
||||
return projectIssueActions;
|
||||
}
|
||||
};
|
||||
|
||||
const useProjectIssueActions = () => {
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT);
|
||||
|
||||
const {
|
||||
router: { workspaceSlug },
|
||||
} = useApplication();
|
||||
|
||||
const fetchIssues = useCallback(
|
||||
async (projectId: string, loadType: TLoader) => {
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.fetchIssues(workspaceSlug, projectId, loadType);
|
||||
},
|
||||
[issues.fetchIssues, workspaceSlug]
|
||||
);
|
||||
const createIssue = useCallback(
|
||||
async (projectId: string, data: Partial<TIssue>) => {
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.createIssue(workspaceSlug, projectId, data);
|
||||
},
|
||||
[issues.createIssue, workspaceSlug]
|
||||
);
|
||||
const updateIssue = useCallback(
|
||||
async (projectId: string, issueId: string, data: Partial<TIssue>) => {
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.updateIssue(workspaceSlug, projectId, issueId, data);
|
||||
},
|
||||
[issues.updateIssue, workspaceSlug]
|
||||
);
|
||||
const removeIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.removeIssue(workspaceSlug, projectId, issueId);
|
||||
},
|
||||
[issues.removeIssue, workspaceSlug]
|
||||
);
|
||||
const archiveIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.archiveIssue(workspaceSlug, projectId, issueId);
|
||||
},
|
||||
[issues.archiveIssue, 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, workspaceSlug]
|
||||
);
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
fetchIssues,
|
||||
createIssue,
|
||||
updateIssue,
|
||||
removeIssue,
|
||||
archiveIssue,
|
||||
updateFilters,
|
||||
}),
|
||||
[fetchIssues, createIssue, updateIssue, removeIssue, archiveIssue, updateFilters]
|
||||
);
|
||||
};
|
||||
|
||||
const useCycleIssueActions = () => {
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.CYCLE);
|
||||
|
||||
const {
|
||||
router: { workspaceSlug, cycleId },
|
||||
} = useApplication();
|
||||
|
||||
const fetchIssues = useCallback(
|
||||
async (projectId: string, loadType: TLoader) => {
|
||||
if (!cycleId || !workspaceSlug) return;
|
||||
return await issues.fetchIssues(workspaceSlug, projectId, loadType, cycleId);
|
||||
},
|
||||
[issues.fetchIssues, cycleId, workspaceSlug]
|
||||
);
|
||||
const createIssue = useCallback(
|
||||
async (projectId: string, data: Partial<TIssue>) => {
|
||||
if (!cycleId || !workspaceSlug) return;
|
||||
return await issues.createIssue(workspaceSlug, projectId, data, cycleId);
|
||||
},
|
||||
[issues.createIssue, cycleId, workspaceSlug]
|
||||
);
|
||||
const updateIssue = useCallback(
|
||||
async (projectId: string, issueId: string, data: Partial<TIssue>) => {
|
||||
if (!cycleId || !workspaceSlug) return;
|
||||
return await issues.updateIssue(workspaceSlug, projectId, issueId, data, cycleId);
|
||||
},
|
||||
[issues.updateIssue, cycleId, workspaceSlug]
|
||||
);
|
||||
const removeIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!cycleId || !workspaceSlug) return;
|
||||
return await issues.removeIssue(workspaceSlug, projectId, issueId, cycleId);
|
||||
},
|
||||
[issues.removeIssue, cycleId, workspaceSlug]
|
||||
);
|
||||
const removeIssueFromView = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!cycleId || !workspaceSlug) return;
|
||||
return await issues.removeIssueFromCycle(workspaceSlug, projectId, cycleId, issueId);
|
||||
},
|
||||
[issues.removeIssueFromCycle, cycleId, workspaceSlug]
|
||||
);
|
||||
const archiveIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!cycleId || !workspaceSlug) return;
|
||||
return await issues.archiveIssue(workspaceSlug, projectId, issueId, cycleId);
|
||||
},
|
||||
[issues.archiveIssue, cycleId, workspaceSlug]
|
||||
);
|
||||
|
||||
const updateFilters = useCallback(
|
||||
async (
|
||||
projectId: string,
|
||||
filterType: EIssueFilterType,
|
||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters
|
||||
) => {
|
||||
if (!cycleId || !workspaceSlug) return;
|
||||
return await issuesFilter.updateFilters(workspaceSlug, projectId, filterType, filters, cycleId);
|
||||
},
|
||||
[issuesFilter.updateFilters, cycleId, workspaceSlug]
|
||||
);
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
fetchIssues,
|
||||
createIssue,
|
||||
updateIssue,
|
||||
removeIssue,
|
||||
removeIssueFromView,
|
||||
archiveIssue,
|
||||
updateFilters,
|
||||
}),
|
||||
[fetchIssues, createIssue, updateIssue, removeIssue, removeIssueFromView, archiveIssue, updateFilters]
|
||||
);
|
||||
};
|
||||
|
||||
const useModuleIssueActions = () => {
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.MODULE);
|
||||
|
||||
const {
|
||||
router: { workspaceSlug, moduleId },
|
||||
} = useApplication();
|
||||
|
||||
const fetchIssues = useCallback(
|
||||
async (projectId: string, loadType: TLoader) => {
|
||||
if (!moduleId || !workspaceSlug) return;
|
||||
return await issues.fetchIssues(workspaceSlug, projectId, loadType, moduleId);
|
||||
},
|
||||
[issues.fetchIssues, moduleId, workspaceSlug]
|
||||
);
|
||||
const createIssue = useCallback(
|
||||
async (projectId: string, data: Partial<TIssue>) => {
|
||||
if (!moduleId || !workspaceSlug) return;
|
||||
return await issues.createIssue(workspaceSlug, projectId, data, moduleId);
|
||||
},
|
||||
[issues.createIssue, moduleId, workspaceSlug]
|
||||
);
|
||||
const updateIssue = useCallback(
|
||||
async (projectId: string, issueId: string, data: Partial<TIssue>) => {
|
||||
if (!moduleId || !workspaceSlug) return;
|
||||
return await issues.updateIssue(workspaceSlug, projectId, issueId, data, moduleId);
|
||||
},
|
||||
[issues.updateIssue, moduleId, workspaceSlug]
|
||||
);
|
||||
const removeIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!moduleId || !workspaceSlug) return;
|
||||
return await issues.removeIssue(workspaceSlug, projectId, issueId, moduleId);
|
||||
},
|
||||
[issues.removeIssue, moduleId, workspaceSlug]
|
||||
);
|
||||
const removeIssueFromView = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!moduleId || !workspaceSlug) return;
|
||||
return await issues.removeIssueFromModule(workspaceSlug, projectId, moduleId, issueId);
|
||||
},
|
||||
[issues.removeIssueFromModule, moduleId, workspaceSlug]
|
||||
);
|
||||
const archiveIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!moduleId || !workspaceSlug) return;
|
||||
return await issues.archiveIssue(workspaceSlug, projectId, issueId, moduleId);
|
||||
},
|
||||
[issues.archiveIssue, moduleId, workspaceSlug]
|
||||
);
|
||||
|
||||
const updateFilters = useCallback(
|
||||
async (
|
||||
projectId: string,
|
||||
filterType: EIssueFilterType,
|
||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters
|
||||
) => {
|
||||
if (!moduleId || !workspaceSlug) return;
|
||||
return await issuesFilter.updateFilters(workspaceSlug, projectId, filterType, filters, moduleId);
|
||||
},
|
||||
[issuesFilter.updateFilters, moduleId]
|
||||
);
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
fetchIssues,
|
||||
createIssue,
|
||||
updateIssue,
|
||||
removeIssue,
|
||||
removeIssueFromView,
|
||||
archiveIssue,
|
||||
updateFilters,
|
||||
}),
|
||||
[fetchIssues, createIssue, updateIssue, removeIssue, removeIssueFromView, archiveIssue, updateFilters]
|
||||
);
|
||||
};
|
||||
|
||||
const useProfileIssueActions = () => {
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROFILE);
|
||||
|
||||
const {
|
||||
router: { workspaceSlug, userId },
|
||||
} = useApplication();
|
||||
|
||||
const fetchIssues = useCallback(
|
||||
async (projectId: string, loadType: TLoader) => {
|
||||
if (!userId || !workspaceSlug) return;
|
||||
return await issues.fetchIssues(workspaceSlug, projectId, loadType, userId);
|
||||
},
|
||||
[issues.fetchIssues, userId, workspaceSlug]
|
||||
);
|
||||
const createIssue = useCallback(
|
||||
async (projectId: string, data: Partial<TIssue>) => {
|
||||
if (!userId || !workspaceSlug) return;
|
||||
return await issues.createIssue(workspaceSlug, projectId, data, userId);
|
||||
},
|
||||
[issues.createIssue, userId, workspaceSlug]
|
||||
);
|
||||
const updateIssue = useCallback(
|
||||
async (projectId: string, issueId: string, data: Partial<TIssue>) => {
|
||||
if (!userId || !workspaceSlug) return;
|
||||
return await issues.updateIssue(workspaceSlug, projectId, issueId, data, userId);
|
||||
},
|
||||
[issues.updateIssue, userId, workspaceSlug]
|
||||
);
|
||||
const removeIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!userId || !workspaceSlug) return;
|
||||
return await issues.removeIssue(workspaceSlug, projectId, issueId, userId);
|
||||
},
|
||||
[issues.removeIssue, userId, workspaceSlug]
|
||||
);
|
||||
const archiveIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!userId || !workspaceSlug) return;
|
||||
return await issues.archiveIssue(workspaceSlug, projectId, issueId, userId);
|
||||
},
|
||||
[issues.archiveIssue, userId, workspaceSlug]
|
||||
);
|
||||
|
||||
const updateFilters = useCallback(
|
||||
async (
|
||||
projectId: string,
|
||||
filterType: EIssueFilterType,
|
||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters
|
||||
) => {
|
||||
if (!userId || !workspaceSlug) return;
|
||||
return await issuesFilter.updateFilters(workspaceSlug, projectId, filterType, filters, userId);
|
||||
},
|
||||
[issuesFilter.updateFilters, userId, workspaceSlug]
|
||||
);
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
fetchIssues,
|
||||
createIssue,
|
||||
updateIssue,
|
||||
removeIssue,
|
||||
archiveIssue,
|
||||
updateFilters,
|
||||
}),
|
||||
[fetchIssues, createIssue, updateIssue, removeIssue, archiveIssue, updateFilters]
|
||||
);
|
||||
};
|
||||
|
||||
const useProjectViewIssueActions = () => {
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.PROJECT_VIEW);
|
||||
|
||||
const {
|
||||
router: { workspaceSlug, viewId },
|
||||
} = useApplication();
|
||||
|
||||
const fetchIssues = useCallback(
|
||||
async (projectId: string, loadType: TLoader) => {
|
||||
if (!viewId || !workspaceSlug) return;
|
||||
return await issues.fetchIssues(workspaceSlug, projectId, loadType, viewId);
|
||||
},
|
||||
[issues.fetchIssues, viewId, workspaceSlug]
|
||||
);
|
||||
const createIssue = useCallback(
|
||||
async (projectId: string, data: Partial<TIssue>) => {
|
||||
if (!viewId || !workspaceSlug) return;
|
||||
return await issues.createIssue(workspaceSlug, projectId, data, viewId);
|
||||
},
|
||||
[issues.createIssue, viewId, workspaceSlug]
|
||||
);
|
||||
const updateIssue = useCallback(
|
||||
async (projectId: string, issueId: string, data: Partial<TIssue>) => {
|
||||
if (!viewId || !workspaceSlug) return;
|
||||
return await issues.updateIssue(workspaceSlug, projectId, issueId, data, viewId);
|
||||
},
|
||||
[issues.updateIssue, viewId, workspaceSlug]
|
||||
);
|
||||
const removeIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!viewId || !workspaceSlug) return;
|
||||
return await issues.removeIssue(workspaceSlug, projectId, issueId, viewId);
|
||||
},
|
||||
[issues.removeIssue, viewId, workspaceSlug]
|
||||
);
|
||||
const archiveIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!viewId || !workspaceSlug) return;
|
||||
return await issues.archiveIssue(workspaceSlug, projectId, issueId, viewId);
|
||||
},
|
||||
[issues.archiveIssue, viewId, workspaceSlug]
|
||||
);
|
||||
|
||||
const updateFilters = useCallback(
|
||||
async (
|
||||
projectId: string,
|
||||
filterType: EIssueFilterType,
|
||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters
|
||||
) => {
|
||||
if (!viewId || !workspaceSlug) return;
|
||||
return await issuesFilter.updateFilters(workspaceSlug, projectId, filterType, filters, viewId);
|
||||
},
|
||||
[issuesFilter.updateFilters, viewId, workspaceSlug]
|
||||
);
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
fetchIssues,
|
||||
createIssue,
|
||||
updateIssue,
|
||||
removeIssue,
|
||||
archiveIssue,
|
||||
updateFilters,
|
||||
}),
|
||||
[fetchIssues, createIssue, updateIssue, removeIssue, archiveIssue, updateFilters]
|
||||
);
|
||||
};
|
||||
|
||||
const useDraftIssueActions = () => {
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.DRAFT);
|
||||
|
||||
const {
|
||||
router: { workspaceSlug },
|
||||
} = useApplication();
|
||||
|
||||
const fetchIssues = useCallback(
|
||||
async (projectId: string, loadType: TLoader) => {
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.fetchIssues(workspaceSlug, projectId, loadType);
|
||||
},
|
||||
[issues.fetchIssues, workspaceSlug]
|
||||
);
|
||||
const createIssue = useCallback(
|
||||
async (projectId: string, data: Partial<TIssue>) => {
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.createIssue(workspaceSlug, projectId, data);
|
||||
},
|
||||
[issues.createIssue, workspaceSlug]
|
||||
);
|
||||
const updateIssue = useCallback(
|
||||
async (projectId: string, issueId: string, data: Partial<TIssue>) => {
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.updateIssue(workspaceSlug, projectId, issueId, data);
|
||||
},
|
||||
[issues.updateIssue, workspaceSlug]
|
||||
);
|
||||
const removeIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!workspaceSlug) 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,
|
||||
createIssue,
|
||||
updateIssue,
|
||||
removeIssue,
|
||||
updateFilters,
|
||||
}),
|
||||
[fetchIssues, createIssue, updateIssue, removeIssue, updateFilters]
|
||||
);
|
||||
};
|
||||
|
||||
const useArchivedIssueActions = () => {
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.ARCHIVED);
|
||||
|
||||
const {
|
||||
router: { workspaceSlug },
|
||||
} = useApplication();
|
||||
|
||||
const fetchIssues = useCallback(
|
||||
async (projectId: string, loadType: TLoader) => {
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.fetchIssues(workspaceSlug, projectId, loadType);
|
||||
},
|
||||
[issues.fetchIssues]
|
||||
);
|
||||
const removeIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.removeIssue(workspaceSlug, projectId, issueId);
|
||||
},
|
||||
[issues.removeIssue]
|
||||
);
|
||||
const restoreIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!workspaceSlug) return;
|
||||
return await issues.restoreIssue(workspaceSlug, projectId, issueId);
|
||||
},
|
||||
[issues.restoreIssue]
|
||||
);
|
||||
|
||||
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,
|
||||
removeIssue,
|
||||
restoreIssue,
|
||||
updateFilters,
|
||||
}),
|
||||
[fetchIssues, removeIssue, restoreIssue, updateFilters]
|
||||
);
|
||||
};
|
||||
|
||||
const useGlobalIssueActions = () => {
|
||||
const { issues, issuesFilter } = useIssues(EIssuesStoreType.GLOBAL);
|
||||
|
||||
const {
|
||||
router: { workspaceSlug, globalViewId },
|
||||
} = useApplication();
|
||||
const createIssue = useCallback(
|
||||
async (projectId: string, data: Partial<TIssue>) => {
|
||||
if (!globalViewId || !workspaceSlug) return;
|
||||
return await issues.createIssue(workspaceSlug, projectId, data, globalViewId);
|
||||
},
|
||||
[issues.createIssue, globalViewId, workspaceSlug]
|
||||
);
|
||||
const updateIssue = useCallback(
|
||||
async (projectId: string, issueId: string, data: Partial<TIssue>) => {
|
||||
if (!globalViewId || !workspaceSlug) return;
|
||||
return await issues.updateIssue(workspaceSlug, projectId, issueId, data, globalViewId);
|
||||
},
|
||||
[issues.updateIssue, globalViewId, workspaceSlug]
|
||||
);
|
||||
const removeIssue = useCallback(
|
||||
async (projectId: string, issueId: string) => {
|
||||
if (!globalViewId || !workspaceSlug) return;
|
||||
return await issues.removeIssue(workspaceSlug, projectId, issueId, globalViewId);
|
||||
},
|
||||
[issues.removeIssue, globalViewId, workspaceSlug]
|
||||
);
|
||||
|
||||
const updateFilters = useCallback(
|
||||
async (
|
||||
projectId: string,
|
||||
filterType: EIssueFilterType,
|
||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters
|
||||
) => {
|
||||
if (!globalViewId || !workspaceSlug) return;
|
||||
return await issuesFilter.updateFilters(workspaceSlug, projectId, filterType, filters, globalViewId);
|
||||
},
|
||||
[issuesFilter.updateFilters, globalViewId, workspaceSlug]
|
||||
);
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
createIssue,
|
||||
updateIssue,
|
||||
removeIssue,
|
||||
updateFilters,
|
||||
}),
|
||||
[createIssue, updateIssue, removeIssue, updateFilters]
|
||||
);
|
||||
};
|
@ -83,7 +83,7 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => {
|
||||
avatar: formData.avatar,
|
||||
cover_image: formData.cover_image,
|
||||
role: formData.role,
|
||||
display_name: formData.display_name,
|
||||
display_name: formData?.display_name,
|
||||
user_timezone: formData.user_timezone,
|
||||
};
|
||||
|
||||
@ -195,7 +195,7 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => {
|
||||
src={watch("avatar")}
|
||||
className="absolute left-0 top-0 h-full w-full rounded-lg object-cover"
|
||||
onClick={() => setIsImageUploadModalOpen(true)}
|
||||
alt={myProfile.display_name}
|
||||
alt={myProfile?.display_name}
|
||||
role="button"
|
||||
/>
|
||||
</div>
|
||||
@ -377,14 +377,14 @@ const ProfileSettingsPage: NextPageWithLayout = observer(() => {
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
ref={ref}
|
||||
hasError={Boolean(errors.display_name)}
|
||||
hasError={Boolean(errors?.display_name)}
|
||||
placeholder="Enter your display name"
|
||||
className={`w-full ${errors.display_name ? "border-red-500" : ""}`}
|
||||
className={`w-full ${errors?.display_name ? "border-red-500" : ""}`}
|
||||
maxLength={24}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
{errors.display_name && <span className="text-xs text-red-500">Please enter display name</span>}
|
||||
{errors?.display_name && <span className="text-xs text-red-500">Please enter display name</span>}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
|
@ -17,7 +17,7 @@ export interface IArchivedIssues {
|
||||
// computed
|
||||
groupedIssueIds: TGroupedIssues | TSubGroupedIssues | TUnGroupedIssues | undefined;
|
||||
// actions
|
||||
fetchIssues: (workspaceSlug: string, projectId: string, loadType: TLoader) => Promise<TIssue>;
|
||||
fetchIssues: (workspaceSlug: string, projectId: string, loadType: TLoader) => Promise<TIssue[]>;
|
||||
removeIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||
restoreIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||
quickAddIssue: undefined;
|
||||
|
@ -35,7 +35,7 @@ export interface ICycleIssuesFilter {
|
||||
projectId: string,
|
||||
filterType: EIssueFilterType,
|
||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters,
|
||||
cycleId?: string | undefined
|
||||
cycleId: string
|
||||
) => Promise<void>;
|
||||
}
|
||||
|
||||
@ -136,10 +136,9 @@ export class CycleIssuesFilter extends IssueFilterHelperStore implements ICycleI
|
||||
projectId: string,
|
||||
type: EIssueFilterType,
|
||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters,
|
||||
cycleId: string | undefined = undefined
|
||||
cycleId: string
|
||||
) => {
|
||||
try {
|
||||
if (!cycleId) throw new Error("Cycle id is required");
|
||||
if (isEmpty(this.filters) || isEmpty(this.filters[cycleId]) || isEmpty(filters)) return;
|
||||
|
||||
const _filters = {
|
||||
|
@ -27,33 +27,23 @@ export interface ICycleIssues {
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader,
|
||||
cycleId?: string | undefined
|
||||
cycleId: string
|
||||
) => Promise<TIssue[] | undefined>;
|
||||
createIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: Partial<TIssue>,
|
||||
cycleId?: string | undefined
|
||||
cycleId: string
|
||||
) => Promise<TIssue | undefined>;
|
||||
updateIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
data: Partial<TIssue>,
|
||||
cycleId?: string | undefined
|
||||
) => Promise<void>;
|
||||
removeIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
cycleId?: string | undefined
|
||||
) => Promise<void>;
|
||||
archiveIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
cycleId?: string | undefined
|
||||
cycleId: string
|
||||
) => Promise<void>;
|
||||
removeIssue: (workspaceSlug: string, projectId: string, issueId: string, cycleId: string) => Promise<void>;
|
||||
archiveIssue: (workspaceSlug: string, projectId: string, issueId: string, cycleId: string) => Promise<void>;
|
||||
quickAddIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
@ -67,7 +57,7 @@ export interface ICycleIssues {
|
||||
issueIds: string[],
|
||||
fetchAddedIssues?: boolean
|
||||
) => Promise<void>;
|
||||
removeIssueFromCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => Promise<TIssue>;
|
||||
removeIssueFromCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => Promise<void>;
|
||||
transferIssuesFromCycle: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
@ -156,11 +146,9 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader = "init-loader",
|
||||
cycleId: string | undefined = undefined
|
||||
cycleId: string
|
||||
) => {
|
||||
try {
|
||||
if (!cycleId) throw new Error("Cycle Id is required");
|
||||
|
||||
this.loader = loadType;
|
||||
|
||||
const params = this.rootIssueStore?.cycleIssuesFilter?.appliedFilters;
|
||||
@ -185,15 +173,8 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
||||
}
|
||||
};
|
||||
|
||||
createIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: Partial<TIssue>,
|
||||
cycleId: string | undefined = undefined
|
||||
) => {
|
||||
createIssue = async (workspaceSlug: string, projectId: string, data: Partial<TIssue>, cycleId: string) => {
|
||||
try {
|
||||
if (!cycleId) throw new Error("Cycle Id is required");
|
||||
|
||||
const response = await this.rootIssueStore.projectIssues.createIssue(workspaceSlug, projectId, data);
|
||||
await this.addIssueToCycle(workspaceSlug, projectId, cycleId, [response.id], false);
|
||||
this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId);
|
||||
@ -209,11 +190,9 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
data: Partial<TIssue>,
|
||||
cycleId: string | undefined = undefined
|
||||
cycleId: string
|
||||
) => {
|
||||
try {
|
||||
if (!cycleId) throw new Error("Cycle Id is required");
|
||||
|
||||
await this.rootIssueStore.projectIssues.updateIssue(workspaceSlug, projectId, issueId, data);
|
||||
this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId);
|
||||
} catch (error) {
|
||||
@ -222,15 +201,8 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
||||
}
|
||||
};
|
||||
|
||||
removeIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
cycleId: string | undefined = undefined
|
||||
) => {
|
||||
removeIssue = async (workspaceSlug: string, projectId: string, issueId: string, cycleId: string) => {
|
||||
try {
|
||||
if (!cycleId) throw new Error("Cycle Id is required");
|
||||
|
||||
await this.rootIssueStore.projectIssues.removeIssue(workspaceSlug, projectId, issueId);
|
||||
this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId);
|
||||
|
||||
@ -244,15 +216,8 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
||||
}
|
||||
};
|
||||
|
||||
archiveIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
cycleId: string | undefined = undefined
|
||||
) => {
|
||||
archiveIssue = async (workspaceSlug: string, projectId: string, issueId: string, cycleId: string) => {
|
||||
try {
|
||||
if (!cycleId) throw new Error("Cycle Id is required");
|
||||
|
||||
await this.rootIssueStore.projectIssues.archiveIssue(workspaceSlug, projectId, issueId);
|
||||
this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId);
|
||||
|
||||
@ -290,7 +255,7 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation", cycleId);
|
||||
if (cycleId) this.fetchIssues(workspaceSlug, projectId, "mutation", cycleId);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
@ -335,10 +300,8 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
|
||||
|
||||
this.rootStore.issues.updateIssue(issueId, { cycle_id: null });
|
||||
|
||||
const response = await this.issueService.removeIssueFromCycle(workspaceSlug, projectId, cycleId, issueId);
|
||||
await this.issueService.removeIssueFromCycle(workspaceSlug, projectId, cycleId, issueId);
|
||||
this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId);
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ export interface IIssueStoreActions {
|
||||
removeIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||
archiveIssue: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||
addIssueToCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueIds: string[]) => Promise<void>;
|
||||
removeIssueFromCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => Promise<TIssue>;
|
||||
removeIssueFromCycle: (workspaceSlug: string, projectId: string, cycleId: string, issueId: string) => Promise<void>;
|
||||
addModulesToIssue: (workspaceSlug: string, projectId: string, issueId: string, moduleIds: string[]) => Promise<any>;
|
||||
removeModulesFromIssue: (
|
||||
workspaceSlug: string,
|
||||
|
@ -35,7 +35,7 @@ export interface IModuleIssuesFilter {
|
||||
projectId: string,
|
||||
filterType: EIssueFilterType,
|
||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters,
|
||||
moduleId?: string | undefined
|
||||
moduleId: string
|
||||
) => Promise<void>;
|
||||
}
|
||||
|
||||
@ -136,10 +136,9 @@ export class ModuleIssuesFilter extends IssueFilterHelperStore implements IModul
|
||||
projectId: string,
|
||||
type: EIssueFilterType,
|
||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters,
|
||||
moduleId: string | undefined = undefined
|
||||
moduleId: string
|
||||
) => {
|
||||
try {
|
||||
if (!moduleId) throw new Error("Module id is required");
|
||||
if (isEmpty(this.filters) || isEmpty(this.filters[moduleId]) || isEmpty(filters)) return;
|
||||
|
||||
const _filters = {
|
||||
|
@ -25,33 +25,23 @@ export interface IModuleIssues {
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader,
|
||||
moduleId?: string | undefined
|
||||
moduleId: string
|
||||
) => Promise<TIssue[] | undefined>;
|
||||
createIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: Partial<TIssue>,
|
||||
moduleId?: string | undefined
|
||||
moduleId: string
|
||||
) => Promise<TIssue | undefined>;
|
||||
updateIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
data: Partial<TIssue>,
|
||||
moduleId?: string | undefined
|
||||
) => Promise<void>;
|
||||
removeIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
moduleId?: string | undefined
|
||||
) => Promise<void>;
|
||||
archiveIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
moduleId?: string | undefined
|
||||
moduleId: string
|
||||
) => Promise<void>;
|
||||
removeIssue: (workspaceSlug: string, projectId: string, issueId: string, moduleId: string) => Promise<void>;
|
||||
archiveIssue: (workspaceSlug: string, projectId: string, issueId: string, moduleId: string) => Promise<void>;
|
||||
quickAddIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
@ -160,11 +150,9 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader = "init-loader",
|
||||
moduleId: string | undefined = undefined
|
||||
moduleId: string
|
||||
) => {
|
||||
try {
|
||||
if (!moduleId) throw new Error("Module Id is required");
|
||||
|
||||
this.loader = loadType;
|
||||
|
||||
const params = this.rootIssueStore?.moduleIssuesFilter?.appliedFilters;
|
||||
@ -190,15 +178,8 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
|
||||
}
|
||||
};
|
||||
|
||||
createIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: Partial<TIssue>,
|
||||
moduleId: string | undefined = undefined
|
||||
) => {
|
||||
createIssue = async (workspaceSlug: string, projectId: string, data: Partial<TIssue>, moduleId: string) => {
|
||||
try {
|
||||
if (!moduleId) throw new Error("Module Id is required");
|
||||
|
||||
const response = await this.rootIssueStore.projectIssues.createIssue(workspaceSlug, projectId, data);
|
||||
await this.addIssuesToModule(workspaceSlug, projectId, moduleId, [response.id], false);
|
||||
this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId);
|
||||
@ -214,11 +195,9 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
data: Partial<TIssue>,
|
||||
moduleId: string | undefined = undefined
|
||||
moduleId: string
|
||||
) => {
|
||||
try {
|
||||
if (!moduleId) throw new Error("Module Id is required");
|
||||
|
||||
await this.rootIssueStore.projectIssues.updateIssue(workspaceSlug, projectId, issueId, data);
|
||||
this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId);
|
||||
} catch (error) {
|
||||
@ -227,15 +206,8 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
|
||||
}
|
||||
};
|
||||
|
||||
removeIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
moduleId: string | undefined = undefined
|
||||
) => {
|
||||
removeIssue = async (workspaceSlug: string, projectId: string, issueId: string, moduleId: string) => {
|
||||
try {
|
||||
if (!moduleId) throw new Error("Module Id is required");
|
||||
|
||||
await this.rootIssueStore.projectIssues.removeIssue(workspaceSlug, projectId, issueId);
|
||||
this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId);
|
||||
|
||||
@ -249,15 +221,8 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
|
||||
}
|
||||
};
|
||||
|
||||
archiveIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
moduleId: string | undefined = undefined
|
||||
) => {
|
||||
archiveIssue = async (workspaceSlug: string, projectId: string, issueId: string, moduleId: string) => {
|
||||
try {
|
||||
if (!moduleId) throw new Error("Module Id is required");
|
||||
|
||||
await this.rootIssueStore.projectIssues.archiveIssue(workspaceSlug, projectId, issueId);
|
||||
this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId);
|
||||
|
||||
|
@ -36,7 +36,7 @@ export interface IProfileIssuesFilter {
|
||||
projectId: string | undefined,
|
||||
filterType: EIssueFilterType,
|
||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters,
|
||||
userId?: string | undefined
|
||||
userId: string
|
||||
) => Promise<void>;
|
||||
}
|
||||
|
||||
@ -125,11 +125,9 @@ export class ProfileIssuesFilter extends IssueFilterHelperStore implements IProf
|
||||
projectId: string | undefined,
|
||||
type: EIssueFilterType,
|
||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters,
|
||||
userId: string | undefined = undefined
|
||||
userId: string
|
||||
) => {
|
||||
try {
|
||||
if (!userId) throw new Error("user id is required");
|
||||
|
||||
if (isEmpty(this.filters) || isEmpty(this.filters[userId]) || isEmpty(filters)) return;
|
||||
|
||||
const _filters = {
|
||||
|
@ -27,34 +27,24 @@ export interface IProfileIssues {
|
||||
workspaceSlug: string,
|
||||
projectId: string | undefined,
|
||||
loadType: TLoader,
|
||||
userId?: string | undefined,
|
||||
userId: string,
|
||||
view?: "assigned" | "created" | "subscribed"
|
||||
) => Promise<TIssue[]>;
|
||||
createIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: Partial<TIssue>,
|
||||
userId?: string | undefined
|
||||
userId: string
|
||||
) => Promise<TIssue | undefined>;
|
||||
updateIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
data: Partial<TIssue>,
|
||||
userId?: string | undefined
|
||||
) => Promise<void>;
|
||||
removeIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
userId?: string | undefined
|
||||
) => Promise<void>;
|
||||
archiveIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
userId?: string | undefined
|
||||
userId: string
|
||||
) => Promise<void>;
|
||||
removeIssue: (workspaceSlug: string, projectId: string, issueId: string, userId: string) => Promise<void>;
|
||||
archiveIssue: (workspaceSlug: string, projectId: string, issueId: string, userId: string) => Promise<void>;
|
||||
quickAddIssue: undefined;
|
||||
}
|
||||
|
||||
@ -150,15 +140,14 @@ export class ProfileIssues extends IssueHelperStore implements IProfileIssues {
|
||||
workspaceSlug: string,
|
||||
projectId: string | undefined,
|
||||
loadType: TLoader = "init-loader",
|
||||
userId?: string | undefined,
|
||||
userId: string,
|
||||
view?: "assigned" | "created" | "subscribed"
|
||||
) => {
|
||||
try {
|
||||
if (!userId) throw new Error("user id is required");
|
||||
if (!view) throw new Error("current tab view is required");
|
||||
|
||||
this.loader = loadType;
|
||||
this.currentView = view;
|
||||
if (view) this.currentView = view;
|
||||
|
||||
if (!this.currentView) throw new Error("current tab view is required");
|
||||
|
||||
const uniqueViewId = `${workspaceSlug}_${view}`;
|
||||
|
||||
@ -193,15 +182,8 @@ export class ProfileIssues extends IssueHelperStore implements IProfileIssues {
|
||||
}
|
||||
};
|
||||
|
||||
createIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: Partial<TIssue>,
|
||||
userId: string | undefined = undefined
|
||||
) => {
|
||||
createIssue = async (workspaceSlug: string, projectId: string, data: Partial<TIssue>, userId: string) => {
|
||||
try {
|
||||
if (!userId) throw new Error("user id is required");
|
||||
|
||||
const response = await this.rootIssueStore.projectIssues.createIssue(workspaceSlug, projectId, data);
|
||||
|
||||
const uniqueViewId = `${workspaceSlug}_${this.currentView}`;
|
||||
@ -223,11 +205,9 @@ export class ProfileIssues extends IssueHelperStore implements IProfileIssues {
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
data: Partial<TIssue>,
|
||||
userId: string | undefined = undefined
|
||||
userId: string
|
||||
) => {
|
||||
try {
|
||||
if (!userId) throw new Error("user id is required");
|
||||
|
||||
this.rootStore.issues.updateIssue(issueId, data);
|
||||
await this.rootIssueStore.projectIssues.updateIssue(workspaceSlug, projectId, data.id as keyof TIssue, data);
|
||||
} catch (error) {
|
||||
@ -258,13 +238,7 @@ export class ProfileIssues extends IssueHelperStore implements IProfileIssues {
|
||||
}
|
||||
};
|
||||
|
||||
archiveIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
userId: string | undefined = undefined
|
||||
) => {
|
||||
if (!userId) return;
|
||||
archiveIssue = async (workspaceSlug: string, projectId: string, issueId: string, userId: string) => {
|
||||
try {
|
||||
await this.rootIssueStore.projectIssues.archiveIssue(workspaceSlug, projectId, issueId);
|
||||
|
||||
|
@ -35,7 +35,7 @@ export interface IProjectViewIssuesFilter {
|
||||
projectId: string,
|
||||
filterType: EIssueFilterType,
|
||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters,
|
||||
viewId?: string | undefined
|
||||
viewId: string
|
||||
) => Promise<void>;
|
||||
}
|
||||
|
||||
@ -134,11 +134,9 @@ export class ProjectViewIssuesFilter extends IssueFilterHelperStore implements I
|
||||
projectId: string,
|
||||
type: EIssueFilterType,
|
||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters,
|
||||
viewId: string | undefined = undefined
|
||||
viewId: string
|
||||
) => {
|
||||
try {
|
||||
if (!viewId) throw new Error("View id is required");
|
||||
|
||||
if (isEmpty(this.filters) || isEmpty(this.filters[viewId]) || isEmpty(filters)) return;
|
||||
|
||||
const _filters = {
|
||||
|
@ -21,33 +21,23 @@ export interface IProjectViewIssues {
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader,
|
||||
viewId?: string | undefined
|
||||
viewId: string
|
||||
) => Promise<TIssue[] | undefined>;
|
||||
createIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: Partial<TIssue>,
|
||||
viewId?: string | undefined
|
||||
viewId: string
|
||||
) => Promise<TIssue | undefined>;
|
||||
updateIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
data: Partial<TIssue>,
|
||||
viewId?: string | undefined
|
||||
) => Promise<void>;
|
||||
removeIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
viewId?: string | undefined
|
||||
) => Promise<void>;
|
||||
archiveIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
viewId?: string | undefined
|
||||
viewId: string
|
||||
) => Promise<void>;
|
||||
removeIssue: (workspaceSlug: string, projectId: string, issueId: string, viewId: string) => Promise<void>;
|
||||
archiveIssue: (workspaceSlug: string, projectId: string, issueId: string, viewId: string) => Promise<void>;
|
||||
quickAddIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
@ -124,15 +114,8 @@ export class ProjectViewIssues extends IssueHelperStore implements IProjectViewI
|
||||
return issues;
|
||||
}
|
||||
|
||||
fetchIssues = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loadType: TLoader = "init-loader",
|
||||
viewId: string | undefined = undefined
|
||||
) => {
|
||||
fetchIssues = async (workspaceSlug: string, projectId: string, loadType: TLoader = "init-loader", viewId: string) => {
|
||||
try {
|
||||
if (!viewId) throw new Error("View Id is required");
|
||||
|
||||
this.loader = loadType;
|
||||
|
||||
const params = this.rootIssueStore?.projectViewIssuesFilter?.appliedFilters;
|
||||
@ -157,15 +140,8 @@ export class ProjectViewIssues extends IssueHelperStore implements IProjectViewI
|
||||
}
|
||||
};
|
||||
|
||||
createIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: Partial<TIssue>,
|
||||
viewId: string | undefined = undefined
|
||||
) => {
|
||||
createIssue = async (workspaceSlug: string, projectId: string, data: Partial<TIssue>, viewId: string) => {
|
||||
try {
|
||||
if (!viewId) throw new Error("View Id is required");
|
||||
|
||||
const response = await this.rootIssueStore.projectIssues.createIssue(workspaceSlug, projectId, data);
|
||||
|
||||
runInAction(() => {
|
||||
@ -174,7 +150,7 @@ export class ProjectViewIssues extends IssueHelperStore implements IProjectViewI
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation");
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation", viewId);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
@ -184,27 +160,18 @@ export class ProjectViewIssues extends IssueHelperStore implements IProjectViewI
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
data: Partial<TIssue>,
|
||||
viewId: string | undefined = undefined
|
||||
viewId: string
|
||||
) => {
|
||||
try {
|
||||
if (!viewId) throw new Error("View Id is required");
|
||||
|
||||
await this.rootIssueStore.projectIssues.updateIssue(workspaceSlug, projectId, issueId, data);
|
||||
} catch (error) {
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation");
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation", viewId);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
removeIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
viewId: string | undefined = undefined
|
||||
) => {
|
||||
removeIssue = async (workspaceSlug: string, projectId: string, issueId: string, viewId: string) => {
|
||||
try {
|
||||
if (!viewId) throw new Error("View Id is required");
|
||||
|
||||
await this.rootIssueStore.projectIssues.removeIssue(workspaceSlug, projectId, issueId);
|
||||
|
||||
const issueIndex = this.issues[viewId].findIndex((_issueId) => _issueId === issueId);
|
||||
@ -213,27 +180,20 @@ export class ProjectViewIssues extends IssueHelperStore implements IProjectViewI
|
||||
this.issues[viewId].splice(issueIndex, 1);
|
||||
});
|
||||
} catch (error) {
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation");
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation", viewId);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
archiveIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
viewId: string | undefined = undefined
|
||||
) => {
|
||||
archiveIssue = async (workspaceSlug: string, projectId: string, issueId: string, viewId: string) => {
|
||||
try {
|
||||
if (!viewId) throw new Error("View Id is required");
|
||||
|
||||
await this.rootIssueStore.projectIssues.archiveIssue(workspaceSlug, projectId, issueId);
|
||||
|
||||
runInAction(() => {
|
||||
pull(this.issues[viewId], issueId);
|
||||
});
|
||||
} catch (error) {
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation");
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation", viewId);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
@ -263,7 +223,7 @@ export class ProjectViewIssues extends IssueHelperStore implements IProjectViewI
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
this.fetchIssues(workspaceSlug, projectId, "mutation");
|
||||
if (viewId) this.fetchIssues(workspaceSlug, projectId, "mutation", viewId);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
@ -37,7 +37,7 @@ export interface IWorkspaceIssuesFilter {
|
||||
projectId: string | undefined,
|
||||
filterType: EIssueFilterType,
|
||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters,
|
||||
viewId?: string | undefined
|
||||
viewId: string
|
||||
) => Promise<void>;
|
||||
//helper action
|
||||
getIssueFilters: (viewId: string | undefined) => IIssueFilters | undefined;
|
||||
@ -156,10 +156,9 @@ export class WorkspaceIssuesFilter extends IssueFilterHelperStore implements IWo
|
||||
projectId: string | undefined,
|
||||
type: EIssueFilterType,
|
||||
filters: IIssueFilterOptions | IIssueDisplayFilterOptions | IIssueDisplayProperties | TIssueKanbanFilters,
|
||||
viewId: string | undefined = undefined
|
||||
viewId: string
|
||||
) => {
|
||||
try {
|
||||
if (!viewId) throw new Error("View id is required");
|
||||
const issueFilters = this.getIssueFilters(viewId);
|
||||
|
||||
if (!issueFilters || isEmpty(filters)) return;
|
||||
|
@ -23,27 +23,23 @@ export interface IWorkspaceIssues {
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: Partial<TIssue>,
|
||||
viewId?: string | undefined
|
||||
viewId: string
|
||||
) => Promise<TIssue | undefined>;
|
||||
updateIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
data: Partial<TIssue>,
|
||||
viewId?: string | undefined
|
||||
) => Promise<void>;
|
||||
removeIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
viewId?: string | undefined
|
||||
viewId: string
|
||||
) => Promise<void>;
|
||||
removeIssue: (workspaceSlug: string, projectId: string, issueId: string, viewId: string) => Promise<void>;
|
||||
archiveIssue: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
viewId?: string | undefined
|
||||
) => Promise<void>;
|
||||
quickAddIssue: undefined;
|
||||
}
|
||||
|
||||
export class WorkspaceIssues extends IssueHelperStore implements IWorkspaceIssues {
|
||||
@ -61,6 +57,8 @@ export class WorkspaceIssues extends IssueHelperStore implements IWorkspaceIssue
|
||||
issueService;
|
||||
issueArchiveService;
|
||||
|
||||
quickAddIssue = undefined;
|
||||
|
||||
constructor(_rootStore: IIssueRootStore) {
|
||||
super(_rootStore);
|
||||
|
||||
@ -139,15 +137,8 @@ export class WorkspaceIssues extends IssueHelperStore implements IWorkspaceIssue
|
||||
}
|
||||
};
|
||||
|
||||
createIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: Partial<TIssue>,
|
||||
viewId: string | undefined = undefined
|
||||
) => {
|
||||
createIssue = async (workspaceSlug: string, projectId: string, data: Partial<TIssue>, viewId: string) => {
|
||||
try {
|
||||
if (!viewId) throw new Error("View id is required");
|
||||
|
||||
const uniqueViewId = `${workspaceSlug}_${viewId}`;
|
||||
|
||||
const response = await this.issueService.createIssue(workspaceSlug, projectId, data);
|
||||
@ -169,11 +160,9 @@ export class WorkspaceIssues extends IssueHelperStore implements IWorkspaceIssue
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
data: Partial<TIssue>,
|
||||
viewId: string | undefined = undefined
|
||||
viewId: string
|
||||
) => {
|
||||
try {
|
||||
if (!viewId) throw new Error("View id is required");
|
||||
|
||||
this.rootStore.issues.updateIssue(issueId, data);
|
||||
await this.issueService.patchIssue(workspaceSlug, projectId, issueId, data);
|
||||
} catch (error) {
|
||||
@ -182,15 +171,8 @@ export class WorkspaceIssues extends IssueHelperStore implements IWorkspaceIssue
|
||||
}
|
||||
};
|
||||
|
||||
removeIssue = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
issueId: string,
|
||||
viewId: string | undefined = undefined
|
||||
) => {
|
||||
removeIssue = async (workspaceSlug: string, projectId: string, issueId: string, viewId: string) => {
|
||||
try {
|
||||
if (!viewId) throw new Error("View id is required");
|
||||
|
||||
const uniqueViewId = `${workspaceSlug}_${viewId}`;
|
||||
|
||||
await this.issueService.deleteIssue(workspaceSlug, projectId, issueId);
|
||||
|
@ -128,7 +128,7 @@ export class WorkspaceMemberStore implements IWorkspaceMemberStore {
|
||||
const memberDetails = this.getWorkspaceMemberDetails(userId);
|
||||
if (!memberDetails) return false;
|
||||
const memberSearchQuery = `${memberDetails.member.first_name} ${memberDetails.member.last_name} ${
|
||||
memberDetails.member.display_name
|
||||
memberDetails.member?.display_name
|
||||
} ${memberDetails.member.email ?? ""}`;
|
||||
return memberSearchQuery.toLowerCase()?.includes(searchQuery.toLowerCase());
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user