mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
chore: UI and typos
This commit is contained in:
parent
d82c6cc05c
commit
e55801c58b
@ -132,7 +132,7 @@ export const CreateEstimateModal: FC<TCreateEstimateModal> = observer((props) =>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="relative flex justify-end items-center gap-3 px-5 pt-5 border-t border-custom-border-100">
|
||||
<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>
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { FC } from "react";
|
||||
import { Info } from "lucide-react";
|
||||
import { TEstimateSystemKeys } from "@plane/types";
|
||||
import { RadioInput, Tooltip } from "@plane/ui";
|
||||
// constants
|
||||
import { RadioInput } from "@plane/ui";
|
||||
import { ESTIMATE_SYSTEMS } from "@/constants/estimates";
|
||||
// types
|
||||
|
||||
type TEstimateCreateStageOne = {
|
||||
estimateSystem: TEstimateSystemKeys;
|
||||
@ -18,41 +18,50 @@ export const EstimateCreateStageOne: FC<TEstimateCreateStageOne> = (props) => {
|
||||
|
||||
if (!currentEstimateSystem) return <></>;
|
||||
return (
|
||||
<div className="space-y-7">
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-4 sm:flex sm:items-center sm:space-x-10 sm:space-y-0 gap-2 mb-2">
|
||||
<RadioInput
|
||||
options={Object.keys(ESTIMATE_SYSTEMS).map((system) => {
|
||||
const currentSystem = system as TEstimateSystemKeys;
|
||||
return {
|
||||
label: ESTIMATE_SYSTEMS[currentSystem]?.name || <div>Hello</div>,
|
||||
label: !ESTIMATE_SYSTEMS[currentSystem]?.is_available ? (
|
||||
<div className="relative flex items-center gap-2 cursor-no-drop text-custom-text-300">
|
||||
{ESTIMATE_SYSTEMS[currentSystem]?.name}
|
||||
<Tooltip tooltipContent={"Not available now"}>
|
||||
<Info size={12} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
) : (
|
||||
<div>{ESTIMATE_SYSTEMS[currentSystem]?.name}</div>
|
||||
),
|
||||
value: system,
|
||||
disabled: !ESTIMATE_SYSTEMS[currentSystem]?.is_available,
|
||||
};
|
||||
})}
|
||||
label="Choose an estimate system"
|
||||
labelClassName="text-sm font-medium text-custom-text-200 mb-3"
|
||||
labelClassName="text-sm font-medium text-custom-text-200 mb-2"
|
||||
wrapperClassName="relative flex flex-wrap gap-14"
|
||||
fieldClassName="relative flex items-center gap-2"
|
||||
fieldClassName="relative flex items-center gap-1.5"
|
||||
buttonClassName="size-4"
|
||||
selected={estimateSystem}
|
||||
onChange={(value) => handleEstimateSystem(value as TEstimateSystemKeys)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-2">
|
||||
<div className="text-sm font-medium text-custom-text-200">Start from scratch</div>
|
||||
<button
|
||||
className="border border-custom-border-200 rounded-md p-3 py-2.5 text-left space-y-1 w-full block hover:bg-custom-background-90"
|
||||
onClick={() => handleEstimatePoints("custom")}
|
||||
>
|
||||
<p className="block text-base">Custom</p>
|
||||
<p className="text-xs text-gray-400">
|
||||
<p className="text-base font-medium">Custom</p>
|
||||
<p className="text-xs text-custom-text-300">
|
||||
Add your own <span className="lowercase">{currentEstimateSystem.name}</span> from scratch
|
||||
</p>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-2">
|
||||
<div className="text-sm font-medium text-custom-text-200">Choose a template</div>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-3">
|
||||
{Object.keys(currentEstimateSystem.templates).map((name) =>
|
||||
@ -62,8 +71,8 @@ export const EstimateCreateStageOne: FC<TEstimateCreateStageOne> = (props) => {
|
||||
className="border border-custom-border-200 rounded-md p-3 py-2.5 text-left space-y-1 hover:bg-custom-background-90"
|
||||
onClick={() => handleEstimatePoints(name)}
|
||||
>
|
||||
<p className="block text-base">{currentEstimateSystem.templates[name]?.title}</p>
|
||||
<p className="text-xs text-gray-400">
|
||||
<p className="text-base font-medium">{currentEstimateSystem.templates[name]?.title}</p>
|
||||
<p className="text-xs text-custom-text-300">
|
||||
{currentEstimateSystem.templates[name]?.values?.map((template) => template?.value)?.join(", ")}
|
||||
</p>
|
||||
</button>
|
||||
|
@ -28,7 +28,7 @@ export const EstimateDisableSwitch: FC<TEstimateDisableSwitch> = observer((props
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: "Success!",
|
||||
message: "Estimates have been disabled",
|
||||
message: currentProjectActiveEstimate ? "Estimates have been disabled" : "Estimates have been enabled",
|
||||
});
|
||||
} catch (err) {
|
||||
setToast({
|
||||
|
@ -34,7 +34,7 @@ export const EstimatePointItemCreatePreview: FC<TEstimatePointItemCreatePreview>
|
||||
{estimatePoint?.value ? (
|
||||
estimatePoint?.value
|
||||
) : (
|
||||
<span className="text-custom-text-200">Enter Estimate Value</span>
|
||||
<span className="text-custom-text-400">Enter estimate point</span>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
|
@ -77,7 +77,7 @@ export const EstimatePointItemCreateUpdate: FC<TEstimatePointItemCreateUpdate> =
|
||||
value={estimateInputValue}
|
||||
onChange={(e) => setEstimateInputValue(e.target.value)}
|
||||
className="border-none focus:ring-0 focus:border-0 focus:outline-none p-2.5 w-full bg-transparent"
|
||||
placeholder="Enter estimate value"
|
||||
placeholder="Enter estimate point"
|
||||
autoFocus
|
||||
/>
|
||||
{error && (
|
||||
|
@ -85,6 +85,7 @@ export const EstimatePointCreate: FC<TEstimatePointCreate> = observer((props) =>
|
||||
value={estimateInputValue}
|
||||
onChange={(e) => setEstimateInputValue(e.target.value)}
|
||||
className="border-none focus:ring-0 focus:border-0 focus:outline-none p-2.5 w-full bg-transparent"
|
||||
placeholder="Enter estimate point"
|
||||
autoFocus
|
||||
/>
|
||||
{error && (
|
||||
|
@ -37,7 +37,11 @@ export const EstimatePointItemPreview: FC<TEstimatePointItemPreview> = observer(
|
||||
<GripVertical size={14} className="text-custom-text-200" />
|
||||
</div>
|
||||
<div ref={EstimatePointValueRef} className="py-2.5 w-full">
|
||||
{estimatePoint?.value}
|
||||
{estimatePoint?.value ? (
|
||||
estimatePoint?.value
|
||||
) : (
|
||||
<span className="text-custom-text-400">Enter estimate point</span>
|
||||
)}
|
||||
</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"
|
||||
|
@ -90,6 +90,7 @@ export const EstimatePointUpdate: FC<TEstimatePointUpdate> = observer((props) =>
|
||||
value={estimateInputValue}
|
||||
onChange={(e) => setEstimateInputValue(e.target.value)}
|
||||
className="border-none focus:ring-0 focus:border-0 focus:outline-none p-2.5 w-full bg-transparent"
|
||||
placeholder="Enter estimate point"
|
||||
autoFocus
|
||||
/>
|
||||
{error && (
|
||||
|
@ -113,7 +113,7 @@ export const EstimatePointSwitchRoot: FC<TEstimatePointSwitchRoot> = observer((p
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="relative flex justify-end items-center gap-3 px-5 pt-5 border-t border-custom-border-100">
|
||||
<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>
|
||||
|
@ -88,7 +88,7 @@ export const UpdateEstimateModal: FC<TUpdateEstimateModal> = observer((props) =>
|
||||
</div>
|
||||
|
||||
{estimateEditType === undefined && (
|
||||
<div className="relative flex justify-end items-center gap-3 px-5 pt-5 border-t border-custom-border-100">
|
||||
<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>
|
||||
|
33
web/helpers/estimates.ts
Normal file
33
web/helpers/estimates.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { EEstimateSystem } from "@plane/types/src/enums";
|
||||
|
||||
export const isEstimatePointValuesRepeated = (
|
||||
estimatePoints: string[],
|
||||
estimateType: EEstimateSystem,
|
||||
newEstimatePoint?: string | undefined
|
||||
) => {
|
||||
const currentEstimatePoints = estimatePoints.map((estimatePoint) => estimatePoint.trim());
|
||||
let isValid = false;
|
||||
|
||||
if (newEstimatePoint === undefined) {
|
||||
if (estimateType === EEstimateSystem.CATEGORIES) {
|
||||
const points = new Set(currentEstimatePoints);
|
||||
if (points.size === currentEstimatePoints.length) isValid = true;
|
||||
} else if ([EEstimateSystem.POINTS, EEstimateSystem.TIME].includes(estimateType)) {
|
||||
currentEstimatePoints.map((point) => {
|
||||
if (Number(point) === Number(newEstimatePoint)) isValid = true;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (estimateType === EEstimateSystem.CATEGORIES) {
|
||||
currentEstimatePoints.map((point) => {
|
||||
if (point === newEstimatePoint.trim()) isValid = true;
|
||||
});
|
||||
} else if ([EEstimateSystem.POINTS, EEstimateSystem.TIME].includes(estimateType)) {
|
||||
currentEstimatePoints.map((point) => {
|
||||
if (Number(point) === Number(newEstimatePoint.trim())) isValid = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return isValid;
|
||||
};
|
@ -5,7 +5,7 @@ import { API_BASE_URL } from "@/helpers/common.helper";
|
||||
// services
|
||||
import { APIService } from "@/services/api.service";
|
||||
|
||||
export class EstimateService extends APIService {
|
||||
class EstimateService extends APIService {
|
||||
constructor() {
|
||||
super(API_BASE_URL);
|
||||
}
|
||||
@ -125,3 +125,6 @@ export class EstimateService extends APIService {
|
||||
});
|
||||
}
|
||||
}
|
||||
const estimateService = new EstimateService();
|
||||
|
||||
export default estimateService;
|
||||
|
@ -1,5 +1,4 @@
|
||||
export * from "./project.service";
|
||||
export * from "./project-estimate.service";
|
||||
export * from "./estimate.service";
|
||||
export * from "./project-export.service";
|
||||
export * from "./project-member.service";
|
||||
|
@ -1,72 +0,0 @@
|
||||
// services
|
||||
import { API_BASE_URL } from "@/helpers/common.helper";
|
||||
import { APIService } from "@/services/api.service";
|
||||
// types
|
||||
import type { IEstimate, IEstimateFormData, IEstimatePoint } from "@plane/types";
|
||||
// helpers
|
||||
|
||||
export class ProjectEstimateService extends APIService {
|
||||
constructor() {
|
||||
super(API_BASE_URL);
|
||||
}
|
||||
|
||||
async createEstimate(
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: IEstimateFormData
|
||||
): Promise<{
|
||||
estimate: IEstimate;
|
||||
estimate_points: IEstimatePoint[];
|
||||
}> {
|
||||
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/estimates/`, data)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response;
|
||||
});
|
||||
}
|
||||
|
||||
async patchEstimate(
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
estimateId: string,
|
||||
data: IEstimateFormData
|
||||
): Promise<any> {
|
||||
return this.patch(`/api/workspaces/${workspaceSlug}/projects/${projectId}/estimates/${estimateId}/`, data)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
|
||||
async getEstimateDetails(workspaceSlug: string, projectId: string, estimateId: string): Promise<IEstimate> {
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/estimates/${estimateId}/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
|
||||
async getEstimatesList(workspaceSlug: string, projectId: string): Promise<IEstimate[]> {
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/estimates/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
|
||||
async deleteEstimate(workspaceSlug: string, projectId: string, estimateId: string): Promise<any> {
|
||||
return this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/estimates/${estimateId}/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
|
||||
async getWorkspaceEstimatesList(workspaceSlug: string): Promise<IEstimate[]> {
|
||||
return this.get(`/api/workspaces/${workspaceSlug}/estimates/`)
|
||||
.then((response) => response?.data)
|
||||
.catch((error) => {
|
||||
throw error?.response?.data;
|
||||
});
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ import set from "lodash/set";
|
||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||
import { IEstimate, IEstimatePoint as IEstimatePointType } from "@plane/types";
|
||||
// services
|
||||
import { EstimateService } from "@/services/project/estimate.service";
|
||||
import estimateService from "@/services/project/estimate.service";
|
||||
// store
|
||||
import { RootStore } from "@/store/root.store";
|
||||
|
||||
@ -41,8 +41,6 @@ export class EstimatePoint implements IEstimatePoint {
|
||||
updated_by: string | undefined = undefined;
|
||||
// observables
|
||||
error: TErrorCodes | undefined = undefined;
|
||||
// service
|
||||
service: EstimateService;
|
||||
|
||||
constructor(
|
||||
private store: RootStore,
|
||||
@ -80,8 +78,6 @@ export class EstimatePoint implements IEstimatePoint {
|
||||
this.updated_at = this.data.updated_at;
|
||||
this.created_by = this.data.created_by;
|
||||
this.updated_by = this.data.updated_by;
|
||||
// service
|
||||
this.service = new EstimateService();
|
||||
}
|
||||
|
||||
// computed
|
||||
@ -102,6 +98,11 @@ export class EstimatePoint implements IEstimatePoint {
|
||||
}
|
||||
|
||||
// helper actions
|
||||
/**
|
||||
* @description updating an estimate point object in local store
|
||||
* @param { Partial<IEstimatePointType> } estimatePoint
|
||||
* @returns { void }
|
||||
*/
|
||||
updateEstimatePointObject = (estimatePoint: Partial<IEstimatePointType>) => {
|
||||
Object.keys(estimatePoint).map((key) => {
|
||||
const estimatePointKey = key as keyof IEstimatePointType;
|
||||
@ -123,7 +124,7 @@ export class EstimatePoint implements IEstimatePoint {
|
||||
try {
|
||||
if (!this.projectEstimate?.id || !this.id || !payload) return undefined;
|
||||
|
||||
const estimatePoint = await this.service.updateEstimatePoint(
|
||||
const estimatePoint = await estimateService.updateEstimatePoint(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
this.projectEstimate?.id,
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
TEstimatePointsObject,
|
||||
} from "@plane/types";
|
||||
// services
|
||||
import { EstimateService } from "@/services/project/estimate.service";
|
||||
import estimateService from "@/services/project/estimate.service";
|
||||
// store
|
||||
import { IEstimatePoint, EstimatePoint } from "@/store/estimates/estimate-point";
|
||||
import { RootStore } from "@/store/root.store";
|
||||
@ -69,8 +69,6 @@ export class Estimate implements IEstimate {
|
||||
// observables
|
||||
error: TErrorCodes | undefined = undefined;
|
||||
estimatePoints: Record<string, IEstimatePoint> = {};
|
||||
// service
|
||||
service: EstimateService;
|
||||
|
||||
constructor(
|
||||
private store: RootStore,
|
||||
@ -116,8 +114,6 @@ export class Estimate implements IEstimate {
|
||||
if (estimationPoint.id)
|
||||
set(this.estimatePoints, [estimationPoint.id], new EstimatePoint(this.store, this.data, estimationPoint));
|
||||
});
|
||||
// service
|
||||
this.service = new EstimateService();
|
||||
}
|
||||
|
||||
// computed
|
||||
@ -169,7 +165,7 @@ export class Estimate implements IEstimate {
|
||||
try {
|
||||
if (!this.id || !payload) return;
|
||||
|
||||
const estimate = await this.service.updateEstimate(workspaceSlug, projectId, this.id, {
|
||||
const estimate = await estimateService.updateEstimate(workspaceSlug, projectId, this.id, {
|
||||
estimate_points: payload,
|
||||
});
|
||||
runInAction(() => {
|
||||
@ -201,7 +197,7 @@ export class Estimate implements IEstimate {
|
||||
try {
|
||||
if (!this.id || !payload) return;
|
||||
|
||||
const estimate = await this.service.updateEstimate(workspaceSlug, projectId, this.id, payload);
|
||||
const estimate = await estimateService.updateEstimate(workspaceSlug, projectId, this.id, payload);
|
||||
if (estimate) {
|
||||
runInAction(() => {
|
||||
this.name = estimate?.name;
|
||||
@ -238,7 +234,7 @@ export class Estimate implements IEstimate {
|
||||
try {
|
||||
if (!this.id || !payload) return;
|
||||
|
||||
const estimatePoint = await this.service.createEstimatePoint(workspaceSlug, projectId, this.id, payload);
|
||||
const estimatePoint = await estimateService.createEstimatePoint(workspaceSlug, projectId, this.id, payload);
|
||||
if (estimatePoint) {
|
||||
runInAction(() => {
|
||||
if (estimatePoint.id) {
|
||||
@ -268,7 +264,7 @@ export class Estimate implements IEstimate {
|
||||
try {
|
||||
if (!this.id) return;
|
||||
|
||||
const deleteEstimatePoint = await this.service.removeEstimatePoint(
|
||||
const deleteEstimatePoint = await estimateService.removeEstimatePoint(
|
||||
workspaceSlug,
|
||||
projectId,
|
||||
this.id,
|
||||
|
@ -5,7 +5,7 @@ import { action, computed, makeObservable, observable, runInAction } from "mobx"
|
||||
import { computedFn } from "mobx-utils";
|
||||
import { IEstimate as IEstimateType, IEstimateFormData } from "@plane/types";
|
||||
// services
|
||||
import { EstimateService } from "@/services/project/estimate.service";
|
||||
import estimateService from "@/services/project/estimate.service";
|
||||
// store
|
||||
import { IEstimate, Estimate } from "@/store/estimates/estimate";
|
||||
import { RootStore } from "@/store/root.store";
|
||||
@ -47,8 +47,6 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
|
||||
loader: TEstimateLoader = undefined;
|
||||
estimates: Record<string, IEstimate> = {}; // estimate_id -> estimate
|
||||
error: TErrorCodes | undefined = undefined;
|
||||
// service
|
||||
service: EstimateService;
|
||||
|
||||
constructor(private store: RootStore) {
|
||||
makeObservable(this, {
|
||||
@ -65,12 +63,9 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
|
||||
getEstimateById: action,
|
||||
createEstimate: action,
|
||||
});
|
||||
// service
|
||||
this.service = new EstimateService();
|
||||
}
|
||||
|
||||
// computed
|
||||
|
||||
/**
|
||||
* @description get current active estimate id for a project
|
||||
* @returns { string | undefined }
|
||||
@ -146,7 +141,7 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
|
||||
this.error = undefined;
|
||||
if (Object.keys(this.estimates || {}).length <= 0) this.loader = loader ? loader : "init-loader";
|
||||
|
||||
const estimates = await this.service.fetchWorkspaceEstimates(workspaceSlug);
|
||||
const estimates = await estimateService.fetchWorkspaceEstimates(workspaceSlug);
|
||||
if (estimates && estimates.length > 0) {
|
||||
runInAction(() => {
|
||||
estimates.forEach((estimate) => {
|
||||
@ -181,7 +176,7 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
|
||||
this.error = undefined;
|
||||
if (!this.estimateIdsByProjectId(projectId)) this.loader = loader ? loader : "init-loader";
|
||||
|
||||
const estimates = await this.service.fetchProjectEstimates(workspaceSlug, projectId);
|
||||
const estimates = await estimateService.fetchProjectEstimates(workspaceSlug, projectId);
|
||||
if (estimates && estimates.length > 0) {
|
||||
runInAction(() => {
|
||||
estimates.forEach((estimate) => {
|
||||
@ -216,7 +211,7 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
|
||||
try {
|
||||
this.error = undefined;
|
||||
|
||||
const estimate = await this.service.fetchEstimateById(workspaceSlug, projectId, estimateId);
|
||||
const estimate = await estimateService.fetchEstimateById(workspaceSlug, projectId, estimateId);
|
||||
if (estimate) {
|
||||
runInAction(() => {
|
||||
if (estimate.id)
|
||||
@ -252,7 +247,7 @@ export class ProjectEstimateStore implements IProjectEstimateStore {
|
||||
try {
|
||||
this.error = undefined;
|
||||
|
||||
const estimate = await this.service.createEstimate(workspaceSlug, projectId, payload);
|
||||
const estimate = await estimateService.createEstimate(workspaceSlug, projectId, payload);
|
||||
if (estimate) {
|
||||
await this.store.projectRoot.project.updateProject(workspaceSlug, projectId, {
|
||||
estimate: estimate.id,
|
||||
|
Loading…
Reference in New Issue
Block a user