chore: handled inline errors in the estimate switch

This commit is contained in:
guru_sainath 2024-05-30 11:27:28 +05:30
parent c2e07c6b7c
commit 8822c8b184
3 changed files with 120 additions and 38 deletions

View File

@ -1,7 +1,10 @@
import { FC } from "react"; import { FC } from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { MoveRight } from "lucide-react"; import { Info, MoveRight } from "lucide-react";
import { TEstimatePointsObject } from "@plane/types"; import { TEstimatePointsObject } from "@plane/types";
import { Tooltip } from "@plane/ui";
// helpers
import { cn } from "@/helpers/common.helper";
// hooks // hooks
import { useEstimatePoint } from "@/hooks/store"; import { useEstimatePoint } from "@/hooks/store";
@ -10,10 +13,19 @@ type TEstimatePointItemSwitchPreview = {
estimatePointId: string | undefined; estimatePointId: string | undefined;
estimatePoint: TEstimatePointsObject; estimatePoint: TEstimatePointsObject;
handleEstimatePoint: (value: string) => void; handleEstimatePoint: (value: string) => void;
errorType?: string;
isError?: boolean;
}; };
export const EstimatePointItemSwitchPreview: FC<TEstimatePointItemSwitchPreview> = observer((props) => { export const EstimatePointItemSwitchPreview: FC<TEstimatePointItemSwitchPreview> = observer((props) => {
const { estimateId, estimatePointId, estimatePoint: currentEstimatePoint, handleEstimatePoint } = props; const {
estimateId,
estimatePointId,
estimatePoint: currentEstimatePoint,
handleEstimatePoint,
errorType = "",
isError = false,
} = props;
// hooks // hooks
const { asJson: estimatePoint } = useEstimatePoint(estimateId, estimatePointId); const { asJson: estimatePoint } = useEstimatePoint(estimateId, estimatePointId);
@ -26,14 +38,34 @@ export const EstimatePointItemSwitchPreview: FC<TEstimatePointItemSwitchPreview>
<div className="flex-shrink-0 w-4 h-4 relative flex justify-center items-center"> <div className="flex-shrink-0 w-4 h-4 relative flex justify-center items-center">
<MoveRight size={12} /> <MoveRight size={12} />
</div> </div>
<div className="relative w-full border rounded flex items-center border-custom-border-200"> <div
className={cn(
"relative w-full border rounded flex items-center",
isError ? `border-red-500` : `border-custom-border-200`
)}
>
<input <input
type="text" type="text"
value={currentEstimatePoint?.value} value={currentEstimatePoint?.value}
onChange={(e) => handleEstimatePoint(e.target.value)} onChange={(e) => handleEstimatePoint(e.target.value)}
className="border-none focus:ring-0 focus:border-0 focus:outline-none p-2.5 w-full bg-transparent" className="border-none focus:ring-0 focus:border-0 focus:outline-none p-2.5 w-full bg-transparent"
autoFocus autoFocus
placeholder="Enter estimate point value"
/> />
{isError && (
<>
<Tooltip
tooltipContent={
errorType === "empty-fields" ? "please fill this estimate point." : `Repeating estimate point`
}
position="bottom"
>
<div className="flex-shrink-0 w-3.5 h-3.5 overflow-hidden mr-3 relative flex justify-center items-center text-red-500">
<Info size={14} />
</div>
</Tooltip>
</>
)}
</div> </div>
</div> </div>
); );

View File

@ -6,6 +6,8 @@ import { Button, TOAST_TYPE, setToast } from "@plane/ui";
import { EstimatePointItemSwitchPreview } from "@/components/estimates/points"; import { EstimatePointItemSwitchPreview } from "@/components/estimates/points";
// constants // constants
import { EEstimateSystem, EEstimateUpdateStages, ESTIMATE_SYSTEMS } from "@/constants/estimates"; import { EEstimateSystem, EEstimateUpdateStages, ESTIMATE_SYSTEMS } from "@/constants/estimates";
// helpers
import { isEstimatePointValuesRepeated } from "@/helpers/estimates";
// hooks // hooks
import { useEstimate } from "@/hooks/store"; import { useEstimate } from "@/hooks/store";
@ -25,6 +27,7 @@ export const EstimatePointSwitchRoot: FC<TEstimatePointSwitchRoot> = observer((p
const { asJson: estimate, estimatePointIds, estimatePointById, updateEstimateSwitch } = useEstimate(estimateId); const { asJson: estimate, estimatePointIds, estimatePointById, updateEstimateSwitch } = useEstimate(estimateId);
// states // states
const [estimatePoints, setEstimatePoints] = useState<TEstimatePointsObject[] | undefined>(undefined); const [estimatePoints, setEstimatePoints] = useState<TEstimatePointsObject[] | undefined>(undefined);
const [estimateError, setEstimateError] = useState<{ type: string; ids: string[] } | undefined>(undefined);
useEffect(() => { useEffect(() => {
if (!estimatePointIds) return; if (!estimatePointIds) return;
@ -44,9 +47,34 @@ export const EstimatePointSwitchRoot: FC<TEstimatePointSwitchRoot> = observer((p
}); });
}; };
const validateEstimateErrorHandlers = () => {
const currentEstimateError = (estimatePoints || [])
?.map((estimatePoint) => {
if (!estimatePoint.value || estimatePoint.value === "") return estimatePoint.id;
})
.filter((estimatePointId) => estimatePointId !== undefined) as string[];
setEstimateError({ type: "empty-fields", ids: currentEstimateError });
if (currentEstimateError.length > 0) return true;
else return false;
};
const handleSwitchEstimate = async () => { const handleSwitchEstimate = async () => {
try { try {
if (!workspaceSlug || !projectId) return; if (!workspaceSlug || !projectId) return;
setEstimateError(undefined);
if (!validateEstimateErrorHandlers()) {
const isRepeated =
(estimateSystemSwitchType &&
estimatePoints &&
isEstimatePointValuesRepeated(
estimatePoints?.map((estimatePoint) => estimatePoint?.value),
estimateSystemSwitchType,
undefined
)) ||
false;
if (!isRepeated) {
const validatedEstimatePoints: TEstimatePointsObject[] = []; const validatedEstimatePoints: TEstimatePointsObject[] = [];
if ([EEstimateSystem.POINTS, EEstimateSystem.TIME].includes(estimateSystemSwitchType)) { if ([EEstimateSystem.POINTS, EEstimateSystem.TIME].includes(estimateSystemSwitchType)) {
estimatePoints?.map((estimatePoint) => { estimatePoints?.map((estimatePoint) => {
@ -83,6 +111,20 @@ export const EstimatePointSwitchRoot: FC<TEstimatePointSwitchRoot> = observer((p
message: "something went wrong", message: "something went wrong",
}); });
} }
} else {
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
message: "Estimate point values cannot be repeated",
});
}
} else {
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
message: "Please fill all the estimate point fields",
});
}
} catch (error) { } catch (error) {
setToast({ setToast({
type: TOAST_TYPE.ERROR, type: TOAST_TYPE.ERROR,
@ -95,7 +137,7 @@ export const EstimatePointSwitchRoot: FC<TEstimatePointSwitchRoot> = observer((p
if (!workspaceSlug || !projectId || !estimateId || !estimatePoints) return <></>; if (!workspaceSlug || !projectId || !estimateId || !estimatePoints) return <></>;
return ( return (
<> <>
<div className="space-y-3"> <div className="space-y-3 px-5 pb-5">
<div className="text-sm font-medium flex items-center gap-2"> <div className="text-sm font-medium flex items-center gap-2">
<div className="w-full">Current {estimate?.type}</div> <div className="w-full">Current {estimate?.type}</div>
<div className="flex-shrink-0 w-4 h-4" /> <div className="flex-shrink-0 w-4 h-4" />
@ -106,9 +148,11 @@ export const EstimatePointSwitchRoot: FC<TEstimatePointSwitchRoot> = observer((p
<EstimatePointItemSwitchPreview <EstimatePointItemSwitchPreview
key={estimateObject?.id} key={estimateObject?.id}
estimateId={estimateId} estimateId={estimateId}
estimatePointId={estimateObject.id} estimatePointId={estimateObject?.id}
estimatePoint={estimateObject} estimatePoint={estimateObject}
handleEstimatePoint={(value: string) => handleEstimatePoints(index, value)} handleEstimatePoint={(value: string) => handleEstimatePoints(index, value)}
errorType={estimateError?.type}
isError={estimateObject?.id ? estimateError?.ids?.includes(estimateObject.id) : false}
/> />
))} ))}
</div> </div>

View File

@ -67,12 +67,18 @@ export const UpdateEstimateModal: FC<TUpdateEstimateModal> = observer((props) =>
)} )}
</div> </div>
<div>
{!estimateEditType && (
<div className="px-5"> <div className="px-5">
{!estimateEditType && <EstimateUpdateStageOne handleEstimateEditType={handleEstimateEditType} />} <EstimateUpdateStageOne handleEstimateEditType={handleEstimateEditType} />
</div>
)}
{estimateEditType && estimateId && ( {estimateEditType && estimateId && (
<> <>
{estimateEditType === EEstimateUpdateStages.EDIT && ( {estimateEditType === EEstimateUpdateStages.EDIT && (
<div className="px-5">
<EstimatePointEditRoot workspaceSlug={workspaceSlug} projectId={projectId} estimateId={estimateId} /> <EstimatePointEditRoot workspaceSlug={workspaceSlug} projectId={projectId} estimateId={estimateId} />
</div>
)} )}
{estimateEditType === EEstimateUpdateStages.SWITCH && estimateSystemSwitchType && ( {estimateEditType === EEstimateUpdateStages.SWITCH && estimateSystemSwitchType && (
<EstimatePointSwitchRoot <EstimatePointSwitchRoot