fix: updated and handled the estimate points

This commit is contained in:
guru_sainath 2024-06-03 17:06:34 +05:30
parent a725385190
commit 16fa9c9baa
14 changed files with 129 additions and 129 deletions

View File

@ -63,7 +63,7 @@ export type TEstimateSystem = {
name: string; name: string;
templates: Record<string, TTemplateValues>; templates: Record<string, TTemplateValues>;
is_available: boolean; is_available: boolean;
is_active: boolean; is_ee: boolean;
}; };
export type TEstimateSystems = { export type TEstimateSystems = {

View File

@ -26,6 +26,7 @@ export const CreateEstimateModal: FC<TCreateEstimateModal> = observer((props) =>
// states // states
const [estimateSystem, setEstimateSystem] = useState<TEstimateSystemKeys>(EEstimateSystem.CATEGORIES); const [estimateSystem, setEstimateSystem] = useState<TEstimateSystemKeys>(EEstimateSystem.CATEGORIES);
const [estimatePoints, setEstimatePoints] = useState<TEstimatePointsObject[] | undefined>(undefined); const [estimatePoints, setEstimatePoints] = useState<TEstimatePointsObject[] | undefined>(undefined);
const [buttonLoader, setButtonLoader] = useState(false);
const handleUpdatePoints = (newPoints: TEstimatePointsObject[] | undefined) => setEstimatePoints(newPoints); const handleUpdatePoints = (newPoints: TEstimatePointsObject[] | undefined) => setEstimatePoints(newPoints);
@ -39,7 +40,7 @@ export const CreateEstimateModal: FC<TCreateEstimateModal> = observer((props) =>
const handleCreateEstimate = async () => { const handleCreateEstimate = async () => {
try { try {
if (!workspaceSlug || !projectId || !estimatePoints) return; if (!workspaceSlug || !projectId || !estimatePoints) return;
setButtonLoader(true);
const payload: IEstimateFormData = { const payload: IEstimateFormData = {
estimate: { estimate: {
name: ESTIMATE_SYSTEMS[estimateSystem]?.name, name: ESTIMATE_SYSTEMS[estimateSystem]?.name,
@ -50,13 +51,15 @@ export const CreateEstimateModal: FC<TCreateEstimateModal> = observer((props) =>
}; };
await createEstimate(workspaceSlug, projectId, payload); await createEstimate(workspaceSlug, projectId, payload);
setButtonLoader(false);
setToast({ setToast({
type: TOAST_TYPE.SUCCESS, type: TOAST_TYPE.SUCCESS,
title: "Estimate created", title: "Estimate created",
message: "Created and Enabled successfully", message: "A new estimate has been added in your project.",
}); });
handleClose(); handleClose();
} catch (error) { } catch (error) {
setButtonLoader(false);
setToast({ setToast({
type: TOAST_TYPE.ERROR, type: TOAST_TYPE.ERROR,
title: "Estimate creation failed", title: "Estimate creation failed",
@ -116,12 +119,12 @@ export const CreateEstimateModal: FC<TCreateEstimateModal> = observer((props) =>
</div> </div>
<div className="relative flex justify-end items-center gap-3 px-5 pt-5 border-t border-custom-border-200"> <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 Cancel
</Button> </Button>
{estimatePoints && ( {estimatePoints && (
<Button variant="primary" size="sm" onClick={handleCreateEstimate}> <Button variant="primary" size="sm" onClick={handleCreateEstimate} disabled={buttonLoader}>
Create Estimate {buttonLoader ? `Creating` : `Create Estimate`}
</Button> </Button>
)} )}
</div> </div>

View File

@ -31,7 +31,7 @@ export const EstimateCreateStageOne: FC<TEstimateCreateStageOne> = (props) => {
<Info size={12} /> <Info size={12} />
</Tooltip> </Tooltip>
</div> </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"> <div className="relative flex items-center gap-2 cursor-no-drop text-custom-text-300">
{ESTIMATE_SYSTEMS[currentSystem]?.name} {ESTIMATE_SYSTEMS[currentSystem]?.name}
<Tooltip tooltipContent={"upgrade"}> <Tooltip tooltipContent={"upgrade"}>
@ -42,7 +42,7 @@ export const EstimateCreateStageOne: FC<TEstimateCreateStageOne> = (props) => {
<div>{ESTIMATE_SYSTEMS[currentSystem]?.name}</div> <div>{ESTIMATE_SYSTEMS[currentSystem]?.name}</div>
), ),
value: system, 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" label="Choose an estimate system"
@ -55,7 +55,7 @@ export const EstimateCreateStageOne: FC<TEstimateCreateStageOne> = (props) => {
/> />
</div> </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="space-y-1.5">
<div className="text-sm font-medium text-custom-text-200">Start from scratch</div> <div className="text-sm font-medium text-custom-text-200">Start from scratch</div>

View File

@ -1,129 +1,79 @@
import { FC, useEffect, useMemo, useState } from "react"; import { FC, useState } from "react";
import { observer } from "mobx-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"; import { Button, TOAST_TYPE, setToast } from "@plane/ui";
// components // components
import { EModalPosition, EModalWidth, ModalCore } from "@/components/core"; import { EModalPosition, EModalWidth, ModalCore } from "@/components/core";
import { EstimateCreateStageOne, EstimatePointCreateRoot } from "@/components/estimates";
// constants
import { EEstimateSystem, ESTIMATE_SYSTEMS } from "@/constants/estimates";
// hooks // hooks
import { useProjectEstimates } from "@/hooks/store"; import { useEstimate, useProject, useProjectEstimates } from "@/hooks/store";
type TDeleteEstimateModal = { type TDeleteEstimateModal = {
workspaceSlug: string; workspaceSlug: string;
projectId: string; projectId: string;
estimateId: string | undefined;
isOpen: boolean; isOpen: boolean;
handleClose: () => void; handleClose: () => void;
}; };
export const DeleteEstimateModal: FC<TDeleteEstimateModal> = observer((props) => { export const DeleteEstimateModal: FC<TDeleteEstimateModal> = observer((props) => {
// props // props
const { workspaceSlug, projectId, isOpen, handleClose } = props; const { workspaceSlug, projectId, estimateId, isOpen, handleClose } = props;
// hooks // hooks
const { createEstimate } = useProjectEstimates(); const { areEstimateEnabledByProjectId, deleteEstimate } = useProjectEstimates();
const { asJson: estimate } = useEstimate(estimateId);
const { updateProject } = useProject();
// states // states
const [estimateSystem, setEstimateSystem] = useState<TEstimateSystemKeys>(EEstimateSystem.CATEGORIES); const [buttonLoader, setButtonLoader] = useState(false);
const [estimatePoints, setEstimatePoints] = useState<TEstimatePointsObject[] | undefined>(undefined);
const handleUpdatePoints = (newPoints: TEstimatePointsObject[] | undefined) => setEstimatePoints(newPoints); const handleDeleteEstimate = async () => {
useEffect(() => {
if (isOpen) {
setEstimateSystem(EEstimateSystem.CATEGORIES);
setEstimatePoints(undefined);
}
}, [isOpen]);
const handleCreateEstimate = async () => {
try { try {
if (!workspaceSlug || !projectId || !estimatePoints) return; if (!workspaceSlug || !projectId || !estimateId) return;
setButtonLoader(true);
const payload: IEstimateFormData = {
estimate: {
name: ESTIMATE_SYSTEMS[estimateSystem]?.name,
type: estimateSystem,
last_used: true,
},
estimate_points: estimatePoints,
};
await createEstimate(workspaceSlug, projectId, payload);
await deleteEstimate(workspaceSlug, projectId, estimateId);
if (areEstimateEnabledByProjectId(projectId)) {
await updateProject(workspaceSlug, projectId, { estimate: null });
}
setButtonLoader(false);
setToast({ setToast({
type: TOAST_TYPE.SUCCESS, type: TOAST_TYPE.SUCCESS,
title: "Estimate created", title: "Estimate deleted",
message: "Created and Enabled successfully", message: "Estimate has been removed from your project.",
}); });
handleClose(); handleClose();
} catch (error) { } catch (error) {
setButtonLoader(false);
setToast({ setToast({
type: TOAST_TYPE.ERROR, type: TOAST_TYPE.ERROR,
title: "Estimate creation failed", 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 ( return (
<ModalCore isOpen={isOpen} handleClose={handleClose} position={EModalPosition.TOP} width={EModalWidth.XXL}> <ModalCore isOpen={isOpen} handleClose={handleClose} position={EModalPosition.TOP} width={EModalWidth.XXL}>
<div className="relative space-y-6 py-5"> <div className="relative space-y-6 py-5">
{/* heading */} {/* heading */}
<div className="relative flex justify-between items-center gap-2 px-5"> <div className="relative flex justify-between items-center gap-2 px-5">
<div className="relative flex items-center gap-1"> <div className="text-xl font-medium text-custom-text-100">Delete Estimate System</div>
{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> </div>
{/* estimate steps */} {/* estimate steps */}
<div className="px-5"> <div className="px-5">
{!estimatePoints && ( <div className="text-base">
<EstimateCreateStageOne Deleting the estimate <span className="font-medium text-custom-primary-100">{estimate?.name}</span>
estimateSystem={estimateSystem} &nbsp;system will remove it from all issues permanently. This action cannot be undone. If you add estimates
handleEstimateSystem={setEstimateSystem} again, you will need to update all the issues.
handleEstimatePoints={(templateType: string) => </div>
handleUpdatePoints(ESTIMATE_SYSTEMS[estimateSystem].templates[templateType].values)
}
/>
)}
{estimatePoints && (
<>
<EstimatePointCreateRoot
workspaceSlug={workspaceSlug}
projectId={projectId}
estimateId={undefined}
estimateType={estimateSystem}
estimatePoints={estimatePoints}
setEstimatePoints={setEstimatePoints}
/>
</>
)}
</div> </div>
<div className="relative flex justify-end items-center gap-3 px-5 pt-5 border-t border-custom-border-200"> <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 Cancel
</Button> </Button>
{estimatePoints && ( <Button variant="danger" size="sm" onClick={handleDeleteEstimate} disabled={buttonLoader}>
<Button variant="primary" size="sm" onClick={handleCreateEstimate}> {buttonLoader ? "Deleting" : "Delete Estimate"}
Create Estimate
</Button> </Button>
)}
</div> </div>
</div> </div>
</ModalCore> </ModalCore>

View 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>
);
};

View File

@ -1,3 +1,4 @@
import { FC } from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { Pen, Trash } from "lucide-react"; import { Pen, Trash } from "lucide-react";
@ -7,12 +8,15 @@ type TEstimateListItem = {
isEstimateEnabled: boolean; isEstimateEnabled: boolean;
isEditable: boolean; isEditable: boolean;
onEditClick?: (estimateId: string) => void; onEditClick?: (estimateId: string) => void;
onDeleteClick?: (estimateId: string) => void;
}; };
export const EstimateListItemButtons: FC<TEstimateListItem> = observer((props) => { export const EstimateListItemButtons: FC<TEstimateListItem> = observer((props) => {
const { estimateId, isAdmin, isEditable, onEditClick } = props; const { estimateId, isAdmin, isEditable, onEditClick, onDeleteClick } = props;
return isAdmin && isEditable ? ( if (!isAdmin || !isEditable) return <></>;
<div className="flex">
return (
<div className="relative flex items-center gap-1">
<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" 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={() => onEditClick && onEditClick(estimateId)}
@ -21,10 +25,10 @@ export const EstimateListItemButtons: FC<TEstimateListItem> = observer((props) =
</button> </button>
<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" 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} /> <Trash size={12} />
</button> </button>
</div> </div>
) : null; );
}); });

View File

@ -1,6 +1,5 @@
import { FC } from "react"; import { FC } from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { Pen } from "lucide-react";
// helpers // helpers
import { cn } from "@/helpers/common.helper"; import { cn } from "@/helpers/common.helper";
// hooks // hooks
@ -13,10 +12,11 @@ type TEstimateListItem = {
isEstimateEnabled: boolean; isEstimateEnabled: boolean;
isEditable: boolean; isEditable: boolean;
onEditClick?: (estimateId: string) => void; onEditClick?: (estimateId: string) => void;
onDeleteClick?: (estimateId: string) => void;
}; };
export const EstimateListItem: FC<TEstimateListItem> = observer((props) => { export const EstimateListItem: FC<TEstimateListItem> = observer((props) => {
const { estimateId, isAdmin, isEstimateEnabled, isEditable, onEditClick } = props; const { estimateId, isAdmin, isEstimateEnabled, isEditable } = props;
// hooks // hooks
const { estimateById } = useProjectEstimates(); const { estimateById } = useProjectEstimates();
const { estimatePointIds, estimatePointById } = useEstimate(estimateId); const { estimatePointIds, estimatePointById } = useEstimate(estimateId);

View File

@ -9,10 +9,11 @@ type TEstimateList = {
isEstimateEnabled?: boolean; isEstimateEnabled?: boolean;
isEditable?: boolean; isEditable?: boolean;
onEditClick?: (estimateId: string) => void; onEditClick?: (estimateId: string) => void;
onDeleteClick?: (estimateId: string) => void;
}; };
export const EstimateList: FC<TEstimateList> = observer((props) => { 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 <></>; if (!estimateIds || estimateIds?.length <= 0) return <></>;
return ( return (
@ -26,6 +27,7 @@ export const EstimateList: FC<TEstimateList> = observer((props) => {
isEstimateEnabled={isEstimateEnabled} isEstimateEnabled={isEstimateEnabled}
isEditable={isEditable} isEditable={isEditable}
onEditClick={onEditClick} onEditClick={onEditClick}
onDeleteClick={onDeleteClick}
/> />
))} ))}
</div> </div>

View File

@ -2,6 +2,7 @@ export * from "./root";
export * from "./empty-screen"; export * from "./empty-screen";
export * from "./loader-screen"; export * from "./loader-screen";
export * from "./ee-banner";
export * from "./estimate-search"; export * from "./estimate-search";
export * from "./estimate-disable-switch"; export * from "./estimate-disable-switch";
@ -9,11 +10,12 @@ export * from "./estimate-disable-switch";
// estimates // estimates
export * from "./estimate-list"; export * from "./estimate-list";
export * from "./estimate-list-item"; export * from "./estimate-list-item";
export * from "./estimate-list-item-buttons";
// create // create
export * from "./create"; export * from "./create";
// create // update
export * from "./update"; export * from "./update";
// delete // delete
@ -21,5 +23,3 @@ export * from "./delete";
// estimate points // estimate points
export * from "./points"; export * from "./points";
export * from "./estimate-list-item-buttons";

View File

@ -8,7 +8,9 @@ import {
EstimateDisableSwitch, EstimateDisableSwitch,
CreateEstimateModal, CreateEstimateModal,
UpdateEstimateModal, UpdateEstimateModal,
DeleteEstimateModal,
EstimateList, EstimateList,
EstimateEEBanner,
} from "@/components/estimates"; } from "@/components/estimates";
// hooks // hooks
import { useProject, useProjectEstimates } from "@/hooks/store"; import { useProject, useProjectEstimates } from "@/hooks/store";
@ -27,6 +29,7 @@ export const EstimateRoot: FC<TEstimateRoot> = observer((props) => {
// states // states
const [isEstimateCreateModalOpen, setIsEstimateCreateModalOpen] = useState(false); const [isEstimateCreateModalOpen, setIsEstimateCreateModalOpen] = useState(false);
const [estimateToUpdate, setEstimateToUpdate] = useState<string | undefined>(); const [estimateToUpdate, setEstimateToUpdate] = useState<string | undefined>();
const [estimateToDelete, setEstimateToDelete] = useState<string | undefined>();
const { isLoading: isSWRLoading } = useSWR( const { isLoading: isSWRLoading } = useSWR(
workspaceSlug && projectId ? `PROJECT_ESTIMATES_${workspaceSlug}_${projectId}` : null, workspaceSlug && projectId ? `PROJECT_ESTIMATES_${workspaceSlug}_${projectId}` : null,
@ -38,7 +41,7 @@ export const EstimateRoot: FC<TEstimateRoot> = observer((props) => {
{loader === "init-loader" || isSWRLoading ? ( {loader === "init-loader" || isSWRLoading ? (
<EstimateLoaderScreen /> <EstimateLoaderScreen />
) : ( ) : (
<div className="space-y-10"> <div className="space-y-12">
{/* header */} {/* header */}
<div className="text-xl font-medium text-custom-text-100 border-b border-custom-border-200 py-3.5"> <div className="text-xl font-medium text-custom-text-100 border-b border-custom-border-200 py-3.5">
Estimates Estimates
@ -46,7 +49,7 @@ export const EstimateRoot: FC<TEstimateRoot> = observer((props) => {
{/* current active estimate section */} {/* current active estimate section */}
{currentActiveEstimateId ? ( {currentActiveEstimateId ? (
<div className="space-y-4"> <div className="">
{/* estimates activated deactivated section */} {/* estimates activated deactivated section */}
<div className="relative border-b border-custom-border-200 pb-4 flex justify-between items-center gap-3"> <div className="relative border-b border-custom-border-200 pb-4 flex justify-between items-center gap-3">
<div className="space-y-1"> <div className="space-y-1">
@ -64,6 +67,7 @@ export const EstimateRoot: FC<TEstimateRoot> = observer((props) => {
isEstimateEnabled={Boolean(currentProjectDetails?.estimate)} isEstimateEnabled={Boolean(currentProjectDetails?.estimate)}
isEditable isEditable
onEditClick={(estimateId: string) => setEstimateToUpdate(estimateId)} onEditClick={(estimateId: string) => setEstimateToUpdate(estimateId)}
onDeleteClick={(estimateId: string) => setEstimateToDelete(estimateId)}
/> />
</div> </div>
) : ( ) : (
@ -72,7 +76,7 @@ export const EstimateRoot: FC<TEstimateRoot> = observer((props) => {
{/* archived estimates section */} {/* archived estimates section */}
{archivedEstimateIds && archivedEstimateIds.length > 0 && ( {archivedEstimateIds && archivedEstimateIds.length > 0 && (
<div> <div className="">
<div className="border-b border-custom-border-200 space-y-1 pb-4"> <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> <h3 className="text-lg font-medium text-custom-text-100">Archived estimates</h3>
<p className="text-sm text-custom-text-200"> <p className="text-sm text-custom-text-200">
@ -89,25 +93,28 @@ export const EstimateRoot: FC<TEstimateRoot> = observer((props) => {
</div> </div>
)} )}
{/* <EstimateEEBanner /> */}
{/* CRUD modals */} {/* CRUD modals */}
<CreateEstimateModal <CreateEstimateModal
workspaceSlug={workspaceSlug} workspaceSlug={workspaceSlug}
projectId={projectId} projectId={projectId}
isOpen={isEstimateCreateModalOpen} isOpen={isEstimateCreateModalOpen}
handleClose={() => { handleClose={() => setIsEstimateCreateModalOpen(false)}
setIsEstimateCreateModalOpen(false);
setEstimateToUpdate(undefined);
}}
/> />
<UpdateEstimateModal <UpdateEstimateModal
workspaceSlug={workspaceSlug} workspaceSlug={workspaceSlug}
projectId={projectId} projectId={projectId}
estimateId={estimateToUpdate ? estimateToUpdate : undefined} estimateId={estimateToUpdate ? estimateToUpdate : undefined}
isOpen={estimateToUpdate ? true : false} isOpen={estimateToUpdate ? true : false}
handleClose={() => { handleClose={() => setEstimateToUpdate(undefined)}
setIsEstimateCreateModalOpen(false); />
setEstimateToUpdate(undefined); <DeleteEstimateModal
}} workspaceSlug={workspaceSlug}
projectId={projectId}
estimateId={estimateToDelete ? estimateToDelete : undefined}
isOpen={estimateToDelete ? true : false}
handleClose={() => setEstimateToDelete(undefined)}
/> />
</div> </div>
); );

View File

@ -22,13 +22,13 @@ export const EstimateUpdateStageOne: FC<TEstimateUpdateStageOne> = (props) => {
key={stage.key} key={stage.key}
className={cn( className={cn(
"border border-custom-border-300 cursor-pointer space-y-1 p-3 rounded transition-colors", "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"> <h3 className="text-base font-medium relative flex items-center gap-2">
{stage.title} {stage.title}
{stage?.is_active && ( {stage?.is_ee && (
<Tooltip tooltipContent={"upgrade"}> <Tooltip tooltipContent={"upgrade"}>
<Crown size={12} className="text-amber-400" /> <Crown size={12} className="text-amber-400" />
</Tooltip> </Tooltip>

View File

@ -49,7 +49,7 @@ export const ESTIMATE_SYSTEMS: TEstimateSystems = {
}, },
}, },
is_available: true, is_available: true,
is_active: false, is_ee: false,
}, },
points: { points: {
name: "Points", name: "Points",
@ -102,7 +102,7 @@ export const ESTIMATE_SYSTEMS: TEstimateSystems = {
}, },
}, },
is_available: true, is_available: true,
is_active: true, is_ee: false,
}, },
time: { time: {
name: "Time", name: "Time",
@ -124,7 +124,7 @@ export const ESTIMATE_SYSTEMS: TEstimateSystems = {
}, },
}, },
is_available: false, is_available: false,
is_active: true, is_ee: true,
}, },
}; };
@ -133,12 +133,12 @@ export const ESTIMATE_OPTIONS_STAGE_ONE = [
key: EEstimateUpdateStages.EDIT, key: EEstimateUpdateStages.EDIT,
title: "Add, update or remove estimates", title: "Add, update or remove estimates",
description: "Manage current system either adding, updating or removing the points or categories.", description: "Manage current system either adding, updating or removing the points or categories.",
is_active: false, is_ee: true,
}, },
{ {
key: EEstimateUpdateStages.SWITCH, key: EEstimateUpdateStages.SWITCH,
title: "Change estimate type", title: "Change estimate type",
description: "Convert your points system to categories system and vice versa.", description: "Convert your points system to categories system and vice versa.",
is_active: true, is_ee: true,
}, },
]; ];

View File

@ -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 { try {
await this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/estimates/${estimateId}/`); await this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/estimates/${estimateId}/`);
} catch (error) { } catch (error) {

View File

@ -1,5 +1,6 @@
import orderBy from "lodash/orderBy"; import orderBy from "lodash/orderBy";
import set from "lodash/set"; import set from "lodash/set";
import unset from "lodash/unset";
import update from "lodash/update"; import update from "lodash/update";
import { action, computed, makeObservable, observable, runInAction } from "mobx"; import { action, computed, makeObservable, observable, runInAction } from "mobx";
import { computedFn } from "mobx-utils"; import { computedFn } from "mobx-utils";
@ -40,6 +41,7 @@ export interface IProjectEstimateStore {
projectId: string, projectId: string,
data: IEstimateFormData data: IEstimateFormData
) => Promise<IEstimateType | undefined>; ) => Promise<IEstimateType | undefined>;
deleteEstimate: (workspaceSlug: string, projectId: string, estimateId: string) => Promise<void>;
} }
export class ProjectEstimateStore implements IProjectEstimateStore { export class ProjectEstimateStore implements IProjectEstimateStore {
@ -62,6 +64,7 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
getProjectEstimates: action, getProjectEstimates: action,
getEstimateById: action, getEstimateById: action,
createEstimate: action, createEstimate: action,
deleteEstimate: action,
}); });
} }
@ -249,9 +252,10 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
const estimate = await estimateService.createEstimate(workspaceSlug, projectId, payload); const estimate = await estimateService.createEstimate(workspaceSlug, projectId, payload);
if (estimate) { if (estimate) {
await this.store.projectRoot.project.updateProject(workspaceSlug, projectId, { // update estimate_id in current project
estimate: estimate.id, // await this.store.projectRoot.project.updateProject(workspaceSlug, projectId, {
}); // estimate: estimate.id,
// });
runInAction(() => { runInAction(() => {
if (estimate.id) set(this.estimates, [estimate.id], new Estimate(this.store, estimate)); 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 workspaceSlug
* @param projectId * @param projectId
* @param estimateId * @param estimateId
*/ */
deleteEstimate = async (workspaceSlug: string, projectId: string, estimateId: string) => { deleteEstimate = async (workspaceSlug: string, projectId: string, estimateId: string) => {
await estimateService.deleteEstimate(workspaceSlug, projectId, estimateId).then(() => { try {
runInAction(() => { await estimateService.deleteEstimate(workspaceSlug, projectId, estimateId);
delete this.estimates[estimateId]; runInAction(() => estimateId && unset(this.estimates, [estimateId]));
}); } catch (error) {
}); this.error = {
status: "error",
message: "Error deleting estimate",
};
throw error;
}
}; };
} }