chore: integrated new endpoints

This commit is contained in:
guru_sainath 2024-05-27 15:51:27 +05:30
parent be03d90c86
commit 1cd31f4705
16 changed files with 670 additions and 187 deletions

View File

@ -1,4 +1,3 @@
import { IWorkspace, IProject } from "./";
import { EEstimateSystem, EEstimateUpdateStages } from "./enums"; import { EEstimateSystem, EEstimateUpdateStages } from "./enums";
export interface IEstimatePoint { export interface IEstimatePoint {
@ -27,9 +26,8 @@ export interface IEstimate {
type: TEstimateSystemKeys | undefined; // categories, points, time type: TEstimateSystemKeys | undefined; // categories, points, time
points: IEstimatePoint[] | undefined; points: IEstimatePoint[] | undefined;
workspace: string | undefined; workspace: string | undefined;
workspace_detail: IWorkspace | undefined;
project: string | undefined; project: string | undefined;
project_detail: IProject | undefined; last_used: boolean | undefined;
created_at: Date | undefined; created_at: Date | undefined;
updated_at: Date | undefined; updated_at: Date | undefined;
created_by: string | undefined; created_by: string | undefined;
@ -38,7 +36,9 @@ export interface IEstimate {
export interface IEstimateFormData { export interface IEstimateFormData {
estimate?: { estimate?: {
type: string; name?: string;
type?: string;
last_used?: boolean;
}; };
estimate_points: { estimate_points: {
id?: string | undefined; id?: string | undefined;

View File

@ -59,7 +59,9 @@ export const CreateEstimateModal: FC<TCreateEstimateModal> = observer((props) =>
if (validatedEstimatePoints.length === estimatePoints?.length) { if (validatedEstimatePoints.length === estimatePoints?.length) {
const payload: IEstimateFormData = { const payload: IEstimateFormData = {
estimate: { estimate: {
name: ESTIMATE_SYSTEMS[estimateSystem]?.name,
type: estimateSystem, type: estimateSystem,
last_used: true,
}, },
estimate_points: validatedEstimatePoints, estimate_points: validatedEstimatePoints,
}; };
@ -122,6 +124,8 @@ export const CreateEstimateModal: FC<TCreateEstimateModal> = observer((props) =>
)} )}
{estimatePoints && ( {estimatePoints && (
<EstimateCreateStageTwo <EstimateCreateStageTwo
workspaceSlug={workspaceSlug}
projectId={projectId}
estimateSystem={estimateSystem} estimateSystem={estimateSystem}
estimatePoints={estimatePoints} estimatePoints={estimatePoints}
handleEstimatePoints={handleUpdatePoints} handleEstimatePoints={handleUpdatePoints}

View File

@ -1,4 +1,5 @@
import { FC } from "react"; import { FC } from "react";
import { observer } from "mobx-react";
import { Plus } from "lucide-react"; import { Plus } from "lucide-react";
import { TEstimatePointsObject } from "@plane/types"; import { TEstimatePointsObject } from "@plane/types";
import { Button, Sortable } from "@plane/ui"; import { Button, Sortable } from "@plane/ui";
@ -8,13 +9,15 @@ import { EstimatePointItem } from "@/components/estimates";
import { EEstimateSystem, EEstimateUpdateStages, ESTIMATE_SYSTEMS, maxEstimatesCount } from "@/constants/estimates"; import { EEstimateSystem, EEstimateUpdateStages, ESTIMATE_SYSTEMS, maxEstimatesCount } from "@/constants/estimates";
type TEstimateCreateStageTwo = { type TEstimateCreateStageTwo = {
workspaceSlug: string;
projectId: string;
estimateSystem: EEstimateSystem; estimateSystem: EEstimateSystem;
estimatePoints: TEstimatePointsObject[]; estimatePoints: TEstimatePointsObject[];
handleEstimatePoints: (value: TEstimatePointsObject[]) => void; handleEstimatePoints: (value: TEstimatePointsObject[]) => void;
}; };
export const EstimateCreateStageTwo: FC<TEstimateCreateStageTwo> = (props) => { export const EstimateCreateStageTwo: FC<TEstimateCreateStageTwo> = observer((props) => {
const { estimateSystem, estimatePoints, handleEstimatePoints } = props; const { workspaceSlug, projectId, estimateSystem, estimatePoints, handleEstimatePoints } = props;
const currentEstimateSystem = ESTIMATE_SYSTEMS[estimateSystem] || undefined; const currentEstimateSystem = ESTIMATE_SYSTEMS[estimateSystem] || undefined;
@ -22,7 +25,7 @@ export const EstimateCreateStageTwo: FC<TEstimateCreateStageTwo> = (props) => {
const currentEstimationPoints = estimatePoints; const currentEstimationPoints = estimatePoints;
const newEstimationPoint: TEstimatePointsObject = { const newEstimationPoint: TEstimatePointsObject = {
key: currentEstimationPoints.length + 1, key: currentEstimationPoints.length + 1,
value: "0", value: "",
}; };
handleEstimatePoints([...currentEstimationPoints, newEstimationPoint]); handleEstimatePoints([...currentEstimationPoints, newEstimationPoint]);
}; };
@ -59,11 +62,15 @@ export const EstimateCreateStageTwo: FC<TEstimateCreateStageTwo> = (props) => {
data={estimatePoints} data={estimatePoints}
render={(value: TEstimatePointsObject, index: number) => ( render={(value: TEstimatePointsObject, index: number) => (
<EstimatePointItem <EstimatePointItem
workspaceSlug={workspaceSlug}
projectId={projectId}
estimateId={undefined} estimateId={undefined}
mode={EEstimateUpdateStages.CREATE} mode={EEstimateUpdateStages.CREATE}
item={value} item={value}
estimatePoints={estimatePoints}
editItem={(value: string) => editEstimationPoint(index, value)} editItem={(value: string) => editEstimationPoint(index, value)}
deleteItem={() => deleteEstimationPoint(index)} deleteItem={() => deleteEstimationPoint(index)}
handleEstimatePoints={handleEstimatePoints}
/> />
)} )}
onChange={(data: TEstimatePointsObject[]) => handleEstimatePoints(updatedSortedKeys(data))} onChange={(data: TEstimatePointsObject[]) => handleEstimatePoints(updatedSortedKeys(data))}
@ -77,4 +84,4 @@ export const EstimateCreateStageTwo: FC<TEstimateCreateStageTwo> = (props) => {
</div> </div>
</div> </div>
); );
}; });

View File

@ -17,5 +17,4 @@ export * from "./create";
export * from "./update"; export * from "./update";
// estimate points // estimate points
export * from "./points/estimate-point-item"; export * from "./points";
export * from "./points/inline-editable";

View File

@ -0,0 +1,95 @@
import { FC, useState } from "react";
import { observer } from "mobx-react";
import { Check, X } from "lucide-react";
import { Spinner } from "@plane/ui";
// constants
import { EEstimateSystem } from "@/constants/estimates";
// hooks
import { useEstimate } from "@/hooks/store";
type TEstimatePointCreate = {
workspaceSlug: string;
projectId: string;
estimateId: string;
estimatePointId: string | undefined;
};
export const EstimatePointCreate: FC<TEstimatePointCreate> = observer((props) => {
const { workspaceSlug, projectId, estimateId, estimatePointId } = props;
// hooks
const { asJson: estimate, estimatePointIds, creteEstimatePoint } = useEstimate(estimateId);
// states
const [loader, setLoader] = useState(false);
const [estimateValue, setEstimateValue] = useState("");
const handleCreate = async () => {
if (estimatePointId) {
if (!workspaceSlug || !projectId || !projectId || !estimatePointIds) return;
try {
const estimateType: EEstimateSystem | undefined = estimate?.type;
let isEstimateValid = false;
if (estimateType && [(EEstimateSystem.TIME, EEstimateSystem.POINTS)].includes(estimateType)) {
if (estimateValue && Number(estimateValue) && Number(estimateValue) >= 0) {
isEstimateValid = true;
}
} else if (estimateType && estimateType === EEstimateSystem.CATEGORIES) {
if (estimateValue && estimateValue.length > 0) {
isEstimateValid = true;
}
}
if (isEstimateValid) {
setLoader(true);
const payload = {
key: estimatePointIds?.length + 1,
value: estimateValue,
};
await creteEstimatePoint(workspaceSlug, projectId, payload);
setLoader(false);
handleClose();
} else {
console.log("please enter a valid estimate value");
}
} catch {
setLoader(false);
console.log("something went wrong. please try again later");
}
} else {
}
};
const handleClose = () => {
setEstimateValue("");
};
return (
<div className="relative flex items-center gap-2">
<div className="w-full border border-custom-border-200 rounded">
<input
type="text"
value={estimateValue}
onChange={(e) => setEstimateValue(e.target.value)}
className="border-none focus:ring-0 focus:border-0 focus:outline-none p-2.5 w-full bg-transparent"
/>
</div>
{loader ? (
<div className="w-6 h-6 flex-shrink-0 relative flex justify-center items-center rota">
<Spinner className="w-4 h-4" />
</div>
) : (
<div
className="rounded-sm w-6 h-6 flex-shrink-0 relative flex justify-center items-center hover:bg-custom-background-80 transition-colors cursor-pointer text-green-500"
onClick={handleCreate}
>
<Check size={14} />
</div>
)}
<div
className="rounded-sm w-6 h-6 flex-shrink-0 relative flex justify-center items-center hover:bg-custom-background-80 transition-colors cursor-pointer"
onClick={handleClose}
>
<X size={14} className="text-custom-text-200" />
</div>
</div>
);
});

View File

@ -0,0 +1,100 @@
import { FC, useEffect, useState } from "react";
import { observer } from "mobx-react";
import { Check, X } from "lucide-react";
import { Spinner } from "@plane/ui";
// constants
import { EEstimateSystem } from "@/constants/estimates";
// hooks
import { useEstimate, useEstimatePoint } from "@/hooks/store";
type TEstimatePointDelete = {
workspaceSlug: string;
projectId: string;
estimateId: string;
estimatePointId: string | undefined;
};
export const EstimatePointDelete: FC<TEstimatePointDelete> = observer((props) => {
const { workspaceSlug, projectId, estimateId, estimatePointId } = props;
// hooks
const { asJson: estimate, estimatePointIds } = useEstimate(estimateId);
const { asJson: estimatePoint, updateEstimatePoint } = useEstimatePoint(estimateId, estimatePointId);
// states
const [loader, setLoader] = useState(false);
const [estimateValue, setEstimateValue] = useState<string | undefined>(undefined);
useEffect(() => {
if (estimateValue === undefined) setEstimateValue(estimatePoint?.value || "");
}, [estimateValue, estimatePoint]);
const handleCreate = async () => {
if (estimatePointId) {
if (!workspaceSlug || !projectId || !projectId || !estimatePointIds) return;
try {
const estimateType: EEstimateSystem | undefined = estimate?.type;
let isEstimateValid = false;
if (estimateType && [(EEstimateSystem.TIME, EEstimateSystem.POINTS)].includes(estimateType)) {
if (estimateValue && Number(estimateValue) && Number(estimateValue) >= 0) {
isEstimateValid = true;
}
} else if (estimateType && estimateType === EEstimateSystem.CATEGORIES) {
if (estimateValue && estimateValue.length > 0) {
isEstimateValid = true;
}
}
if (isEstimateValid) {
setLoader(true);
const payload = {
key: estimatePointIds?.length + 1,
value: estimateValue,
};
await updateEstimatePoint(workspaceSlug, projectId, payload);
setLoader(false);
handleClose();
} else {
console.log("please enter a valid estimate value");
}
} catch {
setLoader(false);
console.log("something went wrong. please try again later");
}
} else {
}
};
const handleClose = () => {
setEstimateValue("");
};
return (
<div className="relative flex items-center gap-2">
<div className="w-full border border-custom-border-200 rounded">
<input
type="text"
value={estimateValue}
onChange={(e) => setEstimateValue(e.target.value)}
className="border-none focus:ring-0 focus:border-0 focus:outline-none p-2.5 w-full bg-transparent"
/>
</div>
{loader ? (
<div className="w-6 h-6 flex-shrink-0 relative flex justify-center items-center rota">
<Spinner className="w-4 h-4" />
</div>
) : (
<div
className="rounded-sm w-6 h-6 flex-shrink-0 relative flex justify-center items-center hover:bg-custom-background-80 transition-colors cursor-pointer text-green-500"
onClick={handleCreate}
>
<Check size={14} />
</div>
)}
<div
className="rounded-sm w-6 h-6 flex-shrink-0 relative flex justify-center items-center hover:bg-custom-background-80 transition-colors cursor-pointer"
onClick={handleClose}
>
<X size={14} className="text-custom-text-200" />
</div>
</div>
);
});

View File

@ -0,0 +1,29 @@
import { FC, useState } from "react";
import { observer } from "mobx-react";
import { Draggable } from "@plane/ui";
// constants
import { EEstimateUpdateStages } from "@/constants/estimates";
type TEstimatePointEditRoot = {
workspaceSlug: string;
projectId: string;
estimateId: string;
mode: EEstimateUpdateStages;
};
type TEstimatePointEditingState = "update" | "delete";
export const EstimatePointEditRoot: FC<TEstimatePointEditRoot> = observer((props) => {
// props
const { workspaceSlug, projectId, estimateId, mode } = props;
// hooks
// states
const [editingState, setEditingState] = useState<TEstimatePointEditingState | undefined>(undefined);
const [estimateEditLoader, setEstimateEditLoader] = useState(false);
const [deletedEstimateValue, setDeletedEstimateValue] = useState<string | undefined>(undefined);
const [isEstimateEditing, setIsEstimateEditing] = useState(false);
const [isEstimateDeleting, setIsEstimateDeleting] = useState(false);
return <Draggable data={item}></Draggable>;
});

View File

@ -8,22 +8,37 @@ import { Draggable, Spinner } from "@plane/ui";
import { EEstimateUpdateStages } from "@/constants/estimates"; import { EEstimateUpdateStages } from "@/constants/estimates";
// helpers // helpers
import { cn } from "@/helpers/common.helper"; import { cn } from "@/helpers/common.helper";
import { useEstimate } from "@/hooks/store"; import { useEstimate, useEstimatePoint } from "@/hooks/store";
type TEstimatePointItem = { type TEstimatePointItem = {
workspaceSlug: string;
projectId: string;
estimateId: string | undefined; estimateId: string | undefined;
mode: EEstimateUpdateStages; mode: EEstimateUpdateStages;
item: TEstimatePointsObject; item: TEstimatePointsObject;
estimatePoints: TEstimatePointsObject[];
editItem: (value: string) => void; editItem: (value: string) => void;
deleteItem: () => void; deleteItem: () => void;
handleEstimatePoints: (value: TEstimatePointsObject[]) => void;
}; };
export const EstimatePointItem: FC<TEstimatePointItem> = observer((props) => { export const EstimatePointItem: FC<TEstimatePointItem> = observer((props) => {
// props // props
const { estimateId, mode, item, editItem, deleteItem } = props; const {
workspaceSlug,
projectId,
estimateId,
mode,
item,
estimatePoints,
editItem,
deleteItem,
handleEstimatePoints,
} = props;
const { id, key, value } = item; const { id, key, value } = item;
// hooks // hooks
const { asJson: estimate, updateEstimate, deleteEstimate } = useEstimate(estimateId); const { asJson: estimate, creteEstimatePoint, deleteEstimatePoint } = useEstimate(estimateId);
const { updateEstimatePoint } = useEstimatePoint(estimateId, id);
// ref // ref
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
// states // states
@ -35,7 +50,7 @@ export const EstimatePointItem: FC<TEstimatePointItem> = observer((props) => {
const [isEstimateDeleting, setIsEstimateDeleting] = useState(false); const [isEstimateDeleting, setIsEstimateDeleting] = useState(false);
useEffect(() => { useEffect(() => {
if (inputValue === undefined || inputValue != value) setInputValue(value); if (value && inputValue === undefined) setInputValue(value);
}, [value, inputValue]); }, [value, inputValue]);
const handleCreateEdit = (value: string) => { const handleCreateEdit = (value: string) => {
@ -43,11 +58,26 @@ export const EstimatePointItem: FC<TEstimatePointItem> = observer((props) => {
editItem(value); editItem(value);
}; };
const handleNewEstimatePoint = async () => {
if (inputValue) {
try {
setEstimateEditLoader(true);
const estimatePoint = await creteEstimatePoint(workspaceSlug, projectId, { key: key, value: inputValue });
if (estimatePoint)
handleEstimatePoints([...estimatePoints, { id: estimatePoint.id, key: key, value: inputValue }]);
setIsEstimateEditing(false);
setEstimateEditLoader(false);
} catch (error) {
setEstimateEditLoader(false);
}
}
};
const handleEdit = async () => { const handleEdit = async () => {
if (id) { if (id) {
try { try {
setEstimateEditLoader(true); setEstimateEditLoader(true);
await updateEstimate({ estimate_points: [{ id: id, key: key, value: value }] }); await updateEstimatePoint(workspaceSlug, projectId, { key: key, value: inputValue });
setIsEstimateEditing(false); setIsEstimateEditing(false);
setEstimateEditLoader(false); setEstimateEditLoader(false);
} catch (error) { } catch (error) {
@ -62,7 +92,7 @@ export const EstimatePointItem: FC<TEstimatePointItem> = observer((props) => {
if (id) { if (id) {
try { try {
setEstimateEditLoader(true); setEstimateEditLoader(true);
await deleteEstimate(deletedEstimateValue); await deleteEstimatePoint(workspaceSlug, projectId, id, deletedEstimateValue);
setIsEstimateDeleting(false); setIsEstimateDeleting(false);
setEstimateEditLoader(false); setEstimateEditLoader(false);
} catch (error) { } catch (error) {
@ -78,24 +108,65 @@ export const EstimatePointItem: FC<TEstimatePointItem> = observer((props) => {
return ( return (
<Draggable data={item}> <Draggable data={item}>
{!id && ( {!id && (
<div className="border border-custom-border-200 rounded relative flex items-center px-2.5 gap-2"> <>
<div className="rounded-sm w-6 h-6 flex-shrink-0 relative flex justify-center items-center hover:bg-custom-background-80 transition-colors cursor-pointer"> {mode === EEstimateUpdateStages.CREATE && (
<GripVertical size={14} className="text-custom-text-200" /> <div className="border border-custom-border-200 rounded relative flex items-center px-2.5 gap-2">
</div> <div className="rounded-sm w-6 h-6 flex-shrink-0 relative flex justify-center items-center hover:bg-custom-background-80 transition-colors cursor-pointer">
<input <GripVertical size={14} className="text-custom-text-200" />
ref={inputRef} </div>
type="text" <input
value={inputValue} ref={inputRef}
onChange={(e) => handleCreateEdit(e.target.value)} type="text"
className="flex-grow border-none bg-transparent focus:ring-0 focus:border-0 focus:outline-none py-2.5 w-full" value={inputValue}
/> onChange={(e) => handleCreateEdit(e.target.value)}
<div className="flex-grow border-none bg-transparent focus:ring-0 focus:border-0 focus:outline-none py-2.5 w-full"
className="rounded-sm w-6 h-6 flex-shrink-0 relative flex justify-center items-center hover:bg-custom-background-80 transition-colors cursor-pointer" />
onClick={handleDelete} <div
> className="rounded-sm w-6 h-6 flex-shrink-0 relative flex justify-center items-center hover:bg-custom-background-80 transition-colors cursor-pointer"
<Trash2 size={14} className="text-custom-text-200" /> onClick={handleDelete}
</div> >
</div> <Trash2 size={14} className="text-custom-text-200" />
</div>
</div>
)}
{mode === EEstimateUpdateStages.EDIT && (
<div className="relative flex items-center gap-2">
<div className="w-full border border-custom-border-200 rounded">
<input
ref={inputRef}
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
className={cn(
"border-none focus:ring-0 focus:border-0 focus:outline-none p-2.5 w-full",
isEstimateDeleting ? `bg-custom-background-90` : `bg-transparent`
)}
disabled={isEstimateDeleting}
/>
</div>
{estimateEditLoader ? (
<div className="w-6 h-6 flex-shrink-0 relative flex justify-center items-center rota">
<Spinner className="w-4 h-4" />
</div>
) : (
<div
className="rounded-sm w-6 h-6 flex-shrink-0 relative flex justify-center items-center hover:bg-custom-background-80 transition-colors cursor-pointer text-green-500"
onClick={handleNewEstimatePoint}
>
<Check size={14} />
</div>
)}
<div
className="rounded-sm w-6 h-6 flex-shrink-0 relative flex justify-center items-center hover:bg-custom-background-80 transition-colors cursor-pointer"
onClick={handleDelete}
>
<X size={14} className="text-custom-text-200" />
</div>
</div>
)}
</>
)} )}
{id && ( {id && (

View File

@ -0,0 +1,8 @@
export * from "./estimate-point-item";
export * from "./inline-editable";
export * from "./edit-root";
export * from "./create";
export * from "./update";
export * from "./delete";

View File

@ -0,0 +1,100 @@
import { FC, useEffect, useState } from "react";
import { observer } from "mobx-react";
import { Check, X } from "lucide-react";
import { Spinner } from "@plane/ui";
// constants
import { EEstimateSystem } from "@/constants/estimates";
// hooks
import { useEstimate, useEstimatePoint } from "@/hooks/store";
type TEstimatePointUpdate = {
workspaceSlug: string;
projectId: string;
estimateId: string;
estimatePointId: string | undefined;
};
export const EstimatePointUpdate: FC<TEstimatePointUpdate> = observer((props) => {
const { workspaceSlug, projectId, estimateId, estimatePointId } = props;
// hooks
const { asJson: estimate, estimatePointIds } = useEstimate(estimateId);
const { asJson: estimatePoint, updateEstimatePoint } = useEstimatePoint(estimateId, estimatePointId);
// states
const [loader, setLoader] = useState(false);
const [estimateValue, setEstimateValue] = useState<string | undefined>(undefined);
useEffect(() => {
if (estimateValue === undefined) setEstimateValue(estimatePoint?.value || "");
}, [estimateValue, estimatePoint]);
const handleCreate = async () => {
if (estimatePointId) {
if (!workspaceSlug || !projectId || !projectId || !estimatePointIds) return;
try {
const estimateType: EEstimateSystem | undefined = estimate?.type;
let isEstimateValid = false;
if (estimateType && [(EEstimateSystem.TIME, EEstimateSystem.POINTS)].includes(estimateType)) {
if (estimateValue && Number(estimateValue) && Number(estimateValue) >= 0) {
isEstimateValid = true;
}
} else if (estimateType && estimateType === EEstimateSystem.CATEGORIES) {
if (estimateValue && estimateValue.length > 0) {
isEstimateValid = true;
}
}
if (isEstimateValid) {
setLoader(true);
const payload = {
key: estimatePointIds?.length + 1,
value: estimateValue,
};
await updateEstimatePoint(workspaceSlug, projectId, payload);
setLoader(false);
handleClose();
} else {
console.log("please enter a valid estimate value");
}
} catch {
setLoader(false);
console.log("something went wrong. please try again later");
}
} else {
}
};
const handleClose = () => {
setEstimateValue("");
};
return (
<div className="relative flex items-center gap-2">
<div className="w-full border border-custom-border-200 rounded">
<input
type="text"
value={estimateValue}
onChange={(e) => setEstimateValue(e.target.value)}
className="border-none focus:ring-0 focus:border-0 focus:outline-none p-2.5 w-full bg-transparent"
/>
</div>
{loader ? (
<div className="w-6 h-6 flex-shrink-0 relative flex justify-center items-center rota">
<Spinner className="w-4 h-4" />
</div>
) : (
<div
className="rounded-sm w-6 h-6 flex-shrink-0 relative flex justify-center items-center hover:bg-custom-background-80 transition-colors cursor-pointer text-green-500"
onClick={handleCreate}
>
<Check size={14} />
</div>
)}
<div
className="rounded-sm w-6 h-6 flex-shrink-0 relative flex justify-center items-center hover:bg-custom-background-80 transition-colors cursor-pointer"
onClick={handleClose}
>
<X size={14} className="text-custom-text-200" />
</div>
</div>
);
});

View File

@ -1,13 +1,12 @@
import { FC, useEffect, useMemo, useState } from "react"; import { FC, useEffect, useMemo, useState } from "react";
import orderBy from "lodash/orderBy";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { ChevronLeft } from "lucide-react"; import { ChevronLeft } from "lucide-react";
import { IEstimateFormData, TEstimatePointsObject, TEstimateUpdateStageKeys, TEstimateSystemKeys } from "@plane/types"; import { TEstimatePointsObject, TEstimateUpdateStageKeys } from "@plane/types";
import { Button, TOAST_TYPE, setToast } from "@plane/ui"; import { Button } from "@plane/ui";
// components // components
import { EModalPosition, EModalWidth, ModalCore } from "@/components/core"; import { EModalPosition, EModalWidth, ModalCore } from "@/components/core";
import { EstimateUpdateStageOne, EstimateUpdateStageTwo } from "@/components/estimates"; import { EstimateUpdateStageOne, EstimateUpdateStageTwo } from "@/components/estimates";
// constants
import { EEstimateSystem } from "@/constants/estimates";
// hooks // hooks
import { import {
useEstimate, useEstimate,
@ -26,18 +25,19 @@ export const UpdateEstimateModal: FC<TUpdateEstimateModal> = observer((props) =>
// props // props
const { workspaceSlug, projectId, estimateId, isOpen, handleClose } = props; const { workspaceSlug, projectId, estimateId, isOpen, handleClose } = props;
// hooks // hooks
const { asJson: currentEstimate, updateEstimate } = useEstimate(estimateId); const { asJson: currentEstimate } = useEstimate(estimateId);
// states // states
const [estimateEditType, setEstimateEditType] = useState<TEstimateUpdateStageKeys | undefined>(undefined); const [estimateEditType, setEstimateEditType] = useState<TEstimateUpdateStageKeys | undefined>(undefined);
const [estimatePoints, setEstimatePoints] = useState<TEstimatePointsObject[] | undefined>(undefined); const [estimatePoints, setEstimatePoints] = useState<TEstimatePointsObject[] | undefined>(undefined);
const handleEstimateEditType = (type: TEstimateUpdateStageKeys) => { const handleEstimateEditType = (type: TEstimateUpdateStageKeys) => {
if (currentEstimate?.points && currentEstimate?.points.length > 0) { if (currentEstimate?.points && currentEstimate?.points.length > 0) {
const estimateValidatePoints: TEstimatePointsObject[] = []; let estimateValidatePoints: TEstimatePointsObject[] = [];
currentEstimate?.points.map( currentEstimate?.points.map(
(point) => (point) =>
point.key && point.value && estimateValidatePoints.push({ id: point.id, key: point.key, value: point.value }) point.key && point.value && estimateValidatePoints.push({ id: point.id, key: point.key, value: point.value })
); );
estimateValidatePoints = orderBy(estimateValidatePoints, ["key"], ["asc"]);
if (estimateValidatePoints.length > 0) { if (estimateValidatePoints.length > 0) {
setEstimateEditType(type); setEstimateEditType(type);
setEstimatePoints(estimateValidatePoints); setEstimatePoints(estimateValidatePoints);
@ -56,57 +56,6 @@ export const UpdateEstimateModal: FC<TUpdateEstimateModal> = observer((props) =>
// derived values // derived values
const renderEstimateStepsCount = useMemo(() => (estimatePoints ? "2" : "1"), [estimatePoints]); const renderEstimateStepsCount = useMemo(() => (estimatePoints ? "2" : "1"), [estimatePoints]);
const isNewEstimatePointsToCreate =
(estimatePoints || []).filter((point) => point.id === undefined).length > 0 ? true : false;
const handleUpdateEstimate = async () => {
try {
if (!workspaceSlug || !projectId || !estimateId || currentEstimate?.type === undefined) return;
const currentEstimatePoints = (estimatePoints || []).filter((point) => point.id === undefined);
const currentEstimationType: TEstimateSystemKeys = currentEstimate?.type;
const validatedEstimatePoints: TEstimatePointsObject[] = [];
if ([EEstimateSystem.POINTS, EEstimateSystem.TIME].includes(currentEstimationType)) {
currentEstimatePoints?.map((estimatePoint) => {
if (
estimatePoint.value &&
((estimatePoint.value != "0" && Number(estimatePoint.value)) || estimatePoint.value === "0")
)
validatedEstimatePoints.push(estimatePoint);
});
} else {
currentEstimatePoints?.map((estimatePoint) => {
if (estimatePoint.value) validatedEstimatePoints.push(estimatePoint);
});
}
if (validatedEstimatePoints.length === currentEstimatePoints?.length) {
const payload: IEstimateFormData = {
estimate_points: validatedEstimatePoints,
};
await updateEstimate(payload);
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Estimate system created",
message: "Created and Enabled successfully",
});
handleClose();
} else {
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
message: "something went wrong",
});
}
} catch (error) {
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
message: "something went wrong",
});
}
};
return ( return (
<ModalCore isOpen={isOpen} handleClose={handleClose} position={EModalPosition.TOP} width={EModalWidth.XXL}> <ModalCore isOpen={isOpen} handleClose={handleClose} position={EModalPosition.TOP} width={EModalWidth.XXL}>
@ -135,6 +84,8 @@ export const UpdateEstimateModal: FC<TUpdateEstimateModal> = observer((props) =>
{!estimateEditType && <EstimateUpdateStageOne handleEstimateEditType={handleEstimateEditType} />} {!estimateEditType && <EstimateUpdateStageOne handleEstimateEditType={handleEstimateEditType} />}
{estimateEditType && estimatePoints && ( {estimateEditType && estimatePoints && (
<EstimateUpdateStageTwo <EstimateUpdateStageTwo
workspaceSlug={workspaceSlug}
projectId={projectId}
estimate={currentEstimate} estimate={currentEstimate}
estimateEditType={estimateEditType} estimateEditType={estimateEditType}
estimatePoints={estimatePoints} estimatePoints={estimatePoints}
@ -147,11 +98,6 @@ export const UpdateEstimateModal: FC<TUpdateEstimateModal> = observer((props) =>
<Button variant="neutral-primary" size="sm" onClick={handleClose}> <Button variant="neutral-primary" size="sm" onClick={handleClose}>
Cancel Cancel
</Button> </Button>
{isNewEstimatePointsToCreate && (
<Button variant="primary" size="sm" onClick={handleUpdateEstimate}>
Update Estimate
</Button>
)}
</div> </div>
</div> </div>
</ModalCore> </ModalCore>

View File

@ -1,4 +1,5 @@
import { FC } from "react"; import { FC } from "react";
import cloneDeep from "lodash/cloneDeep";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { Plus } from "lucide-react"; import { Plus } from "lucide-react";
import { IEstimate, TEstimatePointsObject, TEstimateUpdateStageKeys } from "@plane/types"; import { IEstimate, TEstimatePointsObject, TEstimateUpdateStageKeys } from "@plane/types";
@ -9,6 +10,8 @@ import { EstimatePointItem } from "@/components/estimates";
import { EEstimateUpdateStages, maxEstimatesCount } from "@/constants/estimates"; import { EEstimateUpdateStages, maxEstimatesCount } from "@/constants/estimates";
type TEstimateUpdateStageTwo = { type TEstimateUpdateStageTwo = {
workspaceSlug: string;
projectId: string;
estimate: IEstimate; estimate: IEstimate;
estimateEditType: TEstimateUpdateStageKeys | undefined; estimateEditType: TEstimateUpdateStageKeys | undefined;
estimatePoints: TEstimatePointsObject[]; estimatePoints: TEstimatePointsObject[];
@ -16,15 +19,15 @@ type TEstimateUpdateStageTwo = {
}; };
export const EstimateUpdateStageTwo: FC<TEstimateUpdateStageTwo> = observer((props) => { export const EstimateUpdateStageTwo: FC<TEstimateUpdateStageTwo> = observer((props) => {
const { estimate, estimateEditType, estimatePoints, handleEstimatePoints } = props; const { workspaceSlug, projectId, estimate, estimateEditType, estimatePoints, handleEstimatePoints } = props;
const currentEstimateSystem = estimate || undefined; const currentEstimateSystem = estimate || undefined;
const addNewEstimationPoint = () => { const addNewEstimationPoint = () => {
const currentEstimationPoints = estimatePoints; const currentEstimationPoints = cloneDeep(estimatePoints);
const newEstimationPoint: TEstimatePointsObject = { const newEstimationPoint: TEstimatePointsObject = {
key: currentEstimationPoints.length + 1, key: currentEstimationPoints.length + 1,
value: "0", value: "",
}; };
handleEstimatePoints([...currentEstimationPoints, newEstimationPoint]); handleEstimatePoints([...currentEstimationPoints, newEstimationPoint]);
}; };
@ -64,11 +67,15 @@ export const EstimateUpdateStageTwo: FC<TEstimateUpdateStageTwo> = observer((pro
data={estimatePoints} data={estimatePoints}
render={(value: TEstimatePointsObject, index: number) => ( render={(value: TEstimatePointsObject, index: number) => (
<EstimatePointItem <EstimatePointItem
workspaceSlug={workspaceSlug}
projectId={projectId}
estimateId={estimate?.id || undefined} estimateId={estimate?.id || undefined}
mode={estimateEditType} mode={estimateEditType}
item={value} item={value}
estimatePoints={estimatePoints}
editItem={(value: string) => editEstimationPoint(index, value)} editItem={(value: string) => editEstimationPoint(index, value)}
deleteItem={() => deleteEstimationPoint(index)} deleteItem={() => deleteEstimationPoint(index)}
handleEstimatePoints={handleEstimatePoints}
/> />
)} )}
onChange={(data: TEstimatePointsObject[]) => handleEstimatePoints(updatedSortedKeys(data))} onChange={(data: TEstimatePointsObject[]) => handleEstimatePoints(updatedSortedKeys(data))}

View File

@ -1,5 +1,5 @@
// types // types
import { IEstimate, IEstimateFormData } from "@plane/types"; import { IEstimate, IEstimateFormData, IEstimatePoint } from "@plane/types";
// helpers // helpers
import { API_BASE_URL } from "@/helpers/common.helper"; import { API_BASE_URL } from "@/helpers/common.helper";
// services // services
@ -10,7 +10,6 @@ export class EstimateService extends APIService {
super(API_BASE_URL); super(API_BASE_URL);
} }
// fetching the estimates in workspace level
async fetchWorkspaceEstimates(workspaceSlug: string): Promise<IEstimate[] | undefined> { async fetchWorkspaceEstimates(workspaceSlug: string): Promise<IEstimate[] | undefined> {
try { try {
const { data } = await this.get(`/api/workspaces/${workspaceSlug}/estimates/`); const { data } = await this.get(`/api/workspaces/${workspaceSlug}/estimates/`);
@ -61,7 +60,7 @@ export class EstimateService extends APIService {
workspaceSlug: string, workspaceSlug: string,
projectId: string, projectId: string,
estimateId: string, estimateId: string,
payload: IEstimateFormData payload: Partial<IEstimate>
): Promise<IEstimate | undefined> { ): Promise<IEstimate | undefined> {
try { try {
const { data } = await this.patch( const { data } = await this.patch(
@ -74,16 +73,51 @@ export class EstimateService extends APIService {
} }
} }
async createEstimatePoint(
workspaceSlug: string,
projectId: string,
estimateId: string,
payload: Partial<IEstimatePoint>
): Promise<IEstimatePoint | undefined> {
try {
const { data } = await this.post(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/estimates/${estimateId}/estimate-points/`,
payload
);
return data || undefined;
} catch (error) {
throw error;
}
}
async updateEstimatePoint(
workspaceSlug: string,
projectId: string,
estimateId: string,
estimatePointId: string,
payload: Partial<IEstimatePoint>
): Promise<IEstimatePoint | undefined> {
try {
const { data } = await this.post(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/estimates/${estimateId}/estimate-points/${estimatePointId}/`,
payload
);
return data || undefined;
} catch (error) {
throw error;
}
}
async removeEstimatePoint( async removeEstimatePoint(
workspaceSlug: string, workspaceSlug: string,
projectId: string, projectId: string,
estimateId: string, estimateId: string,
estimatePointId: string, estimatePointId: string,
payload: { new_estimate_id: string | undefined } params?: { new_estimate_id: string | undefined }
): Promise<any> { ): Promise<void> {
return this.patch( return this.delete(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/estimates/${estimateId}/estimate-point/${estimatePointId}/`, `/api/workspaces/${workspaceSlug}/projects/${projectId}/estimates/${estimateId}/estimate-points/${estimatePointId}/`,
payload params
) )
.then((response) => response?.data) .then((response) => response?.data)
.catch((error) => { .catch((error) => {

View File

@ -1,5 +1,6 @@
import { action, computed, makeObservable, observable } from "mobx"; import set from "lodash/set";
import { IEstimateFormData, IEstimate, IEstimatePoint as IEstimatePointType } from "@plane/types"; import { action, computed, makeObservable, observable, runInAction } from "mobx";
import { IEstimate, IEstimatePoint as IEstimatePointType } from "@plane/types";
// services // services
import { EstimateService } from "@/services/project/estimate.service"; import { EstimateService } from "@/services/project/estimate.service";
// store // store
@ -16,7 +17,11 @@ export interface IEstimatePoint extends IEstimatePointType {
// computed // computed
asJson: IEstimatePointType; asJson: IEstimatePointType;
// actions // actions
updateEstimatePoint: (payload: IEstimateFormData) => Promise<void>; updateEstimatePoint: (
workspaceSlug: string,
projectId: string,
payload: Partial<IEstimatePointType>
) => Promise<IEstimatePointType | undefined>;
} }
export class EstimatePoint implements IEstimatePoint { export class EstimatePoint implements IEstimatePoint {
@ -73,7 +78,6 @@ export class EstimatePoint implements IEstimatePoint {
this.updated_at = this.data.updated_at; this.updated_at = this.data.updated_at;
this.created_by = this.data.created_by; this.created_by = this.data.created_by;
this.updated_by = this.data.updated_by; this.updated_by = this.data.updated_by;
// service // service
this.service = new EstimateService(); this.service = new EstimateService();
} }
@ -96,10 +100,36 @@ export class EstimatePoint implements IEstimatePoint {
} }
// actions // actions
updateEstimatePoint = async (payload: IEstimateFormData) => { /**
* @description updating an estimate point
* @param { Partial<IEstimatePointType> } payload
* @returns { IEstimatePointType | undefined }
*/
updateEstimatePoint = async (
workspaceSlug: string,
projectId: string,
payload: Partial<IEstimatePointType>
): Promise<IEstimatePointType | undefined> => {
try { try {
const { workspaceSlug, projectId } = this.store.router; if (!this.projectEstimate?.id || !this.id || !payload) return undefined;
if (!workspaceSlug || !projectId || !this.projectEstimate?.id || !this.id || !payload) return undefined;
const estimatePoint = await this.service.updateEstimatePoint(
workspaceSlug,
projectId,
this.projectEstimate?.id,
this.id,
payload
);
if (estimatePoint) {
runInAction(() => {
Object.keys(payload).map((key) => {
const estimatePointKey = key as keyof IEstimatePointType;
set(this, estimatePointKey, estimatePoint[estimatePointKey]);
});
});
}
return estimatePoint;
} catch (error) { } catch (error) {
throw error; throw error;
} }

View File

@ -1,16 +1,8 @@
import set from "lodash/set"; import set from "lodash/set";
import unset from "lodash/unset"; import unset from "lodash/unset";
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";
import { import { IEstimate as IEstimateType, IEstimatePoint as IEstimatePointType, TEstimateSystemKeys } from "@plane/types";
IEstimate as IEstimateType,
IEstimatePoint as IEstimatePointType,
IProject,
IWorkspace,
TEstimateSystemKeys,
IEstimateFormData,
} from "@plane/types";
// services // services
import { EstimateService } from "@/services/project/estimate.service"; import { EstimateService } from "@/services/project/estimate.service";
// store // store
@ -31,8 +23,22 @@ export interface IEstimate extends IEstimateType {
estimatePointIds: string[] | undefined; estimatePointIds: string[] | undefined;
estimatePointById: (estimateId: string) => IEstimatePointType | undefined; estimatePointById: (estimateId: string) => IEstimatePointType | undefined;
// actions // actions
updateEstimate: (payload: IEstimateFormData) => Promise<void>; updateEstimate: (
deleteEstimate: (estimatePointId: string | undefined) => Promise<void>; workspaceSlug: string,
projectId: string,
payload: Partial<IEstimateType>
) => Promise<IEstimateType | undefined>;
creteEstimatePoint: (
workspaceSlug: string,
projectId: string,
payload: Partial<IEstimatePointType>
) => Promise<IEstimatePointType | undefined>;
deleteEstimatePoint: (
workspaceSlug: string,
projectId: string,
estimatePointId: string,
newEstimatePointId: string | undefined
) => Promise<void>;
} }
export class Estimate implements IEstimate { export class Estimate implements IEstimate {
@ -43,9 +49,8 @@ export class Estimate implements IEstimate {
type: TEstimateSystemKeys | undefined = undefined; type: TEstimateSystemKeys | undefined = undefined;
points: IEstimatePointType[] | undefined = undefined; points: IEstimatePointType[] | undefined = undefined;
workspace: string | undefined = undefined; workspace: string | undefined = undefined;
workspace_detail: IWorkspace | undefined = undefined;
project: string | undefined = undefined; project: string | undefined = undefined;
project_detail: IProject | undefined = undefined; last_used: boolean | undefined = undefined;
created_at: Date | undefined = undefined; created_at: Date | undefined = undefined;
updated_at: Date | undefined = undefined; updated_at: Date | undefined = undefined;
created_by: string | undefined = undefined; created_by: string | undefined = undefined;
@ -68,9 +73,8 @@ export class Estimate implements IEstimate {
type: observable.ref, type: observable.ref,
points: observable, points: observable,
workspace: observable.ref, workspace: observable.ref,
workspace_detail: observable,
project: observable.ref, project: observable.ref,
project_detail: observable, last_used: observable.ref,
created_at: observable.ref, created_at: observable.ref,
updated_at: observable.ref, updated_at: observable.ref,
created_by: observable.ref, created_by: observable.ref,
@ -83,7 +87,8 @@ export class Estimate implements IEstimate {
estimatePointIds: computed, estimatePointIds: computed,
// actions // actions
updateEstimate: action, updateEstimate: action,
deleteEstimate: action, creteEstimatePoint: action,
deleteEstimatePoint: action,
}); });
this.id = this.data.id; this.id = this.data.id;
this.name = this.data.name; this.name = this.data.name;
@ -91,14 +96,12 @@ export class Estimate implements IEstimate {
this.type = this.data.type; this.type = this.data.type;
this.points = this.data.points; this.points = this.data.points;
this.workspace = this.data.workspace; this.workspace = this.data.workspace;
this.workspace_detail = this.data.workspace_detail;
this.project = this.data.project; this.project = this.data.project;
this.project_detail = this.data.project_detail; this.last_used = this.data.last_used;
this.created_at = this.data.created_at; this.created_at = this.data.created_at;
this.updated_at = this.data.updated_at; this.updated_at = this.data.updated_at;
this.created_by = this.data.created_by; this.created_by = this.data.created_by;
this.updated_by = this.data.updated_by; this.updated_by = this.data.updated_by;
this.data.points?.forEach((estimationPoint) => { this.data.points?.forEach((estimationPoint) => {
if (estimationPoint.id) if (estimationPoint.id)
set(this.estimatePoints, [estimationPoint.id], new EstimatePoint(this.store, this.data, estimationPoint)); set(this.estimatePoints, [estimationPoint.id], new EstimatePoint(this.store, this.data, estimationPoint));
@ -116,9 +119,8 @@ export class Estimate implements IEstimate {
type: this.type, type: this.type,
points: this.points, points: this.points,
workspace: this.workspace, workspace: this.workspace,
workspace_detail: this.workspace_detail,
project: this.project, project: this.project,
project_detail: this.project_detail, last_used: this.last_used,
created_at: this.created_at, created_at: this.created_at,
updated_at: this.updated_at, updated_at: this.updated_at,
created_by: this.created_by, created_by: this.created_by,
@ -143,35 +145,95 @@ export class Estimate implements IEstimate {
}); });
// actions // actions
updateEstimate = async (payload: IEstimateFormData) => { /**
* @description update an estimate
* @param { string } workspaceSlug
* @param { string } projectId
* @param { Partial<IEstimateType> } payload
* @returns { IEstimateType | undefined }
*/
updateEstimate = async (
workspaceSlug: string,
projectId: string,
payload: Partial<IEstimateType>
): Promise<IEstimateType | undefined> => {
try { try {
const { workspaceSlug, projectId } = this.store.router; if (!this.id || !payload) return;
if (!workspaceSlug || !projectId || !this.id || !payload) return;
await this.service.updateEstimate(workspaceSlug, projectId, this.id, payload); const estimate = await this.service.updateEstimate(workspaceSlug, projectId, this.id, payload);
if (estimate) {
runInAction(() => {
Object.keys(payload).map((key) => {
const estimateKey = key as keyof IEstimateType;
set(this, estimateKey, estimate[estimateKey]);
});
});
}
// runInAction(() => { return estimate;
// this.points = payload.estimate_points;
// this.data.points = payload.estimate_points;
// });
} catch (error) { } catch (error) {
throw error; throw error;
} }
}; };
deleteEstimate = async (estimatePointId: string | undefined) => { /**
* @description create an estimate point
* @param { string } workspaceSlug
* @param { string } projectId
* @param { Partial<IEstimatePointType> } payload
* @returns { IEstimatePointType | undefined }
*/
creteEstimatePoint = async (
workspaceSlug: string,
projectId: string,
payload: Partial<IEstimatePointType>
): Promise<IEstimatePointType | undefined> => {
try { try {
const { workspaceSlug, projectId } = this.store.router; if (!this.id || !payload) return;
if (!workspaceSlug || !projectId || !estimatePointId) return;
// make delete estimation request const estimatePoint = await this.service.createEstimatePoint(workspaceSlug, projectId, this.id, payload);
if (estimatePoint) {
runInAction(() => {
if (estimatePoint.id) {
set(this.estimatePoints, [estimatePoint.id], new EstimatePoint(this.store, this.data, estimatePoint));
}
});
}
} catch (error) {
throw error;
}
};
/**
* @description delete an estimate point
* @param { string } workspaceSlug
* @param { string } projectId
* @param { string } estimatePointId
* @param { string | undefined } newEstimatePointId
* @returns { void }
*/
deleteEstimatePoint = async (
workspaceSlug: string,
projectId: string,
estimatePointId: string,
newEstimatePointId: string | undefined
) => {
try {
if (!this.id) return;
const deleteEstimatePoint = await this.service.removeEstimatePoint(
workspaceSlug,
projectId,
this.id,
estimatePointId,
newEstimatePointId ? { new_estimate_id: newEstimatePointId } : undefined
);
runInAction(() => { runInAction(() => {
update(this, "points", (estimationPoints = []) =>
estimationPoints.filter((point: IEstimatePointType) => point.id !== estimatePointId)
);
unset(this.estimatePoints, [estimatePointId]); unset(this.estimatePoints, [estimatePointId]);
}); });
return deleteEstimatePoint;
} catch (error) { } catch (error) {
throw error; throw error;
} }

View File

@ -1,5 +1,5 @@
import orderBy from "lodash/orderBy";
import set from "lodash/set"; import set from "lodash/set";
import sortBy from "lodash/sortBy";
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";
@ -59,7 +59,6 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
// computed // computed
currentActiveEstimateId: computed, currentActiveEstimateId: computed,
archivedEstimateIds: computed, archivedEstimateIds: computed,
projectEstimateIds: computed,
// actions // actions
getWorkspaceEstimates: action, getWorkspaceEstimates: action,
getProjectEstimates: action, getProjectEstimates: action,
@ -79,9 +78,10 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
get currentActiveEstimateId(): string | undefined { get currentActiveEstimateId(): string | undefined {
const { projectId } = this.store.router; const { projectId } = this.store.router;
if (!projectId) return undefined; if (!projectId) return undefined;
const projectDetails = this.store.projectRoot.project.getProjectById(projectId); const currentActiveEstimateId = Object.values(this.estimates || {}).find(
if (!projectDetails) return undefined; (p) => p.project === projectId && p.last_used
return projectDetails.estimate ?? undefined; );
return currentActiveEstimateId?.id ?? undefined;
} }
/** /**
@ -91,25 +91,15 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
get archivedEstimateIds(): string[] | undefined { get archivedEstimateIds(): string[] | undefined {
const { projectId } = this.store.router; const { projectId } = this.store.router;
if (!projectId) return undefined; if (!projectId) return undefined;
const archivedEstimateIds = Object.values(this.estimates || {}) const archivedEstimates = orderBy(
.filter((p) => p.project === projectId && p.id !== this.currentActiveEstimateId) Object.values(this.estimates || {}).filter((p) => p.project === projectId && !p.last_used),
.map((p) => p.id) as string[]; ["created_at"],
"desc"
);
const archivedEstimateIds = archivedEstimates.map((p) => p.id) as string[];
return archivedEstimateIds ?? undefined; return archivedEstimateIds ?? undefined;
} }
/**
* @description get all estimate ids for a project
* @returns { string[] | undefined }
*/
get projectEstimateIds(): string[] | undefined {
const { projectId } = this.store.router;
if (!projectId) return undefined;
const projectEstimatesIds = Object.values(this.estimates || {})
.filter((p) => p.project === projectId)
.map((p) => p.id) as string[];
return projectEstimatesIds ?? undefined;
}
/** /**
* @description get estimates are enabled in the project or not * @description get estimates are enabled in the project or not
* @returns { boolean } * @returns { boolean }
@ -145,6 +135,7 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
// actions // actions
/** /**
* @description fetch all estimates for a workspace * @description fetch all estimates for a workspace
* @param { string } workspaceSlug
* @returns { IEstimateType[] | undefined } * @returns { IEstimateType[] | undefined }
*/ */
getWorkspaceEstimates = async ( getWorkspaceEstimates = async (
@ -153,7 +144,7 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
): Promise<IEstimateType[] | undefined> => { ): Promise<IEstimateType[] | undefined> => {
try { try {
this.error = undefined; this.error = undefined;
if (!this.projectEstimateIds) this.loader = loader ? loader : "init-loader"; if (Object.keys(this.estimates || {}).length <= 0) this.loader = loader ? loader : "init-loader";
const estimates = await this.service.fetchWorkspaceEstimates(workspaceSlug); const estimates = await this.service.fetchWorkspaceEstimates(workspaceSlug);
if (estimates && estimates.length > 0) { if (estimates && estimates.length > 0) {
@ -176,6 +167,8 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
/** /**
* @description fetch all estimates for a project * @description fetch all estimates for a project
* @param { string } workspaceSlug
* @param { string } projectId
* @returns { IEstimateType[] | undefined } * @returns { IEstimateType[] | undefined }
*/ */
getProjectEstimates = async ( getProjectEstimates = async (
@ -185,7 +178,7 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
): Promise<IEstimateType[] | undefined> => { ): Promise<IEstimateType[] | undefined> => {
try { try {
this.error = undefined; this.error = undefined;
if (!this.projectEstimateIds) this.loader = loader ? loader : "init-loader"; if (!this.estimateIdsByProjectId(projectId)) this.loader = loader ? loader : "init-loader";
const estimates = await this.service.fetchProjectEstimates(workspaceSlug, projectId); const estimates = await this.service.fetchProjectEstimates(workspaceSlug, projectId);
if (estimates && estimates.length > 0) { if (estimates && estimates.length > 0) {
@ -208,6 +201,8 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
/** /**
* @description update an estimate for a project * @description update an estimate for a project
* @param { string } workspaceSlug
* @param { string } projectId
* @param { string } estimateId * @param { string } estimateId
* @returns IEstimateType | undefined * @returns IEstimateType | undefined
*/ */
@ -241,7 +236,9 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
/** /**
* @description create an estimate for a project * @description create an estimate for a project
* @param { Partial<IEstimateType> } payload * @param { string } workspaceSlug
* @param { string } projectId
* @param { Partial<IEstimateFormData> } payload
* @returns * @returns
*/ */
createEstimate = async ( createEstimate = async (
@ -253,21 +250,15 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
this.error = undefined; this.error = undefined;
const estimate = await this.service.createEstimate(workspaceSlug, projectId, payload); const estimate = await this.service.createEstimate(workspaceSlug, projectId, payload);
// FIXME: i am getting different response from the server and once backend changes remove the get request and uncomment the commented code console.log("estimate", estimate);
let estimates = await this.getProjectEstimates(workspaceSlug, projectId, "mutation-loader"); if (estimate) {
estimates = sortBy(estimates, "created_at");
if (estimates && estimates.length > 0)
await this.store.projectRoot.project.updateProject(workspaceSlug, projectId, { await this.store.projectRoot.project.updateProject(workspaceSlug, projectId, {
estimate: estimates[estimates.length - 1].id, estimate: estimate.id,
}); });
// if (estimate) { runInAction(() => {
// await this.store.projectRoot.project.updateProject(workspaceSlug, projectId, { if (estimate.id) set(this.estimates, [estimate.id], new Estimate(this.store, estimate));
// estimate: estimate.id, });
// }); }
// runInAction(() => {
// if (estimate.id) set(this.estimates, [estimate.id], new Estimate(this.store, estimate));
// });
// }
return estimate; return estimate;
} catch (error) { } catch (error) {