mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
fix: updated and handled the estimate points
This commit is contained in:
parent
a725385190
commit
16fa9c9baa
2
packages/types/src/estimate.d.ts
vendored
2
packages/types/src/estimate.d.ts
vendored
@ -63,7 +63,7 @@ export type TEstimateSystem = {
|
||||
name: string;
|
||||
templates: Record<string, TTemplateValues>;
|
||||
is_available: boolean;
|
||||
is_active: boolean;
|
||||
is_ee: boolean;
|
||||
};
|
||||
|
||||
export type TEstimateSystems = {
|
||||
|
@ -26,6 +26,7 @@ export const CreateEstimateModal: FC<TCreateEstimateModal> = observer((props) =>
|
||||
// states
|
||||
const [estimateSystem, setEstimateSystem] = useState<TEstimateSystemKeys>(EEstimateSystem.CATEGORIES);
|
||||
const [estimatePoints, setEstimatePoints] = useState<TEstimatePointsObject[] | undefined>(undefined);
|
||||
const [buttonLoader, setButtonLoader] = useState(false);
|
||||
|
||||
const handleUpdatePoints = (newPoints: TEstimatePointsObject[] | undefined) => setEstimatePoints(newPoints);
|
||||
|
||||
@ -39,7 +40,7 @@ export const CreateEstimateModal: FC<TCreateEstimateModal> = observer((props) =>
|
||||
const handleCreateEstimate = async () => {
|
||||
try {
|
||||
if (!workspaceSlug || !projectId || !estimatePoints) return;
|
||||
|
||||
setButtonLoader(true);
|
||||
const payload: IEstimateFormData = {
|
||||
estimate: {
|
||||
name: ESTIMATE_SYSTEMS[estimateSystem]?.name,
|
||||
@ -50,13 +51,15 @@ export const CreateEstimateModal: FC<TCreateEstimateModal> = observer((props) =>
|
||||
};
|
||||
await createEstimate(workspaceSlug, projectId, payload);
|
||||
|
||||
setButtonLoader(false);
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: "Estimate created",
|
||||
message: "Created and Enabled successfully",
|
||||
message: "A new estimate has been added in your project.",
|
||||
});
|
||||
handleClose();
|
||||
} catch (error) {
|
||||
setButtonLoader(false);
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Estimate creation failed",
|
||||
@ -116,12 +119,12 @@ export const CreateEstimateModal: FC<TCreateEstimateModal> = observer((props) =>
|
||||
</div>
|
||||
|
||||
<div className="relative flex justify-end items-center gap-3 px-5 pt-5 border-t border-custom-border-200">
|
||||
<Button variant="neutral-primary" size="sm" onClick={handleClose}>
|
||||
<Button variant="neutral-primary" size="sm" onClick={handleClose} disabled={buttonLoader}>
|
||||
Cancel
|
||||
</Button>
|
||||
{estimatePoints && (
|
||||
<Button variant="primary" size="sm" onClick={handleCreateEstimate}>
|
||||
Create Estimate
|
||||
<Button variant="primary" size="sm" onClick={handleCreateEstimate} disabled={buttonLoader}>
|
||||
{buttonLoader ? `Creating` : `Create Estimate`}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
@ -31,7 +31,7 @@ export const EstimateCreateStageOne: FC<TEstimateCreateStageOne> = (props) => {
|
||||
<Info size={12} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : ESTIMATE_SYSTEMS[currentSystem]?.is_active ? (
|
||||
) : ESTIMATE_SYSTEMS[currentSystem]?.is_ee ? (
|
||||
<div className="relative flex items-center gap-2 cursor-no-drop text-custom-text-300">
|
||||
{ESTIMATE_SYSTEMS[currentSystem]?.name}
|
||||
<Tooltip tooltipContent={"upgrade"}>
|
||||
@ -42,7 +42,7 @@ export const EstimateCreateStageOne: FC<TEstimateCreateStageOne> = (props) => {
|
||||
<div>{ESTIMATE_SYSTEMS[currentSystem]?.name}</div>
|
||||
),
|
||||
value: system,
|
||||
disabled: !ESTIMATE_SYSTEMS[currentSystem]?.is_available || ESTIMATE_SYSTEMS[currentSystem]?.is_active,
|
||||
disabled: !ESTIMATE_SYSTEMS[currentSystem]?.is_available || ESTIMATE_SYSTEMS[currentSystem]?.is_ee,
|
||||
};
|
||||
})}
|
||||
label="Choose an estimate system"
|
||||
@ -55,7 +55,7 @@ export const EstimateCreateStageOne: FC<TEstimateCreateStageOne> = (props) => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{ESTIMATE_SYSTEMS[estimateSystem]?.is_available && !ESTIMATE_SYSTEMS[estimateSystem]?.is_active && (
|
||||
{ESTIMATE_SYSTEMS[estimateSystem]?.is_available && !ESTIMATE_SYSTEMS[estimateSystem]?.is_ee && (
|
||||
<>
|
||||
<div className="space-y-1.5">
|
||||
<div className="text-sm font-medium text-custom-text-200">Start from scratch</div>
|
||||
|
@ -1,129 +1,79 @@
|
||||
import { FC, useEffect, useMemo, useState } from "react";
|
||||
import { FC, useState } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { ChevronLeft } from "lucide-react";
|
||||
import { IEstimateFormData, TEstimateSystemKeys, TEstimatePointsObject } from "@plane/types";
|
||||
import { Button, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
// components
|
||||
import { EModalPosition, EModalWidth, ModalCore } from "@/components/core";
|
||||
import { EstimateCreateStageOne, EstimatePointCreateRoot } from "@/components/estimates";
|
||||
// constants
|
||||
import { EEstimateSystem, ESTIMATE_SYSTEMS } from "@/constants/estimates";
|
||||
// hooks
|
||||
import { useProjectEstimates } from "@/hooks/store";
|
||||
import { useEstimate, useProject, useProjectEstimates } from "@/hooks/store";
|
||||
|
||||
type TDeleteEstimateModal = {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
estimateId: string | undefined;
|
||||
isOpen: boolean;
|
||||
handleClose: () => void;
|
||||
};
|
||||
|
||||
export const DeleteEstimateModal: FC<TDeleteEstimateModal> = observer((props) => {
|
||||
// props
|
||||
const { workspaceSlug, projectId, isOpen, handleClose } = props;
|
||||
const { workspaceSlug, projectId, estimateId, isOpen, handleClose } = props;
|
||||
// hooks
|
||||
const { createEstimate } = useProjectEstimates();
|
||||
const { areEstimateEnabledByProjectId, deleteEstimate } = useProjectEstimates();
|
||||
const { asJson: estimate } = useEstimate(estimateId);
|
||||
const { updateProject } = useProject();
|
||||
// states
|
||||
const [estimateSystem, setEstimateSystem] = useState<TEstimateSystemKeys>(EEstimateSystem.CATEGORIES);
|
||||
const [estimatePoints, setEstimatePoints] = useState<TEstimatePointsObject[] | undefined>(undefined);
|
||||
const [buttonLoader, setButtonLoader] = useState(false);
|
||||
|
||||
const handleUpdatePoints = (newPoints: TEstimatePointsObject[] | undefined) => setEstimatePoints(newPoints);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
setEstimateSystem(EEstimateSystem.CATEGORIES);
|
||||
setEstimatePoints(undefined);
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
const handleCreateEstimate = async () => {
|
||||
const handleDeleteEstimate = async () => {
|
||||
try {
|
||||
if (!workspaceSlug || !projectId || !estimatePoints) return;
|
||||
|
||||
const payload: IEstimateFormData = {
|
||||
estimate: {
|
||||
name: ESTIMATE_SYSTEMS[estimateSystem]?.name,
|
||||
type: estimateSystem,
|
||||
last_used: true,
|
||||
},
|
||||
estimate_points: estimatePoints,
|
||||
};
|
||||
await createEstimate(workspaceSlug, projectId, payload);
|
||||
if (!workspaceSlug || !projectId || !estimateId) return;
|
||||
setButtonLoader(true);
|
||||
|
||||
await deleteEstimate(workspaceSlug, projectId, estimateId);
|
||||
if (areEstimateEnabledByProjectId(projectId)) {
|
||||
await updateProject(workspaceSlug, projectId, { estimate: null });
|
||||
}
|
||||
setButtonLoader(false);
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: "Estimate created",
|
||||
message: "Created and Enabled successfully",
|
||||
title: "Estimate deleted",
|
||||
message: "Estimate has been removed from your project.",
|
||||
});
|
||||
handleClose();
|
||||
} catch (error) {
|
||||
setButtonLoader(false);
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Estimate creation failed",
|
||||
message: "We were unable to create the new estimate, please try again.",
|
||||
message: "We were unable to delete the estimate, please try again.",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// derived values
|
||||
const renderEstimateStepsCount = useMemo(() => (estimatePoints ? "2" : "1"), [estimatePoints]);
|
||||
|
||||
return (
|
||||
<ModalCore isOpen={isOpen} handleClose={handleClose} position={EModalPosition.TOP} width={EModalWidth.XXL}>
|
||||
<div className="relative space-y-6 py-5">
|
||||
{/* heading */}
|
||||
<div className="relative flex justify-between items-center gap-2 px-5">
|
||||
<div className="relative flex items-center gap-1">
|
||||
{estimatePoints && (
|
||||
<div
|
||||
onClick={() => {
|
||||
setEstimateSystem(EEstimateSystem.CATEGORIES);
|
||||
handleUpdatePoints(undefined);
|
||||
}}
|
||||
className="flex-shrink-0 cursor-pointer w-5 h-5 flex justify-center items-center"
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4" />
|
||||
</div>
|
||||
)}
|
||||
<div className="text-xl font-medium text-custom-text-100">New Estimate System</div>
|
||||
</div>
|
||||
<div className="text-xs text-gray-400">Step {renderEstimateStepsCount} of 2</div>
|
||||
<div className="text-xl font-medium text-custom-text-100">Delete Estimate System</div>
|
||||
</div>
|
||||
|
||||
{/* estimate steps */}
|
||||
<div className="px-5">
|
||||
{!estimatePoints && (
|
||||
<EstimateCreateStageOne
|
||||
estimateSystem={estimateSystem}
|
||||
handleEstimateSystem={setEstimateSystem}
|
||||
handleEstimatePoints={(templateType: string) =>
|
||||
handleUpdatePoints(ESTIMATE_SYSTEMS[estimateSystem].templates[templateType].values)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{estimatePoints && (
|
||||
<>
|
||||
<EstimatePointCreateRoot
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
estimateId={undefined}
|
||||
estimateType={estimateSystem}
|
||||
estimatePoints={estimatePoints}
|
||||
setEstimatePoints={setEstimatePoints}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<div className="text-base">
|
||||
Deleting the estimate <span className="font-medium text-custom-primary-100">{estimate?.name}</span>
|
||||
system will remove it from all issues permanently. This action cannot be undone. If you add estimates
|
||||
again, you will need to update all the issues.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative flex justify-end items-center gap-3 px-5 pt-5 border-t border-custom-border-200">
|
||||
<Button variant="neutral-primary" size="sm" onClick={handleClose}>
|
||||
<Button variant="neutral-primary" size="sm" onClick={handleClose} disabled={buttonLoader}>
|
||||
Cancel
|
||||
</Button>
|
||||
{estimatePoints && (
|
||||
<Button variant="primary" size="sm" onClick={handleCreateEstimate}>
|
||||
Create Estimate
|
||||
</Button>
|
||||
)}
|
||||
<Button variant="danger" size="sm" onClick={handleDeleteEstimate} disabled={buttonLoader}>
|
||||
{buttonLoader ? "Deleting" : "Delete Estimate"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</ModalCore>
|
||||
|
25
web/components/estimates/ee-banner.tsx
Normal file
25
web/components/estimates/ee-banner.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { FC } from "react";
|
||||
import { Button } from "@plane/ui";
|
||||
|
||||
export const EstimateEEBanner: FC = (props) => {
|
||||
const {} = props;
|
||||
|
||||
return (
|
||||
<div className="border border-red-500 rounded overflow-hidden relative flex items-center">
|
||||
<div>
|
||||
<div className="text-lg">Estimate issues better with points</div>
|
||||
<div className="text-base text-custom-text-200">
|
||||
Use points to estimate scope of work better, monitor capacity, track the burn-down report for your project.
|
||||
</div>
|
||||
<div>
|
||||
<Button variant="primary" size="sm">
|
||||
Upgrade
|
||||
</Button>
|
||||
<div>Talk custom pricing</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border border-red-500 w-full">Image</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,3 +1,4 @@
|
||||
import { FC } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { Pen, Trash } from "lucide-react";
|
||||
|
||||
@ -7,12 +8,15 @@ type TEstimateListItem = {
|
||||
isEstimateEnabled: boolean;
|
||||
isEditable: boolean;
|
||||
onEditClick?: (estimateId: string) => void;
|
||||
onDeleteClick?: (estimateId: string) => void;
|
||||
};
|
||||
|
||||
export const EstimateListItemButtons: FC<TEstimateListItem> = observer((props) => {
|
||||
const { estimateId, isAdmin, isEditable, onEditClick } = props;
|
||||
return isAdmin && isEditable ? (
|
||||
<div className="flex">
|
||||
const { estimateId, isAdmin, isEditable, onEditClick, onDeleteClick } = props;
|
||||
if (!isAdmin || !isEditable) return <></>;
|
||||
|
||||
return (
|
||||
<div className="relative flex items-center gap-1">
|
||||
<button
|
||||
className="relative flex-shrink-0 w-6 h-6 flex justify-center items-center rounded cursor-pointer transition-colors overflow-hidden hover:bg-custom-background-80"
|
||||
onClick={() => onEditClick && onEditClick(estimateId)}
|
||||
@ -21,10 +25,10 @@ export const EstimateListItemButtons: FC<TEstimateListItem> = observer((props) =
|
||||
</button>
|
||||
<button
|
||||
className="relative flex-shrink-0 w-6 h-6 flex justify-center items-center rounded cursor-pointer transition-colors overflow-hidden hover:bg-custom-background-80"
|
||||
onClick={() => onEditClick && onEditClick(estimateId)}
|
||||
onClick={() => onDeleteClick && onDeleteClick(estimateId)}
|
||||
>
|
||||
<Trash size={12} />
|
||||
</button>
|
||||
</div>
|
||||
) : null;
|
||||
);
|
||||
});
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { FC } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { Pen } from "lucide-react";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
// hooks
|
||||
@ -13,10 +12,11 @@ type TEstimateListItem = {
|
||||
isEstimateEnabled: boolean;
|
||||
isEditable: boolean;
|
||||
onEditClick?: (estimateId: string) => void;
|
||||
onDeleteClick?: (estimateId: string) => void;
|
||||
};
|
||||
|
||||
export const EstimateListItem: FC<TEstimateListItem> = observer((props) => {
|
||||
const { estimateId, isAdmin, isEstimateEnabled, isEditable, onEditClick } = props;
|
||||
const { estimateId, isAdmin, isEstimateEnabled, isEditable } = props;
|
||||
// hooks
|
||||
const { estimateById } = useProjectEstimates();
|
||||
const { estimatePointIds, estimatePointById } = useEstimate(estimateId);
|
||||
|
@ -9,10 +9,11 @@ type TEstimateList = {
|
||||
isEstimateEnabled?: boolean;
|
||||
isEditable?: boolean;
|
||||
onEditClick?: (estimateId: string) => void;
|
||||
onDeleteClick?: (estimateId: string) => void;
|
||||
};
|
||||
|
||||
export const EstimateList: FC<TEstimateList> = observer((props) => {
|
||||
const { estimateIds, isAdmin, isEstimateEnabled = false, isEditable = false, onEditClick } = props;
|
||||
const { estimateIds, isAdmin, isEstimateEnabled = false, isEditable = false, onEditClick, onDeleteClick } = props;
|
||||
|
||||
if (!estimateIds || estimateIds?.length <= 0) return <></>;
|
||||
return (
|
||||
@ -26,6 +27,7 @@ export const EstimateList: FC<TEstimateList> = observer((props) => {
|
||||
isEstimateEnabled={isEstimateEnabled}
|
||||
isEditable={isEditable}
|
||||
onEditClick={onEditClick}
|
||||
onDeleteClick={onDeleteClick}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
@ -2,6 +2,7 @@ export * from "./root";
|
||||
|
||||
export * from "./empty-screen";
|
||||
export * from "./loader-screen";
|
||||
export * from "./ee-banner";
|
||||
|
||||
export * from "./estimate-search";
|
||||
export * from "./estimate-disable-switch";
|
||||
@ -9,11 +10,12 @@ export * from "./estimate-disable-switch";
|
||||
// estimates
|
||||
export * from "./estimate-list";
|
||||
export * from "./estimate-list-item";
|
||||
export * from "./estimate-list-item-buttons";
|
||||
|
||||
// create
|
||||
export * from "./create";
|
||||
|
||||
// create
|
||||
// update
|
||||
export * from "./update";
|
||||
|
||||
// delete
|
||||
@ -21,5 +23,3 @@ export * from "./delete";
|
||||
|
||||
// estimate points
|
||||
export * from "./points";
|
||||
|
||||
export * from "./estimate-list-item-buttons";
|
||||
|
@ -8,7 +8,9 @@ import {
|
||||
EstimateDisableSwitch,
|
||||
CreateEstimateModal,
|
||||
UpdateEstimateModal,
|
||||
DeleteEstimateModal,
|
||||
EstimateList,
|
||||
EstimateEEBanner,
|
||||
} from "@/components/estimates";
|
||||
// hooks
|
||||
import { useProject, useProjectEstimates } from "@/hooks/store";
|
||||
@ -27,6 +29,7 @@ export const EstimateRoot: FC<TEstimateRoot> = observer((props) => {
|
||||
// states
|
||||
const [isEstimateCreateModalOpen, setIsEstimateCreateModalOpen] = useState(false);
|
||||
const [estimateToUpdate, setEstimateToUpdate] = useState<string | undefined>();
|
||||
const [estimateToDelete, setEstimateToDelete] = useState<string | undefined>();
|
||||
|
||||
const { isLoading: isSWRLoading } = useSWR(
|
||||
workspaceSlug && projectId ? `PROJECT_ESTIMATES_${workspaceSlug}_${projectId}` : null,
|
||||
@ -38,7 +41,7 @@ export const EstimateRoot: FC<TEstimateRoot> = observer((props) => {
|
||||
{loader === "init-loader" || isSWRLoading ? (
|
||||
<EstimateLoaderScreen />
|
||||
) : (
|
||||
<div className="space-y-10">
|
||||
<div className="space-y-12">
|
||||
{/* header */}
|
||||
<div className="text-xl font-medium text-custom-text-100 border-b border-custom-border-200 py-3.5">
|
||||
Estimates
|
||||
@ -46,7 +49,7 @@ export const EstimateRoot: FC<TEstimateRoot> = observer((props) => {
|
||||
|
||||
{/* current active estimate section */}
|
||||
{currentActiveEstimateId ? (
|
||||
<div className="space-y-4">
|
||||
<div className="">
|
||||
{/* estimates activated deactivated section */}
|
||||
<div className="relative border-b border-custom-border-200 pb-4 flex justify-between items-center gap-3">
|
||||
<div className="space-y-1">
|
||||
@ -64,6 +67,7 @@ export const EstimateRoot: FC<TEstimateRoot> = observer((props) => {
|
||||
isEstimateEnabled={Boolean(currentProjectDetails?.estimate)}
|
||||
isEditable
|
||||
onEditClick={(estimateId: string) => setEstimateToUpdate(estimateId)}
|
||||
onDeleteClick={(estimateId: string) => setEstimateToDelete(estimateId)}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
@ -72,7 +76,7 @@ export const EstimateRoot: FC<TEstimateRoot> = observer((props) => {
|
||||
|
||||
{/* archived estimates section */}
|
||||
{archivedEstimateIds && archivedEstimateIds.length > 0 && (
|
||||
<div>
|
||||
<div className="">
|
||||
<div className="border-b border-custom-border-200 space-y-1 pb-4">
|
||||
<h3 className="text-lg font-medium text-custom-text-100">Archived estimates</h3>
|
||||
<p className="text-sm text-custom-text-200">
|
||||
@ -89,25 +93,28 @@ export const EstimateRoot: FC<TEstimateRoot> = observer((props) => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* <EstimateEEBanner /> */}
|
||||
|
||||
{/* CRUD modals */}
|
||||
<CreateEstimateModal
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
isOpen={isEstimateCreateModalOpen}
|
||||
handleClose={() => {
|
||||
setIsEstimateCreateModalOpen(false);
|
||||
setEstimateToUpdate(undefined);
|
||||
}}
|
||||
handleClose={() => setIsEstimateCreateModalOpen(false)}
|
||||
/>
|
||||
<UpdateEstimateModal
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
estimateId={estimateToUpdate ? estimateToUpdate : undefined}
|
||||
isOpen={estimateToUpdate ? true : false}
|
||||
handleClose={() => {
|
||||
setIsEstimateCreateModalOpen(false);
|
||||
setEstimateToUpdate(undefined);
|
||||
}}
|
||||
handleClose={() => setEstimateToUpdate(undefined)}
|
||||
/>
|
||||
<DeleteEstimateModal
|
||||
workspaceSlug={workspaceSlug}
|
||||
projectId={projectId}
|
||||
estimateId={estimateToDelete ? estimateToDelete : undefined}
|
||||
isOpen={estimateToDelete ? true : false}
|
||||
handleClose={() => setEstimateToDelete(undefined)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -22,13 +22,13 @@ export const EstimateUpdateStageOne: FC<TEstimateUpdateStageOne> = (props) => {
|
||||
key={stage.key}
|
||||
className={cn(
|
||||
"border border-custom-border-300 cursor-pointer space-y-1 p-3 rounded transition-colors",
|
||||
stage?.is_active ? `bg-custom-background-90` : `hover:bg-custom-background-90`
|
||||
stage?.is_ee ? `bg-custom-background-90` : `hover:bg-custom-background-90`
|
||||
)}
|
||||
onClick={() => !stage?.is_active && handleEstimateEditType(stage.key)}
|
||||
onClick={() => !stage?.is_ee && handleEstimateEditType(stage.key)}
|
||||
>
|
||||
<h3 className="text-base font-medium relative flex items-center gap-2">
|
||||
{stage.title}
|
||||
{stage?.is_active && (
|
||||
{stage?.is_ee && (
|
||||
<Tooltip tooltipContent={"upgrade"}>
|
||||
<Crown size={12} className="text-amber-400" />
|
||||
</Tooltip>
|
||||
|
@ -49,7 +49,7 @@ export const ESTIMATE_SYSTEMS: TEstimateSystems = {
|
||||
},
|
||||
},
|
||||
is_available: true,
|
||||
is_active: false,
|
||||
is_ee: false,
|
||||
},
|
||||
points: {
|
||||
name: "Points",
|
||||
@ -102,7 +102,7 @@ export const ESTIMATE_SYSTEMS: TEstimateSystems = {
|
||||
},
|
||||
},
|
||||
is_available: true,
|
||||
is_active: true,
|
||||
is_ee: false,
|
||||
},
|
||||
time: {
|
||||
name: "Time",
|
||||
@ -124,7 +124,7 @@ export const ESTIMATE_SYSTEMS: TEstimateSystems = {
|
||||
},
|
||||
},
|
||||
is_available: false,
|
||||
is_active: true,
|
||||
is_ee: true,
|
||||
},
|
||||
};
|
||||
|
||||
@ -133,12 +133,12 @@ export const ESTIMATE_OPTIONS_STAGE_ONE = [
|
||||
key: EEstimateUpdateStages.EDIT,
|
||||
title: "Add, update or remove estimates",
|
||||
description: "Manage current system either adding, updating or removing the points or categories.",
|
||||
is_active: false,
|
||||
is_ee: true,
|
||||
},
|
||||
{
|
||||
key: EEstimateUpdateStages.SWITCH,
|
||||
title: "Change estimate type",
|
||||
description: "Convert your points system to categories system and vice versa.",
|
||||
is_active: true,
|
||||
is_ee: true,
|
||||
},
|
||||
];
|
||||
|
@ -73,7 +73,7 @@ class EstimateService extends APIService {
|
||||
}
|
||||
}
|
||||
|
||||
async deleteEstimate(workspaceSlug: string, projectId: string, estimateId: string): Promise<any> {
|
||||
async deleteEstimate(workspaceSlug: string, projectId: string, estimateId: string): Promise<void> {
|
||||
try {
|
||||
await this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/estimates/${estimateId}/`);
|
||||
} catch (error) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import orderBy from "lodash/orderBy";
|
||||
import set from "lodash/set";
|
||||
import unset from "lodash/unset";
|
||||
import update from "lodash/update";
|
||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||
import { computedFn } from "mobx-utils";
|
||||
@ -40,6 +41,7 @@ export interface IProjectEstimateStore {
|
||||
projectId: string,
|
||||
data: IEstimateFormData
|
||||
) => Promise<IEstimateType | undefined>;
|
||||
deleteEstimate: (workspaceSlug: string, projectId: string, estimateId: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export class ProjectEstimateStore implements IProjectEstimateStore {
|
||||
@ -62,6 +64,7 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
|
||||
getProjectEstimates: action,
|
||||
getEstimateById: action,
|
||||
createEstimate: action,
|
||||
deleteEstimate: action,
|
||||
});
|
||||
}
|
||||
|
||||
@ -249,9 +252,10 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
|
||||
|
||||
const estimate = await estimateService.createEstimate(workspaceSlug, projectId, payload);
|
||||
if (estimate) {
|
||||
await this.store.projectRoot.project.updateProject(workspaceSlug, projectId, {
|
||||
estimate: estimate.id,
|
||||
});
|
||||
// update estimate_id in current project
|
||||
// await this.store.projectRoot.project.updateProject(workspaceSlug, projectId, {
|
||||
// estimate: estimate.id,
|
||||
// });
|
||||
runInAction(() => {
|
||||
if (estimate.id) set(this.estimates, [estimate.id], new Estimate(this.store, estimate));
|
||||
});
|
||||
@ -268,16 +272,21 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
|
||||
};
|
||||
|
||||
/**
|
||||
* @description deletes the given estimate for the given project
|
||||
* @description delete the estimate for a project
|
||||
* @param workspaceSlug
|
||||
* @param projectId
|
||||
* @param estimateId
|
||||
*/
|
||||
deleteEstimate = async (workspaceSlug: string, projectId: string, estimateId: string) => {
|
||||
await estimateService.deleteEstimate(workspaceSlug, projectId, estimateId).then(() => {
|
||||
runInAction(() => {
|
||||
delete this.estimates[estimateId];
|
||||
});
|
||||
});
|
||||
try {
|
||||
await estimateService.deleteEstimate(workspaceSlug, projectId, estimateId);
|
||||
runInAction(() => estimateId && unset(this.estimates, [estimateId]));
|
||||
} catch (error) {
|
||||
this.error = {
|
||||
status: "error",
|
||||
message: "Error deleting estimate",
|
||||
};
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user