mirror of
https://github.com/makeplane/plane.git
synced 2026-07-12 21:40:18 +02:00
[WEB-3290] chore: initiative improvements (#2377)
* chore: initiative list improvements * chore: epic peek view implemented in initiative detail page * fix: mutation * chore: code refactor * chore: code refactor * chore: code refactor
This commit is contained in:
committed by
GitHub
parent
bc4d183835
commit
ecc0afb91b
@@ -22,6 +22,7 @@ interface IListItemProps {
|
||||
actionItemContainerClassName?: string;
|
||||
isSidebarOpen?: boolean;
|
||||
quickActionElement?: JSX.Element;
|
||||
preventDefaultNProgress?: boolean;
|
||||
}
|
||||
|
||||
export const ListItem: FC<IListItemProps> = (props) => {
|
||||
@@ -40,6 +41,7 @@ export const ListItem: FC<IListItemProps> = (props) => {
|
||||
isSidebarOpen = false,
|
||||
quickActionElement,
|
||||
itemClassName = "",
|
||||
preventDefaultNProgress = false,
|
||||
} = props;
|
||||
|
||||
// router
|
||||
@@ -70,6 +72,7 @@ export const ListItem: FC<IListItemProps> = (props) => {
|
||||
target="_self"
|
||||
onClick={handleControlLinkClick}
|
||||
disabled={disableLink}
|
||||
data-prevent-nprogress={preventDefaultNProgress}
|
||||
>
|
||||
<div className="flex items-center gap-4 truncate">
|
||||
{prependTitleElement && <span className="flex items-center flex-shrink-0">{prependTitleElement}</span>}
|
||||
|
||||
@@ -109,52 +109,50 @@ export const EpicView: FC<IEpicView> = observer((props) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="w-full !text-base">
|
||||
{issueId && (
|
||||
<div
|
||||
ref={issuePeekOverviewRef}
|
||||
className={peekOverviewIssueClassName}
|
||||
style={{
|
||||
boxShadow:
|
||||
"0px 4px 8px 0px rgba(0, 0, 0, 0.12), 0px 6px 12px 0px rgba(16, 24, 40, 0.12), 0px 1px 16px 0px rgba(16, 24, 40, 0.12)",
|
||||
}}
|
||||
>
|
||||
{isError ? (
|
||||
<div className="relative h-screen w-full overflow-hidden">
|
||||
<EpicPeekOverviewError removeRoutePeekId={removeRoutePeekId} />
|
||||
</div>
|
||||
) : (
|
||||
isLoading && <EpicPeekOverviewLoader removeRoutePeekId={removeRoutePeekId} />
|
||||
)}
|
||||
{!isLoading && !isError && issue && (
|
||||
<>
|
||||
{/* header */}
|
||||
<EpicPeekOverviewHeader
|
||||
peekMode={peekMode}
|
||||
setPeekMode={(value) => setPeekMode(value)}
|
||||
removeRoutePeekId={removeRoutePeekId}
|
||||
toggleEditEpicModal={toggleEditEpicModal}
|
||||
toggleDeleteEpicModal={toggleDeleteEpicModal}
|
||||
handleRestoreIssue={handleRestore}
|
||||
isArchived={is_archived}
|
||||
issueId={issueId}
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
isSubmitting={isSubmitting}
|
||||
disabled={disabled}
|
||||
embedIssue={embedIssue}
|
||||
/>
|
||||
{/* content */}
|
||||
<EpicDetailRoot
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
projectId={projectId.toString()}
|
||||
epicId={issueId.toString()}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{issueId && (
|
||||
<div
|
||||
ref={issuePeekOverviewRef}
|
||||
className={peekOverviewIssueClassName}
|
||||
style={{
|
||||
boxShadow:
|
||||
"0px 4px 8px 0px rgba(0, 0, 0, 0.12), 0px 6px 12px 0px rgba(16, 24, 40, 0.12), 0px 1px 16px 0px rgba(16, 24, 40, 0.12)",
|
||||
}}
|
||||
>
|
||||
{isError ? (
|
||||
<div className="relative h-screen w-full overflow-hidden">
|
||||
<EpicPeekOverviewError removeRoutePeekId={removeRoutePeekId} />
|
||||
</div>
|
||||
) : (
|
||||
isLoading && <EpicPeekOverviewLoader removeRoutePeekId={removeRoutePeekId} />
|
||||
)}
|
||||
{!isLoading && !isError && issue && (
|
||||
<>
|
||||
{/* header */}
|
||||
<EpicPeekOverviewHeader
|
||||
peekMode={peekMode}
|
||||
setPeekMode={(value) => setPeekMode(value)}
|
||||
removeRoutePeekId={removeRoutePeekId}
|
||||
toggleEditEpicModal={toggleEditEpicModal}
|
||||
toggleDeleteEpicModal={toggleDeleteEpicModal}
|
||||
handleRestoreIssue={handleRestore}
|
||||
isArchived={is_archived}
|
||||
issueId={issueId}
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
isSubmitting={isSubmitting}
|
||||
disabled={disabled}
|
||||
embedIssue={embedIssue}
|
||||
/>
|
||||
{/* content */}
|
||||
<EpicDetailRoot
|
||||
workspaceSlug={workspaceSlug.toString()}
|
||||
projectId={projectId.toString()}
|
||||
epicId={issueId.toString()}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -3,20 +3,21 @@ import { observer } from "mobx-react";
|
||||
import { useParams } from "next/navigation";
|
||||
// Plane
|
||||
import { EUserPermissionsLevel } from "@plane/constants";
|
||||
import { CircularProgressIndicator, InitiativeIcon } from "@plane/ui";
|
||||
import { CircularProgressIndicator, ControlLink, InitiativeIcon } from "@plane/ui";
|
||||
import { cn } from "@plane/utils";
|
||||
// components
|
||||
import { ListItem } from "@/components/core/list";
|
||||
// hooks
|
||||
import { getProgress } from "@/helpers/common.helper";
|
||||
import { useAppTheme, useUserPermissions } from "@/hooks/store";
|
||||
import { useAppRouter } from "@/hooks/use-app-router";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// plane web
|
||||
import { EUserPermissions } from "@/plane-web/constants/user-permissions";
|
||||
import { useInitiatives } from "@/plane-web/hooks/store/use-initiatives";
|
||||
// local components
|
||||
import { BlockProperties } from "./block-properties";
|
||||
import { InitiativeQuickActions } from "./quick-actions";
|
||||
import { ReadOnlyBlockProperties } from "./read-only-properties";
|
||||
|
||||
type Props = {
|
||||
initiativeId: string;
|
||||
@@ -29,6 +30,7 @@ export const InitiativeBlock = observer((props: Props) => {
|
||||
const { workspaceSlug } = useParams();
|
||||
|
||||
// hooks
|
||||
const router = useAppRouter();
|
||||
const {
|
||||
initiative: { getInitiativeById, getInitiativeStatsById },
|
||||
} = useInitiatives();
|
||||
@@ -69,7 +71,16 @@ export const InitiativeBlock = observer((props: Props) => {
|
||||
}
|
||||
quickActionElement={
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
<BlockProperties initiative={initiative} isSidebarCollapsed={isSidebarCollapsed} disabled />
|
||||
<ControlLink
|
||||
className="relative flex w-full items-center gap-3 overflow-hidden"
|
||||
href={`/${workspaceSlug}/initiatives/${initiative.id}`}
|
||||
target="_self"
|
||||
onClick={() => {
|
||||
router.push(`/${workspaceSlug}/initiatives/${initiative.id}`);
|
||||
}}
|
||||
>
|
||||
<ReadOnlyBlockProperties initiative={initiative} isSidebarCollapsed={isSidebarCollapsed} />
|
||||
</ControlLink>
|
||||
<div
|
||||
className={cn("hidden", {
|
||||
"md:flex": isSidebarCollapsed,
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
"use client";
|
||||
|
||||
import React, { FC } from "react";
|
||||
// plane imports
|
||||
import { cn } from "@plane/utils";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export const PropertyBlockWrapper: FC<Props> = (props) => {
|
||||
const { className = "", children } = props;
|
||||
return (
|
||||
<div className="h-5">
|
||||
<div
|
||||
className={cn(
|
||||
"h-full text-xs px-2 flex items-center gap-2 text-custom-text-200 border-[0.5px] border-custom-border-300 rounded",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,128 @@
|
||||
"use client";
|
||||
import { observer } from "mobx-react";
|
||||
import { ArrowRight, Briefcase, CalendarCheck2, CalendarDays, Users } from "lucide-react";
|
||||
// plane imports
|
||||
import { Avatar, EpicIcon, Logo } from "@plane/ui";
|
||||
import { cn, renderFormattedDate } from "@plane/utils";
|
||||
// helpers
|
||||
import { getDate } from "@/helpers/date-time.helper";
|
||||
import { getFileURL } from "@/helpers/file.helper";
|
||||
// hooks
|
||||
import { useMember, useProject } from "@/hooks/store";
|
||||
// plane Web
|
||||
import { TProject } from "@/plane-web/types";
|
||||
import { TInitiative } from "@/plane-web/types/initiative";
|
||||
// local components
|
||||
import { PropertyBlockWrapper } from "./property-block-wrapper";
|
||||
|
||||
type Props = {
|
||||
initiative: TInitiative;
|
||||
isSidebarCollapsed: boolean | undefined;
|
||||
};
|
||||
|
||||
export const ReadOnlyBlockProperties = observer((props: Props) => {
|
||||
const { initiative, isSidebarCollapsed } = props;
|
||||
// store hooks
|
||||
const { getUserDetails } = useMember();
|
||||
const { getProjectById } = useProject();
|
||||
|
||||
// derived values
|
||||
const lead = getUserDetails(initiative.lead ?? "");
|
||||
const startDate = getDate(initiative.start_date);
|
||||
const endDate = getDate(initiative.end_date);
|
||||
|
||||
// helpers
|
||||
const getProjectIcon = (value: string | string[] | null) => {
|
||||
const renderIcon = (projectDetails: TProject) => (
|
||||
<span className="grid place-items-center flex-shrink-0 h-4 w-4">
|
||||
<Logo logo={projectDetails.logo_props} size={14} />
|
||||
</span>
|
||||
);
|
||||
if (Array.isArray(value)) {
|
||||
return (
|
||||
<div className="flex items-center gap-0.5">
|
||||
{value.length > 0 ? (
|
||||
value.map((projectId) => {
|
||||
const projectDetails = getProjectById(projectId);
|
||||
return projectDetails ? renderIcon(projectDetails) : null;
|
||||
})
|
||||
) : (
|
||||
<Briefcase className="size-3 text-custom-text-300" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
const projectDetails = getProjectById(value);
|
||||
return projectDetails ? renderIcon(projectDetails) : null;
|
||||
}
|
||||
};
|
||||
|
||||
const getProjectDisplayName = (value: string | string[] | null, placeholder: string = "") => {
|
||||
if (Array.isArray(value)) {
|
||||
const firstProject = getProjectById(value[0]);
|
||||
return value.length ? (value.length === 1 ? firstProject?.name : `${value.length} projects`) : placeholder;
|
||||
} else {
|
||||
return value ? (getProjectById(value)?.name ?? placeholder) : placeholder;
|
||||
}
|
||||
};
|
||||
|
||||
const getEpicDisplayName = (value: string | string[] | null, placeholder: string = "") => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.length ? `${value.length} epics` : placeholder;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`relative flex flex-wrap ${isSidebarCollapsed ? "md:flex-grow md:flex-shrink-0" : "lg:flex-grow lg:flex-shrink-0"} items-center gap-2 whitespace-nowrap`}
|
||||
>
|
||||
{/* dates */}
|
||||
<PropertyBlockWrapper>
|
||||
<span className={cn("h-full flex items-center justify-center gap-1 rounded-sm flex-grow")}>
|
||||
{<CalendarDays className="h-3 w-3 flex-shrink-0" />}
|
||||
{startDate ? renderFormattedDate(startDate) : ""}
|
||||
</span>
|
||||
<ArrowRight className="h-3 w-3 flex-shrink-0" />
|
||||
<span className={cn("h-full flex items-center justify-center gap-1 rounded-sm flex-grow")}>
|
||||
{!initiative.end_date && <CalendarCheck2 className="h-3 w-3 flex-shrink-0" />}
|
||||
{endDate ? renderFormattedDate(endDate) : ""}
|
||||
</span>
|
||||
</PropertyBlockWrapper>
|
||||
|
||||
{/* lead */}
|
||||
<PropertyBlockWrapper>
|
||||
{lead ? (
|
||||
<>
|
||||
<Avatar
|
||||
key={lead.id}
|
||||
name={lead.display_name}
|
||||
src={getFileURL(lead.avatar_url)}
|
||||
size={14}
|
||||
className="text-[9px]"
|
||||
/>
|
||||
<div>{lead.first_name}</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Users className="h-3 w-3 flex-shrink-0" />
|
||||
<div>Lead</div>
|
||||
</>
|
||||
)}
|
||||
</PropertyBlockWrapper>
|
||||
|
||||
{/* projects */}
|
||||
<PropertyBlockWrapper>
|
||||
{getProjectIcon(initiative.project_ids || [])}
|
||||
<span className="flex-grow truncate max-w-40">
|
||||
{getProjectDisplayName(initiative.project_ids || [], "Project")}
|
||||
</span>
|
||||
</PropertyBlockWrapper>
|
||||
|
||||
{/* epics */}
|
||||
<PropertyBlockWrapper>
|
||||
<EpicIcon className="h-4 w-4 text-custom-text-300" />
|
||||
<span className="flex-grow truncate max-w-40">{getEpicDisplayName(initiative.epic_ids || [], "Epic")}</span>
|
||||
</PropertyBlockWrapper>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
@@ -13,14 +13,17 @@ import { getDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper"
|
||||
import { useIssueDetail } from "@/hooks/store";
|
||||
import { useIssuesActions } from "@/hooks/use-issues-actions";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
import { useInitiatives } from "@/plane-web/hooks/store/use-initiatives";
|
||||
|
||||
type Props = {
|
||||
workspaceSlug: string;
|
||||
initiativeId: string;
|
||||
epicId: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const EpicProperties: FC<Props> = observer((props) => {
|
||||
const { epicId, disabled = false } = props;
|
||||
const { workspaceSlug, initiativeId, epicId, disabled = false } = props;
|
||||
// store hooks
|
||||
const {
|
||||
issue: { getIssueById },
|
||||
@@ -29,6 +32,9 @@ export const EpicProperties: FC<Props> = observer((props) => {
|
||||
|
||||
// hooks
|
||||
const { updateIssue } = useIssuesActions(EIssuesStoreType.EPIC);
|
||||
const {
|
||||
initiative: { fetchInitiativeAnalytics },
|
||||
} = useInitiatives();
|
||||
|
||||
// derived values
|
||||
const epic = getIssueById(epicId);
|
||||
@@ -42,7 +48,11 @@ export const EpicProperties: FC<Props> = observer((props) => {
|
||||
};
|
||||
|
||||
const handleState = (stateId: string) => {
|
||||
if (updateIssue) updateIssue(epic.project_id, epic.id, { state_id: stateId });
|
||||
if (updateIssue) {
|
||||
updateIssue(epic.project_id, epic.id, { state_id: stateId }).then(() => {
|
||||
fetchInitiativeAnalytics(workspaceSlug, initiativeId);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handlePriority = (value: TIssuePriorities) => {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { useIssueDetail, useProject } from "@/hooks/store";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// plane web components
|
||||
import { IdentifierText } from "@/plane-web/components/issues";
|
||||
import { useInitiatives } from "@/plane-web/hooks/store/use-initiatives";
|
||||
import { useIssueTypes } from "@/plane-web/hooks/store";
|
||||
// local components
|
||||
import { EpicProperties } from "./properties";
|
||||
import { EpicQuickActions } from "./quick-action";
|
||||
@@ -31,10 +31,9 @@ export const EpicListItem: React.FC<Props> = observer((props) => {
|
||||
// store hooks
|
||||
const {
|
||||
issue: { getIssueById },
|
||||
setPeekIssue,
|
||||
} = useIssueDetail(EIssueServiceType.EPICS);
|
||||
const {
|
||||
initiative: { getInitiativeEpicStatsById },
|
||||
} = useInitiatives();
|
||||
const { getEpicStatsById } = useIssueTypes();
|
||||
const project = useProject();
|
||||
const { isMobile } = usePlatformOS();
|
||||
|
||||
@@ -43,14 +42,14 @@ export const EpicListItem: React.FC<Props> = observer((props) => {
|
||||
|
||||
// derived values
|
||||
const issue = getIssueById(epicId);
|
||||
const initiativeEpicStats = getInitiativeEpicStatsById(epicId);
|
||||
const initiativeEpicStats = getEpicStatsById(epicId);
|
||||
|
||||
const projectIdentifier = issue?.project_id ? project.getProjectIdentifierById(issue?.project_id) : "";
|
||||
const issueSequenceId = issue?.sequence_id;
|
||||
|
||||
const progress = getProgress(initiativeEpicStats?.completed_issues, initiativeEpicStats?.total_issues);
|
||||
|
||||
if (!issue) return <></>;
|
||||
if (!issue || !issue.project_id) return <></>;
|
||||
return (
|
||||
<ListItem
|
||||
title={issue.name}
|
||||
@@ -75,7 +74,12 @@ export const EpicListItem: React.FC<Props> = observer((props) => {
|
||||
}
|
||||
quickActionElement={
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
<EpicProperties epicId={epicId} disabled={disabled} />
|
||||
<EpicProperties
|
||||
workspaceSlug={workspaceSlug}
|
||||
initiativeId={initiativeId}
|
||||
epicId={epicId}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<div className={cn("hidden md:flex")}>
|
||||
<EpicQuickActions
|
||||
workspaceSlug={workspaceSlug}
|
||||
@@ -90,6 +94,8 @@ export const EpicListItem: React.FC<Props> = observer((props) => {
|
||||
isMobile={isMobile}
|
||||
parentRef={parentRef}
|
||||
className="last:pb-0 last:border-b-0"
|
||||
onItemClick={() => setPeekIssue({ workspaceSlug, projectId: issue.project_id ?? "", issueId: issue.id })}
|
||||
preventDefaultNProgress
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants";
|
||||
import { useInitiatives } from "@/plane-web/hooks/store/use-initiatives";
|
||||
// local components
|
||||
import { LayoutRoot } from "../../common";
|
||||
import { EpicPeekOverview } from "../../epics";
|
||||
import { InitiativeEmptyState } from "../details/empty-state";
|
||||
import { WorkspaceEpicsListModal } from "./main/collapsible-section/epics/workspace-epic-modal";
|
||||
import { InitiativeMainContentRoot } from "./main/root";
|
||||
@@ -118,6 +119,7 @@ export const InitiativeDetailRoot = observer((props: Props) => {
|
||||
handleAddEpicToInitiative(data.map((epic) => epic.id));
|
||||
}}
|
||||
/>
|
||||
<EpicPeekOverview />
|
||||
</LayoutRoot>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -37,7 +37,6 @@ export interface IInitiativeStore {
|
||||
initiativeAnalyticsMap: Record<string, TInitiativeAnalytics>;
|
||||
initiativeEpicLoader: Record<string, TLoader>;
|
||||
initiativeEpicsMap: Record<string, string[]>;
|
||||
initiativeEpicStatsMap: Record<string, TEpicStats> | undefined; // epicId -> stats
|
||||
initiativesLoader: boolean;
|
||||
|
||||
openCollapsibleSection: InitiativeCollapsible[];
|
||||
@@ -58,8 +57,6 @@ export interface IInitiativeStore {
|
||||
removeEpicFromInitiative: (workspaceSlug: string, initiativeId: string, epicId: string) => Promise<void>;
|
||||
addEpicsToInitiative: (workspaceSlug: string, initiativeId: string, epicIds: string[]) => Promise<void>;
|
||||
|
||||
getInitiativeEpicStatsById: (epicId: string) => TEpicStats | undefined;
|
||||
|
||||
fetchInitiatives: (workspaceSlug: string) => Promise<TInitiative[] | undefined>;
|
||||
fetchInitiativesStats: (workspaceSlug: string) => Promise<TInitiativeStats[] | undefined>;
|
||||
fetchInitiativeEpicStats: (workspaceSlug: string, initiativeId: string) => Promise<TEpicStats[] | undefined>;
|
||||
@@ -90,7 +87,6 @@ export class InitiativeStore implements IInitiativeStore {
|
||||
|
||||
initiativeEpicLoader: Record<string, TLoader> = {};
|
||||
initiativeEpicsMap: Record<string, string[]> = {};
|
||||
initiativeEpicStatsMap: Record<string, TEpicStats> | undefined = undefined;
|
||||
|
||||
initiativesLoader: boolean = false;
|
||||
|
||||
@@ -115,7 +111,6 @@ export class InitiativeStore implements IInitiativeStore {
|
||||
|
||||
initiativeEpicLoader: observable,
|
||||
initiativeEpicsMap: observable,
|
||||
initiativeEpicStatsMap: observable,
|
||||
|
||||
openCollapsibleSection: observable.ref,
|
||||
lastCollapsibleAction: observable.ref,
|
||||
@@ -179,8 +174,6 @@ export class InitiativeStore implements IInitiativeStore {
|
||||
|
||||
getInitiativeStatsById = computedFn((initiativeId: string) => this.initiativesStatsMap?.[initiativeId]);
|
||||
|
||||
getInitiativeEpicStatsById = computedFn((epicId: string) => this.initiativeEpicStatsMap?.[epicId]);
|
||||
|
||||
getInitiativeEpicsById = computedFn((initiativeId: string) => this.initiativeEpicsMap?.[initiativeId]);
|
||||
|
||||
getInitiativeAnalyticsById = computedFn((initiativeId: string) => this.initiativeAnalyticsMap[initiativeId]);
|
||||
@@ -257,27 +250,24 @@ export class InitiativeStore implements IInitiativeStore {
|
||||
|
||||
fetchInitiativeEpicStats = async (workspaceSlug: string, initiativeId: string): Promise<TEpicStats[] | undefined> => {
|
||||
try {
|
||||
runInAction(() => {
|
||||
this.initiativeEpicStatsMap = undefined;
|
||||
});
|
||||
|
||||
const response = await this.initiativeService.fetchInitiativeEpicStats(workspaceSlug, initiativeId);
|
||||
|
||||
runInAction(() => {
|
||||
if (!response) return;
|
||||
|
||||
if (!this.initiativeEpicStatsMap) this.initiativeEpicStatsMap = {};
|
||||
if (!this.rootStore.issueTypes.epicStatsMap) this.rootStore.issueTypes.epicStatsMap = {};
|
||||
|
||||
response.forEach((stats) => {
|
||||
if (!stats) return;
|
||||
|
||||
this.initiativeEpicStatsMap![stats.epic_id] = stats;
|
||||
this.rootStore.issueTypes.epicStatsMap![stats.epic_id] = stats;
|
||||
});
|
||||
});
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
console.error("error while fetching initiatives stats", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -439,12 +429,18 @@ export class InitiativeStore implements IInitiativeStore {
|
||||
this.rootStore.issue.issues.addIssue(transformedResponse);
|
||||
|
||||
runInAction(() => {
|
||||
// Update the epicMap by adding the new epicIds
|
||||
update(this.initiativeEpicsMap, [initiativeId], (Ids: string[]) => [...Ids, ...responseIds]);
|
||||
});
|
||||
|
||||
this.fetchInitiativeAnalytics(workspaceSlug, initiativeId);
|
||||
this.fetchInitiativeEpicStats(workspaceSlug, initiativeId);
|
||||
try {
|
||||
await Promise.all([
|
||||
this.fetchInitiativeEpicStats(workspaceSlug, initiativeId),
|
||||
this.fetchInitiativeAnalytics(workspaceSlug, initiativeId),
|
||||
]);
|
||||
} catch (error) {
|
||||
console.error("Error fetching initiative stats or analytics:", error);
|
||||
// Not throwing here since the main operation (adding epics) was successful
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error adding epics to initiative", error);
|
||||
throw error;
|
||||
|
||||
Reference in New Issue
Block a user