mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
Extract the list row actions
This commit is contained in:
parent
0a72adc2a6
commit
a725385190
1
web/components/estimates/delete/index.ts
Normal file
1
web/components/estimates/delete/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./modal";
|
131
web/components/estimates/delete/modal.tsx
Normal file
131
web/components/estimates/delete/modal.tsx
Normal file
@ -0,0 +1,131 @@
|
||||
import { FC, useEffect, useMemo, 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";
|
||||
|
||||
type TDeleteEstimateModal = {
|
||||
workspaceSlug: string;
|
||||
projectId: string;
|
||||
isOpen: boolean;
|
||||
handleClose: () => void;
|
||||
};
|
||||
|
||||
export const DeleteEstimateModal: FC<TDeleteEstimateModal> = observer((props) => {
|
||||
// props
|
||||
const { workspaceSlug, projectId, isOpen, handleClose } = props;
|
||||
// hooks
|
||||
const { createEstimate } = useProjectEstimates();
|
||||
// states
|
||||
const [estimateSystem, setEstimateSystem] = useState<TEstimateSystemKeys>(EEstimateSystem.CATEGORIES);
|
||||
const [estimatePoints, setEstimatePoints] = useState<TEstimatePointsObject[] | undefined>(undefined);
|
||||
|
||||
const handleUpdatePoints = (newPoints: TEstimatePointsObject[] | undefined) => setEstimatePoints(newPoints);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
setEstimateSystem(EEstimateSystem.CATEGORIES);
|
||||
setEstimatePoints(undefined);
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
const handleCreateEstimate = 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);
|
||||
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: "Estimate created",
|
||||
message: "Created and Enabled successfully",
|
||||
});
|
||||
handleClose();
|
||||
} catch (error) {
|
||||
setToast({
|
||||
type: TOAST_TYPE.ERROR,
|
||||
title: "Estimate creation failed",
|
||||
message: "We were unable to create the new 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>
|
||||
|
||||
{/* 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>
|
||||
|
||||
<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}>
|
||||
Cancel
|
||||
</Button>
|
||||
{estimatePoints && (
|
||||
<Button variant="primary" size="sm" onClick={handleCreateEstimate}>
|
||||
Create Estimate
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</ModalCore>
|
||||
);
|
||||
});
|
30
web/components/estimates/estimate-list-item-buttons.tsx
Normal file
30
web/components/estimates/estimate-list-item-buttons.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import { observer } from "mobx-react";
|
||||
import { Pen, Trash } from "lucide-react";
|
||||
|
||||
type TEstimateListItem = {
|
||||
estimateId: string;
|
||||
isAdmin: boolean;
|
||||
isEstimateEnabled: boolean;
|
||||
isEditable: boolean;
|
||||
onEditClick?: (estimateId: string) => void;
|
||||
};
|
||||
|
||||
export const EstimateListItemButtons: FC<TEstimateListItem> = observer((props) => {
|
||||
const { estimateId, isAdmin, isEditable, onEditClick } = props;
|
||||
return isAdmin && isEditable ? (
|
||||
<div className="flex">
|
||||
<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)}
|
||||
>
|
||||
<Pen size={12} />
|
||||
</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)}
|
||||
>
|
||||
<Trash size={12} />
|
||||
</button>
|
||||
</div>
|
||||
) : null;
|
||||
});
|
@ -5,6 +5,7 @@ import { Pen } from "lucide-react";
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
// hooks
|
||||
import { useEstimate, useProjectEstimates } from "@/hooks/store";
|
||||
import { EstimateListItemButtons } from "./estimate-list-item-buttons";
|
||||
|
||||
type TEstimateListItem = {
|
||||
estimateId: string;
|
||||
@ -39,14 +40,7 @@ export const EstimateListItem: FC<TEstimateListItem> = observer((props) => {
|
||||
<h3 className="font-medium text-base">{currentEstimate?.name}</h3>
|
||||
<p className="text-xs">{(estimatePointValues || [])?.join(", ")}</p>
|
||||
</div>
|
||||
{isAdmin && isEditable && (
|
||||
<div
|
||||
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)}
|
||||
>
|
||||
<Pen size={12} />
|
||||
</div>
|
||||
)}
|
||||
<EstimateListItemButtons {...props} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
@ -16,5 +16,10 @@ export * from "./create";
|
||||
// create
|
||||
export * from "./update";
|
||||
|
||||
// delete
|
||||
export * from "./delete";
|
||||
|
||||
// estimate points
|
||||
export * from "./points";
|
||||
|
||||
export * from "./estimate-list-item-buttons";
|
||||
|
@ -73,6 +73,14 @@ class EstimateService extends APIService {
|
||||
}
|
||||
}
|
||||
|
||||
async deleteEstimate(workspaceSlug: string, projectId: string, estimateId: string): Promise<any> {
|
||||
try {
|
||||
await this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/estimates/${estimateId}/`);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async createEstimatePoint(
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
|
@ -266,4 +266,18 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description deletes the given estimate for the given 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];
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user