import { action, computed, observable, makeObservable, runInAction } from "mobx"; // services import { ProjectService } from "services/project"; import { ModuleService } from "services/module.service"; // types import { IModule, ILinkDetails } from "types"; import { RootStore } from "store/root.store"; export interface IModuleStore { // states loader: boolean; error: any | null; // observables moduleId: string | null; moduleMap: { [project_id: string]: { [module_id: string]: IModule; }; }; // actions getModuleById: (moduleId: string) => IModule | null; fetchModules: (workspaceSlug: string, projectId: string) => void; fetchModuleDetails: (workspaceSlug: string, projectId: string, moduleId: string) => Promise; createModule: (workspaceSlug: string, projectId: string, data: Partial) => Promise; updateModuleDetails: ( workspaceSlug: string, projectId: string, moduleId: string, data: Partial ) => Promise; deleteModule: (workspaceSlug: string, projectId: string, moduleId: string) => Promise; createModuleLink: ( workspaceSlug: string, projectId: string, moduleId: string, data: Partial ) => Promise; updateModuleLink: ( workspaceSlug: string, projectId: string, moduleId: string, linkId: string, data: Partial ) => Promise; deleteModuleLink: (workspaceSlug: string, projectId: string, moduleId: string, linkId: string) => Promise; addModuleToFavorites: (workspaceSlug: string, projectId: string, moduleId: string) => Promise; removeModuleFromFavorites: (workspaceSlug: string, projectId: string, moduleId: string) => Promise; // computed projectModules: string[] | null; } export class ModulesStore implements IModuleStore { // states loader: boolean = false; error: any | null = null; // observables moduleId: string | null = null; moduleMap: { [project_id: string]: { [module_id: string]: IModule; }; } = {}; // root store rootStore; // services projectService; moduleService; constructor(_rootStore: RootStore) { makeObservable(this, { // states loader: observable, error: observable.ref, // observables moduleId: observable.ref, moduleMap: observable.ref, // actions getModuleById: action, fetchModules: action, fetchModuleDetails: action, createModule: action, updateModuleDetails: action, deleteModule: action, createModuleLink: action, updateModuleLink: action, deleteModuleLink: action, addModuleToFavorites: action, removeModuleFromFavorites: action, // computed projectModules: computed, }); this.rootStore = _rootStore; // services this.projectService = new ProjectService(); this.moduleService = new ModuleService(); } // computed get projectModules() { if (!this.rootStore.project.projectId) return null; return Object.keys(this.moduleMap[this.rootStore.project.projectId]) || null; } getModuleById = (moduleId: string) => this.moduleMap[this.rootStore.project.projectId][moduleId] || null; // actions fetchModules = async (workspaceSlug: string, projectId: string) => { try { runInAction(() => { this.loader = true; this.error = null; }); const modulesResponse = await this.moduleService.getModules(workspaceSlug, projectId); runInAction(() => { this.moduleMap = { ...this.moduleMap, [projectId]: modulesResponse, }; this.loader = false; this.error = null; }); } catch (error) { console.error("Failed to fetch modules list in module store", error); runInAction(() => { this.loader = false; this.error = error; }); } }; fetchModuleDetails = async (workspaceSlug: string, projectId: string, moduleId: string) => { try { runInAction(() => { this.loader = true; this.error = null; }); const response = await this.moduleService.getModuleDetails(workspaceSlug, projectId, moduleId); runInAction(() => { this.moduleMap = { ...this.moduleMap, [projectId]: { ...this.moduleMap[projectId], [moduleId]: response, }, }; this.loader = false; this.error = null; }); return response; } catch (error) { console.error("Failed to fetch module details in module store", error); runInAction(() => { this.loader = false; this.error = error; }); throw error; } }; createModule = async (workspaceSlug: string, projectId: string, data: Partial) => { try { const response = await this.moduleService.createModule(workspaceSlug, projectId, data); runInAction(() => { this.moduleMap = { ...this.moduleMap, [projectId]: { ...this.moduleMap[projectId], [response.id]: response, }, }; this.loader = false; this.error = null; }); this.fetchModules(workspaceSlug, projectId); return response; } catch (error) { console.error("Failed to create module in module store", error); runInAction(() => { this.loader = false; this.error = error; }); throw error; } }; updateModuleDetails = async (workspaceSlug: string, projectId: string, moduleId: string, data: Partial) => { try { const currentModule = this.moduleMap[projectId][moduleId]; runInAction(() => { this.moduleMap = { ...this.moduleMap, [projectId]: { ...this.moduleMap[projectId], [moduleId]: { ...currentModule, ...data }, }, }; }); const response = await this.moduleService.patchModule(workspaceSlug, projectId, moduleId, data); return response; } catch (error) { console.error("Failed to update module in module store", error); this.fetchModules(workspaceSlug, projectId); this.fetchModuleDetails(workspaceSlug, projectId, moduleId); runInAction(() => { this.error = error; }); throw error; } }; deleteModule = async (workspaceSlug: string, projectId: string, moduleId: string) => { try { const currentProjectModules = this.moduleMap[projectId]; delete currentProjectModules[moduleId]; runInAction(() => { this.moduleMap = { ...this.moduleMap, [projectId]: currentProjectModules, }; }); await this.moduleService.deleteModule(workspaceSlug, projectId, moduleId); } catch (error) { console.error("Failed to delete module in module store", error); this.fetchModules(workspaceSlug, projectId); runInAction(() => { this.error = error; }); } }; createModuleLink = async ( workspaceSlug: string, projectId: string, moduleId: string, data: Partial ) => { try { const response = await this.moduleService.createModuleLink(workspaceSlug, projectId, moduleId, data); const currentModule = this.moduleMap[projectId][moduleId]; runInAction(() => { this.moduleMap = { ...this.moduleMap, [projectId]: { ...this.moduleMap[projectId], [moduleId]: { ...currentModule, link_module: [response, ...currentModule.link_module] }, }, }; }); return response; } catch (error) { console.error("Failed to create module link in module store", error); this.fetchModules(workspaceSlug, projectId); this.fetchModuleDetails(workspaceSlug, projectId, moduleId); runInAction(() => { this.error = error; }); throw error; } }; updateModuleLink = async ( workspaceSlug: string, projectId: string, moduleId: string, linkId: string, data: Partial ) => { try { const response = await this.moduleService.updateModuleLink(workspaceSlug, projectId, moduleId, linkId, data); const currentModule = this.moduleMap[projectId][moduleId]; const linkModules = currentModule.link_module.map((link) => (link.id === linkId ? response : link)); runInAction(() => { this.moduleMap = { ...this.moduleMap, [projectId]: { ...this.moduleMap[projectId], [moduleId]: { ...currentModule, link_module: linkModules }, }, }; }); return response; } catch (error) { console.error("Failed to update module link in module store", error); this.fetchModules(workspaceSlug, projectId); this.fetchModuleDetails(workspaceSlug, projectId, moduleId); runInAction(() => { this.error = error; }); throw error; } }; deleteModuleLink = async (workspaceSlug: string, projectId: string, moduleId: string, linkId: string) => { try { const currentModule = this.moduleMap[projectId][moduleId]; const linkModules = currentModule.link_module.filter((link) => link.id !== linkId); runInAction(() => { this.moduleMap = { ...this.moduleMap, [projectId]: { ...this.moduleMap[projectId], [moduleId]: { ...currentModule, link_module: linkModules }, }, }; }); await this.moduleService.deleteModuleLink(workspaceSlug, projectId, moduleId, linkId); } catch (error) { console.error("Failed to delete module link in module store", error); this.fetchModules(workspaceSlug, projectId); this.fetchModuleDetails(workspaceSlug, projectId, moduleId); runInAction(() => { this.error = error; }); throw error; } }; addModuleToFavorites = async (workspaceSlug: string, projectId: string, moduleId: string) => { try { const currentModule = this.moduleMap[projectId][moduleId]; if (currentModule.is_favorite) return; runInAction(() => { this.moduleMap = { ...this.moduleMap, [projectId]: { ...this.moduleMap[projectId], [moduleId]: { ...currentModule, is_favorite: true }, }, }; }); await this.moduleService.addModuleToFavorites(workspaceSlug, projectId, { module: moduleId, }); } catch (error) { console.error("Failed to add module to favorites in module store", error); const currentModule = this.moduleMap[projectId][moduleId]; runInAction(() => { this.moduleMap = { ...this.moduleMap, [projectId]: { ...this.moduleMap[projectId], [moduleId]: { ...currentModule, is_favorite: false }, }, }; }); } }; removeModuleFromFavorites = async (workspaceSlug: string, projectId: string, moduleId: string) => { try { const currentModule = this.moduleMap[projectId][moduleId]; if (!currentModule.is_favorite) return; runInAction(() => { this.moduleMap = { ...this.moduleMap, [projectId]: { ...this.moduleMap[projectId], [moduleId]: { ...currentModule, is_favorite: false }, }, }; }); await this.moduleService.removeModuleFromFavorites(workspaceSlug, projectId, moduleId); } catch (error) { console.error("Failed to remove module from favorites in module store", error); const currentModule = this.moduleMap[projectId][moduleId]; runInAction(() => { this.moduleMap = { ...this.moduleMap, [projectId]: { ...this.moduleMap[projectId], [moduleId]: { ...currentModule, is_favorite: true }, }, }; }); } }; }