feat: init
This commit is contained in:
151
apps/web/core/store/estimates/estimate-point.ts
Normal file
151
apps/web/core/store/estimates/estimate-point.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
/* eslint-disable no-useless-catch */
|
||||
|
||||
import { set } from "lodash-es";
|
||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||
// types
|
||||
import type { IEstimate, IEstimatePoint as IEstimatePointType } from "@plane/types";
|
||||
// plane web services
|
||||
import estimateService from "@/plane-web/services/project/estimate.service";
|
||||
// store
|
||||
import type { CoreRootStore } from "@/store/root.store";
|
||||
|
||||
type TErrorCodes = {
|
||||
status: string;
|
||||
message?: string;
|
||||
};
|
||||
|
||||
export interface IEstimatePoint extends IEstimatePointType {
|
||||
// observables
|
||||
error: TErrorCodes | undefined;
|
||||
// computed
|
||||
asJson: IEstimatePointType;
|
||||
// helper actions
|
||||
updateEstimatePointObject: (estimatePoint: Partial<IEstimatePointType>) => void;
|
||||
// actions
|
||||
updateEstimatePoint: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
payload: Partial<IEstimatePointType>
|
||||
) => Promise<IEstimatePointType | undefined>;
|
||||
}
|
||||
|
||||
export class EstimatePoint implements IEstimatePoint {
|
||||
// data model observables
|
||||
id: string | undefined = undefined;
|
||||
key: number | undefined = undefined;
|
||||
value: string | undefined = undefined;
|
||||
description: string | undefined = undefined;
|
||||
workspace: string | undefined = undefined;
|
||||
project: string | undefined = undefined;
|
||||
estimate: string | undefined = undefined;
|
||||
created_at: Date | undefined = undefined;
|
||||
updated_at: Date | undefined = undefined;
|
||||
created_by: string | undefined = undefined;
|
||||
updated_by: string | undefined = undefined;
|
||||
// observables
|
||||
error: TErrorCodes | undefined = undefined;
|
||||
|
||||
constructor(
|
||||
private store: CoreRootStore,
|
||||
private projectEstimate: IEstimate,
|
||||
private data: IEstimatePointType
|
||||
) {
|
||||
makeObservable(this, {
|
||||
// data model observables
|
||||
id: observable.ref,
|
||||
key: observable.ref,
|
||||
value: observable.ref,
|
||||
description: observable.ref,
|
||||
workspace: observable.ref,
|
||||
project: observable.ref,
|
||||
estimate: observable.ref,
|
||||
created_at: observable.ref,
|
||||
updated_at: observable.ref,
|
||||
created_by: observable.ref,
|
||||
updated_by: observable.ref,
|
||||
// observables
|
||||
error: observable.ref,
|
||||
// computed
|
||||
asJson: computed,
|
||||
// actions
|
||||
updateEstimatePoint: action,
|
||||
});
|
||||
this.id = this.data.id;
|
||||
this.key = this.data.key;
|
||||
this.value = this.data.value;
|
||||
this.description = this.data.description;
|
||||
this.workspace = this.data.workspace;
|
||||
this.project = this.data.project;
|
||||
this.estimate = this.data.estimate;
|
||||
this.created_at = this.data.created_at;
|
||||
this.updated_at = this.data.updated_at;
|
||||
this.created_by = this.data.created_by;
|
||||
this.updated_by = this.data.updated_by;
|
||||
}
|
||||
|
||||
// computed
|
||||
get asJson() {
|
||||
return {
|
||||
id: this.id,
|
||||
key: this.key,
|
||||
value: this.value,
|
||||
description: this.description,
|
||||
workspace: this.workspace,
|
||||
project: this.project,
|
||||
estimate: this.estimate,
|
||||
created_at: this.created_at,
|
||||
updated_at: this.updated_at,
|
||||
created_by: this.created_by,
|
||||
updated_by: this.updated_by,
|
||||
};
|
||||
}
|
||||
|
||||
// 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;
|
||||
set(this, estimatePointKey, estimatePoint[estimatePointKey]);
|
||||
});
|
||||
};
|
||||
|
||||
// actions
|
||||
/**
|
||||
* @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 {
|
||||
if (!this.projectEstimate?.id || !this.id || !payload) return undefined;
|
||||
|
||||
const estimatePoint = await estimateService.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) {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
313
apps/web/core/store/estimates/project-estimate.store.ts
Normal file
313
apps/web/core/store/estimates/project-estimate.store.ts
Normal file
@@ -0,0 +1,313 @@
|
||||
import { unset, orderBy, set } from "lodash-es";
|
||||
import { action, computed, makeObservable, observable, runInAction } from "mobx";
|
||||
import { computedFn } from "mobx-utils";
|
||||
// types
|
||||
import type { IEstimate as IEstimateType, IEstimateFormData, TEstimateSystemKeys } from "@plane/types";
|
||||
// plane web services
|
||||
import estimateService from "@/plane-web/services/project/estimate.service";
|
||||
// plane web store
|
||||
import type { IEstimate } from "@/plane-web/store/estimates/estimate";
|
||||
import { Estimate } from "@/plane-web/store/estimates/estimate";
|
||||
// store
|
||||
import type { CoreRootStore } from "../root.store";
|
||||
|
||||
type TEstimateLoader = "init-loader" | "mutation-loader" | undefined;
|
||||
type TErrorCodes = {
|
||||
status: string;
|
||||
message?: string;
|
||||
};
|
||||
|
||||
export interface IProjectEstimateStore {
|
||||
// observables
|
||||
loader: TEstimateLoader;
|
||||
estimates: Record<string, IEstimate>;
|
||||
error: TErrorCodes | undefined;
|
||||
// computed
|
||||
currentActiveEstimateId: string | undefined;
|
||||
currentActiveEstimate: IEstimate | undefined;
|
||||
archivedEstimateIds: string[] | undefined;
|
||||
currentProjectEstimateType: TEstimateSystemKeys | undefined;
|
||||
areEstimateEnabledByProjectId: (projectId: string) => boolean;
|
||||
estimateIdsByProjectId: (projectId: string) => string[] | undefined;
|
||||
currentActiveEstimateIdByProjectId: (projectId: string) => string | undefined;
|
||||
estimateById: (estimateId: string) => IEstimate | undefined;
|
||||
// actions
|
||||
getWorkspaceEstimates: (workspaceSlug: string, loader?: TEstimateLoader) => Promise<IEstimateType[] | undefined>;
|
||||
getProjectEstimates: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loader?: TEstimateLoader
|
||||
) => Promise<IEstimateType[] | undefined>;
|
||||
getEstimateById: (estimateId: string) => IEstimate | undefined;
|
||||
createEstimate: (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
data: IEstimateFormData
|
||||
) => Promise<IEstimateType | undefined>;
|
||||
deleteEstimate: (workspaceSlug: string, projectId: string, estimateId: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export class ProjectEstimateStore implements IProjectEstimateStore {
|
||||
// observables
|
||||
loader: TEstimateLoader = undefined;
|
||||
estimates: Record<string, IEstimate> = {}; // estimate_id -> estimate
|
||||
error: TErrorCodes | undefined = undefined;
|
||||
|
||||
constructor(private store: CoreRootStore) {
|
||||
makeObservable(this, {
|
||||
// observables
|
||||
loader: observable.ref,
|
||||
estimates: observable,
|
||||
error: observable,
|
||||
// computed
|
||||
currentActiveEstimateId: computed,
|
||||
currentActiveEstimate: computed,
|
||||
archivedEstimateIds: computed,
|
||||
currentProjectEstimateType: computed,
|
||||
// actions
|
||||
getWorkspaceEstimates: action,
|
||||
getProjectEstimates: action,
|
||||
getEstimateById: action,
|
||||
createEstimate: action,
|
||||
deleteEstimate: action,
|
||||
});
|
||||
}
|
||||
|
||||
// computed
|
||||
|
||||
get currentProjectEstimateType(): TEstimateSystemKeys | undefined {
|
||||
return this.currentActiveEstimateId ? this.estimates[this.currentActiveEstimateId]?.type : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description get current active estimate id for a project
|
||||
* @returns { string | undefined }
|
||||
*/
|
||||
get currentActiveEstimateId(): string | undefined {
|
||||
const { projectId } = this.store.router;
|
||||
if (!projectId) return undefined;
|
||||
const currentActiveEstimateId = Object.values(this.estimates || {}).find(
|
||||
(p) => p.project === projectId && p.last_used
|
||||
);
|
||||
return currentActiveEstimateId?.id ?? undefined;
|
||||
}
|
||||
|
||||
// computed
|
||||
/**
|
||||
* @description get current active estimate for a project
|
||||
* @returns { string | undefined }
|
||||
*/
|
||||
get currentActiveEstimate(): IEstimate | undefined {
|
||||
const { projectId } = this.store.router;
|
||||
if (!projectId) return undefined;
|
||||
const currentActiveEstimate = Object.values(this.estimates || {}).find(
|
||||
(p) => p.project === projectId && p.last_used
|
||||
);
|
||||
return currentActiveEstimate ?? undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description get all archived estimate ids for a project
|
||||
* @returns { string[] | undefined }
|
||||
*/
|
||||
get archivedEstimateIds(): string[] | undefined {
|
||||
const { projectId } = this.store.router;
|
||||
if (!projectId) return undefined;
|
||||
const archivedEstimates = orderBy(
|
||||
Object.values(this.estimates || {}).filter((p) => p.project === projectId && !p.last_used),
|
||||
["created_at"],
|
||||
"desc"
|
||||
);
|
||||
const archivedEstimateIds = archivedEstimates.map((p) => p.id) as string[];
|
||||
return archivedEstimateIds ?? undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description get estimates are enabled in the project or not
|
||||
* @returns { boolean }
|
||||
*/
|
||||
areEstimateEnabledByProjectId = computedFn((projectId: string) => {
|
||||
if (!projectId) return false;
|
||||
const projectDetails = this.store.projectRoot.project.getProjectById(projectId);
|
||||
if (!projectDetails) return false;
|
||||
return Boolean(projectDetails.estimate) || false;
|
||||
});
|
||||
|
||||
/**
|
||||
* @description get all estimate ids for a project
|
||||
* @returns { string[] | undefined }
|
||||
*/
|
||||
estimateIdsByProjectId = computedFn((projectId: string) => {
|
||||
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 current active estimate id for a project
|
||||
* @returns { string | undefined }
|
||||
*/
|
||||
currentActiveEstimateIdByProjectId = computedFn((projectId: string): string | undefined => {
|
||||
if (!projectId) return undefined;
|
||||
const currentActiveEstimateId = Object.values(this.estimates || {}).find(
|
||||
(p) => p.project === projectId && p.last_used
|
||||
);
|
||||
return currentActiveEstimateId?.id ?? undefined;
|
||||
});
|
||||
|
||||
/**
|
||||
* @description get estimate by id
|
||||
* @returns { IEstimate | undefined }
|
||||
*/
|
||||
estimateById = computedFn((estimateId: string) => {
|
||||
if (!estimateId) return undefined;
|
||||
return this.estimates[estimateId] ?? undefined;
|
||||
});
|
||||
|
||||
// actions
|
||||
/**
|
||||
* @description fetch all estimates for a workspace
|
||||
* @param { string } workspaceSlug
|
||||
* @returns { IEstimateType[] | undefined }
|
||||
*/
|
||||
getWorkspaceEstimates = async (
|
||||
workspaceSlug: string,
|
||||
loader: TEstimateLoader = "mutation-loader"
|
||||
): Promise<IEstimateType[] | undefined> => {
|
||||
try {
|
||||
this.error = undefined;
|
||||
if (Object.keys(this.estimates || {}).length <= 0) this.loader = loader ? loader : "init-loader";
|
||||
|
||||
const estimates = await estimateService.fetchWorkspaceEstimates(workspaceSlug);
|
||||
if (estimates && estimates.length > 0) {
|
||||
runInAction(() => {
|
||||
estimates.forEach((estimate) => {
|
||||
if (estimate.id)
|
||||
set(
|
||||
this.estimates,
|
||||
[estimate.id],
|
||||
new Estimate(this.store, { ...estimate, type: estimate.type?.toLowerCase() as TEstimateSystemKeys })
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return estimates;
|
||||
} catch (error) {
|
||||
this.loader = undefined;
|
||||
this.error = {
|
||||
status: "error",
|
||||
message: "Error fetching estimates",
|
||||
};
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description fetch all estimates for a project
|
||||
* @param { string } workspaceSlug
|
||||
* @param { string } projectId
|
||||
* @returns { IEstimateType[] | undefined }
|
||||
*/
|
||||
getProjectEstimates = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
loader: TEstimateLoader = "mutation-loader"
|
||||
): Promise<IEstimateType[] | undefined> => {
|
||||
try {
|
||||
this.error = undefined;
|
||||
if (!this.estimateIdsByProjectId(projectId)) this.loader = loader ? loader : "init-loader";
|
||||
|
||||
const estimates = await estimateService.fetchProjectEstimates(workspaceSlug, projectId);
|
||||
if (estimates && estimates.length > 0) {
|
||||
runInAction(() => {
|
||||
estimates.forEach((estimate) => {
|
||||
if (estimate.id)
|
||||
set(
|
||||
this.estimates,
|
||||
[estimate.id],
|
||||
new Estimate(this.store, { ...estimate, type: estimate.type?.toLowerCase() as TEstimateSystemKeys })
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return estimates;
|
||||
} catch (error) {
|
||||
this.loader = undefined;
|
||||
this.error = {
|
||||
status: "error",
|
||||
message: "Error fetching estimates",
|
||||
};
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param { string } estimateId
|
||||
* @returns IEstimateType | undefined
|
||||
*/
|
||||
getEstimateById = (estimateId: string): IEstimate | undefined => this.estimates[estimateId];
|
||||
|
||||
/**
|
||||
* @description create an estimate for a project
|
||||
* @param { string } workspaceSlug
|
||||
* @param { string } projectId
|
||||
* @param { Partial<IEstimateFormData> } payload
|
||||
* @returns
|
||||
*/
|
||||
createEstimate = async (
|
||||
workspaceSlug: string,
|
||||
projectId: string,
|
||||
payload: IEstimateFormData
|
||||
): Promise<IEstimateType | undefined> => {
|
||||
try {
|
||||
this.error = undefined;
|
||||
|
||||
const estimate = await estimateService.createEstimate(workspaceSlug, projectId, payload);
|
||||
if (estimate) {
|
||||
// update estimate_id in current project
|
||||
// await this.store.projectRoot.project.updateProject(workspaceSlug, projectId, {
|
||||
// estimate: estimate.id,
|
||||
// });
|
||||
runInAction(() => {
|
||||
if (estimate.id)
|
||||
set(
|
||||
this.estimates,
|
||||
[estimate.id],
|
||||
new Estimate(this.store, { ...estimate, type: estimate.type?.toLowerCase() as TEstimateSystemKeys })
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return estimate;
|
||||
} catch (error) {
|
||||
this.error = {
|
||||
status: "error",
|
||||
message: "Error creating estimate",
|
||||
};
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @description delete the estimate for a project
|
||||
* @param workspaceSlug
|
||||
* @param projectId
|
||||
* @param estimateId
|
||||
*/
|
||||
deleteEstimate = async (workspaceSlug: string, projectId: string, estimateId: string) => {
|
||||
try {
|
||||
await estimateService.deleteEstimate(workspaceSlug, projectId, estimateId);
|
||||
runInAction(() => estimateId && unset(this.estimates, [estimateId]));
|
||||
} catch (error) {
|
||||
this.error = {
|
||||
status: "error",
|
||||
message: "Error deleting estimate",
|
||||
};
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user