mirror of
https://github.com/makeplane/plane.git
synced 2026-07-12 13:29:56 +02:00
* chore: add template * chore: add page template and project template migrations * chore: add migration file for project and page templates * chore: page templates * chore: update template * feat: page templates * chore: update model * chore: asset copy * chore: update page templates * feat: add project template create/update endpoint * improvement: update all templates endpoint * chore: project template basic types and services * feat: project template instance and store * feat: project template store hooks * chore: feature flags and minor improvements * feat: project templates list settings * feat: project templates * chore: common constants across templates * improvement: work item template util types * improvement: moved all project related types to packages and re-exported them to avoid changes in files. * feat: create project template form with all basic fields and attributes * chore: add project creation bgtask from template * chore: update project template configuration * chore: update project template migrations * chore: added migration for workspace connection disconnect_meta * chore: update migration files * chore: model changes * chore: add project template validation * chore: update template task * chore: project templates endpoints * fix: base serializers * chore: add comment edit migrations * chore: minor improvements and base template store fixes * chore: minor improvements * improvement: common template related components * feat: project template features * chore: minor formatting * chore: minor translation updates * feat: update project template form data * chore: work item template util improvements * feat: use project templates in create project modal * chore: use template endpoint * chore: update imports * feat: project creation using templates * fix: build error * feat: work item type creation form * chore: convert project work item types form data to required schema * feat: update work item type form data extraction and sanitization * chore: add project template default settings * chore: update formatting * improvement: sync all project level features * revert: utils package build related changes * feat: project epics in templates * chore: update background task to handle member and intake creation * improvement: epic name while creating template * fix: api server * revert: utils build * feat: added work item labels while creating project templates * chore: update project template task * refactor: project state list components * improvement: additional classNames for the group list item and state item * fix: options * fix: epic creations * feat: add project states while creating project templates * improvements: add empty state for labels * chore: remove unused exports * fix: build error * chore: add intake setting schema * feat: intake feature settings for project templates * feat: intake settings and deploy board when creating project from template * fix: feature children layout * fix: form keydown events * fix: template description * fix: delete state button type * improvement: UI enhancements related to background, border and shadow * improvement: project templates dropdown * improvement: remove empty templates list sections * improvement: remove empty templates list sections * improvement: enhance project level template list modularity * improvement: minor component improvements * chore: minor type changes * improvement: template list loader * improvement: project template form background color * improvement: work item template form background color * chore: add asset url validation and resolve pr comments --------- Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com> Co-authored-by: Henit Chobisa <chobisa.henit@gmail.com>
217 lines
7.3 KiB
TypeScript
217 lines
7.3 KiB
TypeScript
/* eslint-disable no-useless-catch */
|
|
|
|
import set from "lodash/set";
|
|
import update from "lodash/update";
|
|
import { action, makeObservable, observable, reaction, runInAction } from "mobx";
|
|
// store
|
|
import { ProjectService } from "@/plane-web/services";
|
|
import { RootStore } from "@/plane-web/store/root.store";
|
|
import {
|
|
TProject,
|
|
TProjectAttributesParams,
|
|
TProjectAttributesResponse,
|
|
TProjectFeatures,
|
|
TProjectFeaturesList,
|
|
} from "@/plane-web/types";
|
|
import { IProjectAttachmentStore, ProjectAttachmentStore } from "./project-details/attachment.store";
|
|
import { IProjectLinkStore, ProjectLinkStore } from "./project-details/link.store";
|
|
import { IProjectReactionStore, ProjectReactionStore } from "./project-details/project_reaction.store";
|
|
import { IProjectUpdateStore, ProjectUpdateStore } from "./project-details/updates.store";
|
|
|
|
export interface IProjectStore {
|
|
reactionStore: IProjectReactionStore;
|
|
attachmentStore: IProjectAttachmentStore;
|
|
featuresLoader: boolean;
|
|
features: Record<string, TProjectFeatures>; // projectId -> project features
|
|
// computed methods
|
|
isProjectFeatureEnabled: (projectId: string, featureKey: keyof TProjectFeaturesList) => boolean;
|
|
// helpers
|
|
getProjectFeatures: (projectId: string) => TProjectFeatures | undefined;
|
|
// actions
|
|
toggleProjectFeatures: (
|
|
workspaceSlug: string,
|
|
projectId: string,
|
|
data: Partial<TProjectFeaturesList>,
|
|
shouldSync?: boolean
|
|
) => Promise<void>;
|
|
createProjectUsingTemplate: (workspaceSlug: string, templateId: string, data: Partial<TProject>) => Promise<TProject>;
|
|
fetchProjectFeatures: (workspaceSlug: string) => Promise<void>;
|
|
fetchProjectAttributes: (
|
|
workspaceSlug: string,
|
|
params?: TProjectAttributesParams
|
|
) => Promise<TProjectAttributesResponse[]>;
|
|
// store
|
|
linkStore: IProjectLinkStore;
|
|
updatesStore: IProjectUpdateStore;
|
|
}
|
|
|
|
export class ProjectStore implements IProjectStore {
|
|
features: Record<string, TProjectFeatures> = {};
|
|
featuresLoader: boolean = false;
|
|
//store
|
|
rootStore: RootStore;
|
|
linkStore: IProjectLinkStore;
|
|
attachmentStore: IProjectAttachmentStore;
|
|
updatesStore: IProjectUpdateStore;
|
|
reactionStore: IProjectReactionStore;
|
|
// services
|
|
projectService;
|
|
constructor(public store: RootStore) {
|
|
makeObservable(this, {
|
|
// observables
|
|
featuresLoader: observable.ref,
|
|
features: observable,
|
|
// actions
|
|
createProjectUsingTemplate: action,
|
|
fetchProjectFeatures: action,
|
|
fetchProjectAttributes: action,
|
|
});
|
|
this.rootStore = store;
|
|
this.linkStore = new ProjectLinkStore(this);
|
|
this.attachmentStore = new ProjectAttachmentStore(this);
|
|
this.updatesStore = new ProjectUpdateStore();
|
|
this.reactionStore = new ProjectReactionStore();
|
|
// services
|
|
this.projectService = new ProjectService();
|
|
|
|
// reaction to add project features when a new project is added to project map
|
|
reaction(
|
|
() => ({
|
|
projectIds: Object.keys(this.rootStore.projectRoot.project.projectMap),
|
|
}),
|
|
({ projectIds }) => {
|
|
for (const projectId of projectIds) {
|
|
if (!this.features[projectId]) {
|
|
this.features[projectId] = {
|
|
is_project_updates_enabled: false,
|
|
is_epic_enabled: false,
|
|
is_issue_type_enabled: false,
|
|
is_time_tracking_enabled: false,
|
|
is_workflow_enabled: false,
|
|
project_id: projectId,
|
|
};
|
|
}
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
// computed methods
|
|
/**
|
|
* @description check if a project feature is enabled
|
|
* @param { string } projectId
|
|
* @param { keyof TProjectFeatures } featureKey
|
|
* @returns { boolean }
|
|
*/
|
|
isProjectFeatureEnabled = (projectId: string, featureKey: keyof TProjectFeaturesList): boolean => {
|
|
const projectFeatures = this.features[projectId];
|
|
return projectFeatures?.[featureKey] ?? false;
|
|
};
|
|
|
|
// helpers
|
|
/**
|
|
* Get project feature by project id
|
|
* @param projectId
|
|
* @returns project feature
|
|
*/
|
|
getProjectFeatures = (projectId: string): TProjectFeatures | undefined => this.features[projectId];
|
|
|
|
// actions
|
|
/**
|
|
* Creates a project in the workspace using a template
|
|
* @param workspaceSlug
|
|
* @param templateId
|
|
* @param data
|
|
* @returns Promise<TProject>
|
|
*/
|
|
createProjectUsingTemplate = async (workspaceSlug: string, templateId: string, data: Partial<TProject>) => {
|
|
try {
|
|
const response = await this.projectService.createProjectUsingTemplate(workspaceSlug, templateId, data);
|
|
this.rootStore.projectRoot.project.processProjectAfterCreation(workspaceSlug, response);
|
|
|
|
// Get template detail to set project features
|
|
const template = this.rootStore.templatesRoot.projectTemplates.getTemplateById(templateId);
|
|
if (template && response.id) {
|
|
set(this.features, response.id, {
|
|
is_project_updates_enabled: template.template_data.is_project_updates_enabled,
|
|
is_epic_enabled: template.template_data.is_epic_enabled,
|
|
is_issue_type_enabled: template.template_data.is_issue_type_enabled,
|
|
is_time_tracking_enabled: template.template_data.is_time_tracking_enabled,
|
|
is_workflow_enabled: template.template_data.is_workflow_enabled,
|
|
project_id: response.id,
|
|
});
|
|
}
|
|
// fetch project work item types and epics
|
|
await this.rootStore.issueTypes.fetchAll(workspaceSlug, response.id);
|
|
|
|
return response;
|
|
} catch (error) {
|
|
console.error("Error creating project using template", error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
fetchProjectFeatures = async (workspaceSlug: string): Promise<void> => {
|
|
try {
|
|
this.featuresLoader = true;
|
|
const projectFeatures = await this.projectService.getProjectFeatures(workspaceSlug);
|
|
runInAction(() => {
|
|
for (const feature of projectFeatures) {
|
|
this.features[feature.project_id] = feature;
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error("Error fetching project features", error);
|
|
throw error;
|
|
} finally {
|
|
runInAction(() => {
|
|
this.featuresLoader = false;
|
|
});
|
|
}
|
|
};
|
|
|
|
toggleProjectFeatures = async (
|
|
workspaceSlug: string,
|
|
projectId: string,
|
|
data: Partial<TProjectFeaturesList>,
|
|
shouldSync: boolean = true
|
|
): Promise<void> => {
|
|
const initialState = this.features[projectId];
|
|
try {
|
|
this.features[projectId] = {
|
|
...this.features[projectId],
|
|
...data,
|
|
};
|
|
if (shouldSync) {
|
|
await this.projectService.toggleProjectFeatures(workspaceSlug, projectId, data);
|
|
}
|
|
} catch (error) {
|
|
console.error(error);
|
|
this.features[projectId] = initialState;
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Fetches project attributes.
|
|
* @param workspaceSlug
|
|
* @param params optional params to filter attributes to filter projects
|
|
* @returns array of project attributes
|
|
*/
|
|
fetchProjectAttributes = async (workspaceSlug: string, params?: TProjectAttributesParams) => {
|
|
try {
|
|
const response = await this.projectService.getProjectAttributes(workspaceSlug, params);
|
|
runInAction(() => {
|
|
for (const attribute of response) {
|
|
const { project_id, ...rest } = attribute;
|
|
update(this.store.projectRoot.project.projectMap, [project_id], (p) => ({ ...p, ...rest }));
|
|
}
|
|
});
|
|
return response;
|
|
} catch (error) {
|
|
console.log("Error while fetching project attributes", error);
|
|
throw error;
|
|
}
|
|
};
|
|
}
|