plane/web/store/estimate.store.ts
rahulramesha b3ac9def8d
fix: issue property dropdown data flow (#3425)
* dev: workspace states and estimates

* refactor issue dropdown logic to help work properly with issues on global level

* fix: project labels response change

* fix label type

* change store computed actions to computed functions from mobx-utils

* fix: state response change

* chore: project and workspace state change

* fix state and label types

* chore: state and label serializer change

* modify state and label types

* fix dropdown reset on project id change

* fix label sort order

---------

Co-authored-by: pablohashescobar <nikhilschacko@gmail.com>
Co-authored-by: Rahul R <rahulr@Rahuls-MacBook-Pro.local>
Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
Co-authored-by: Rahul R <rahul.ramesha@plane.so>
2024-01-22 17:07:32 +05:30

222 lines
7.7 KiB
TypeScript

import { observable, action, makeObservable, runInAction, computed } from "mobx";
import set from "lodash/set";
// services
import { ProjectEstimateService } from "services/project";
// types
import { RootStore } from "store/root.store";
import { IEstimate, IEstimateFormData } from "@plane/types";
import { computedFn } from "mobx-utils";
export interface IEstimateStore {
//Loaders
fetchedMap: Record<string, boolean>;
// observables
estimateMap: Record<string, IEstimate>;
// computed
areEstimatesEnabledForCurrentProject: boolean;
projectEstimates: IEstimate[] | null;
activeEstimateDetails: IEstimate | null;
// computed actions
areEstimatesEnabledForProject: (projectId: string) => boolean;
getEstimatePointValue: (estimateKey: number | null, projectId?: string) => string;
getProjectEstimateById: (estimateId: string) => IEstimate | null;
getProjectActiveEstimateDetails: (projectId: string) => IEstimate | null;
// fetch actions
fetchProjectEstimates: (workspaceSlug: string, projectId: string) => Promise<IEstimate[]>;
fetchWorskpaceEstimates: (workspaceSlug: string) => Promise<IEstimate[]>;
// crud actions
createEstimate: (workspaceSlug: string, projectId: string, data: IEstimateFormData) => Promise<IEstimate>;
updateEstimate: (
workspaceSlug: string,
projectId: string,
estimateId: string,
data: IEstimateFormData
) => Promise<IEstimate>;
deleteEstimate: (workspaceSlug: string, projectId: string, estimateId: string) => Promise<void>;
}
export class EstimateStore implements IEstimateStore {
// observables
estimateMap: Record<string, IEstimate> = {};
//loaders
fetchedMap: Record<string, boolean> = {};
// root store
rootStore;
// services
estimateService;
constructor(_rootStore: RootStore) {
makeObservable(this, {
// observables
estimateMap: observable,
fetchedMap: observable,
// computed
areEstimatesEnabledForCurrentProject: computed,
projectEstimates: computed,
activeEstimateDetails: computed,
// actions
fetchProjectEstimates: action,
fetchWorskpaceEstimates: action,
createEstimate: action,
updateEstimate: action,
deleteEstimate: action,
});
// root store
this.rootStore = _rootStore;
// services
this.estimateService = new ProjectEstimateService();
}
/**
* @description returns true if estimates are enabled for current project, false otherwise
*/
get areEstimatesEnabledForCurrentProject() {
const currentProjectDetails = this.rootStore.projectRoot.project.currentProjectDetails;
if (!currentProjectDetails) return false;
return Boolean(currentProjectDetails?.estimate);
}
/**
* @description returns the list of estimates for current project
*/
get projectEstimates() {
const projectId = this.rootStore.app.router.projectId;
const worksapceSlug = this.rootStore.app.router.workspaceSlug || "";
if (!projectId || !(this.fetchedMap[projectId] || this.fetchedMap[worksapceSlug])) return null;
return Object.values(this.estimateMap).filter((estimate) => estimate.project === projectId);
}
/**
* @description returns the active estimate details for current project
*/
get activeEstimateDetails() {
const currentProjectDetails = this.rootStore.projectRoot.project.currentProjectDetails;
if (!currentProjectDetails || !currentProjectDetails?.estimate) return null;
return this.estimateMap?.[currentProjectDetails?.estimate || ""] || null;
}
/**
* @description returns true if estimates are enabled for a project using project id
* @param projectId
*/
areEstimatesEnabledForProject = computedFn((projectId: string) => {
const projectDetails = this.rootStore.projectRoot.project.getProjectById(projectId);
if (!projectDetails) return false;
return Boolean(projectDetails.estimate) ?? false;
});
/**
* @description returns the point value for the given estimate key to display in the UI
*/
getEstimatePointValue = computedFn((estimateKey: number | null, projectId?: string) => {
if (estimateKey === null) return "None";
const activeEstimate = projectId ? this.getProjectActiveEstimateDetails(projectId) : this.activeEstimateDetails;
return activeEstimate?.points?.find((point) => point.key === estimateKey)?.value || "None";
});
/**
* @description returns the estimate details for the given estimate id
* @param estimateId
*/
getProjectEstimateById = computedFn((estimateId: string) => {
if (!this.projectEstimates) return null;
const estimateInfo = this.estimateMap?.[estimateId] || null;
return estimateInfo;
});
/**
* @description returns the estimate details for the given estimate id
* @param projectId
*/
getProjectActiveEstimateDetails = computedFn((projectId: string) => {
const projectDetails = this.rootStore.projectRoot.project?.getProjectById(projectId);
const worksapceSlug = this.rootStore.app.router.workspaceSlug || "";
if (!projectDetails || !projectDetails?.estimate || !(this.fetchedMap[projectId] || this.fetchedMap[worksapceSlug]))
return null;
return this.estimateMap?.[projectDetails?.estimate || ""] || null;
});
/**
* @description fetches the list of estimates for the given project
* @param workspaceSlug
* @param projectId
*/
fetchProjectEstimates = async (workspaceSlug: string, projectId: string) =>
await this.estimateService.getEstimatesList(workspaceSlug, projectId).then((response) => {
runInAction(() => {
response.forEach((estimate) => {
set(this.estimateMap, estimate.id, estimate);
});
this.fetchedMap[projectId] = true;
});
return response;
});
/**
* @description fetches the list of estimates for the given project
* @param workspaceSlug
* @param projectId
*/
fetchWorskpaceEstimates = async (workspaceSlug: string) =>
await this.estimateService.getWorkspaceEstimatesList(workspaceSlug).then((response) => {
runInAction(() => {
response.forEach((estimate) => {
set(this.estimateMap, estimate.id, estimate);
});
this.fetchedMap[workspaceSlug] = true;
});
return response;
});
/**
* @description creates a new estimate for the given project
* @param workspaceSlug
* @param projectId
* @param data
*/
createEstimate = async (workspaceSlug: string, projectId: string, data: IEstimateFormData) =>
await this.estimateService.createEstimate(workspaceSlug, projectId, data).then((response) => {
const responseEstimate = {
...response.estimate,
points: response.estimate_points,
};
runInAction(() => {
set(this.estimateMap, [responseEstimate.id], responseEstimate);
});
return response.estimate;
});
/**
* @description updates the given estimate for the given project
* @param workspaceSlug
* @param projectId
* @param estimateId
* @param data
*/
updateEstimate = async (workspaceSlug: string, projectId: string, estimateId: string, data: IEstimateFormData) =>
await this.estimateService.patchEstimate(workspaceSlug, projectId, estimateId, data).then((response) => {
runInAction(() => {
set(this.estimateMap, estimateId, {
...this.estimateMap[estimateId],
...data.estimate,
points: [...data.estimate_points],
});
});
return response;
});
/**
* @description deletes the given estimate for the given project
* @param workspaceSlug
* @param projectId
* @param estimateId
*/
deleteEstimate = async (workspaceSlug: string, projectId: string, estimateId: string) =>
await this.estimateService.deleteEstimate(workspaceSlug, projectId, estimateId).then(() => {
runInAction(() => {
delete this.estimateMap[estimateId];
});
});
}