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