fix: project loaders for mobx store (#3356)

* add loaders to all the dropdowns outside project wrpper

* fix build errors

* minor refactor for project states color

---------

Co-authored-by: Rahul R <rahulr@Rahuls-MacBook-Pro.local>
This commit is contained in:
rahulramesha 2024-01-12 13:51:00 +05:30 committed by GitHub
parent f58a00a4ab
commit d64ae9a2e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 112 additions and 53 deletions

View File

@ -464,7 +464,7 @@ export const ActiveCycleDetails: React.FC<IActiveCycleDetails> = observer((props
{ {
issueIds?.filter( issueIds?.filter(
(issueId) => (issueId) =>
getProjectStates(issueMap[issueId]?.project_id).find( getProjectStates(issueMap[issueId]?.project_id)?.find(
(issue) => issue.id === issueMap[issueId]?.state_id (issue) => issue.id === issueMap[issueId]?.state_id
)?.group === "completed" )?.group === "completed"
)?.length )?.length

View File

@ -129,34 +129,37 @@ export const SelectDuplicateInboxIssueModal: React.FC<Props> = (props) => {
<h2 className="mb-2 mt-4 px-3 text-xs font-semibold text-custom-text-100">Select issue</h2> <h2 className="mb-2 mt-4 px-3 text-xs font-semibold text-custom-text-100">Select issue</h2>
)} )}
<ul className="text-sm text-custom-text-100"> <ul className="text-sm text-custom-text-100">
{filteredIssues.map((issue) => ( {filteredIssues.map((issue) => {
<Combobox.Option const stateColor =
key={issue.id} getProjectStates(issue?.project_id)?.find((state) => state?.id == issue?.state_id)
as="div" ?.color || "";
value={issue.id}
className={({ active, selected }) => return (
`flex w-full cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 text-custom-text-200 ${ <Combobox.Option
active || selected ? "bg-custom-background-80 text-custom-text-100" : "" key={issue.id}
} ` as="div"
} value={issue.id}
> className={({ active, selected }) =>
<div className="flex items-center gap-2"> `flex w-full cursor-pointer select-none items-center gap-2 rounded-md px-3 py-2 text-custom-text-200 ${
<span active || selected ? "bg-custom-background-80 text-custom-text-100" : ""
className="block h-1.5 w-1.5 flex-shrink-0 rounded-full" } `
style={{ }
backgroundColor: >
getProjectStates(issue?.project_id)?.find( <div className="flex items-center gap-2">
(state) => state?.id == issue?.state_id <span
)?.color || "", className="block h-1.5 w-1.5 flex-shrink-0 rounded-full"
}} style={{
/> backgroundColor: stateColor,
<span className="flex-shrink-0 text-xs text-custom-text-200"> }}
{getProjectById(issue?.project_id)?.identifier}-{issue.sequence_id} />
</span> <span className="flex-shrink-0 text-xs text-custom-text-200">
<span className="text-custom-text-200">{issue.name}</span> {getProjectById(issue?.project_id)?.identifier}-{issue.sequence_id}
</div> </span>
</Combobox.Option> <span className="text-custom-text-200">{issue.name}</span>
))} </div>
</Combobox.Option>
);
})}
</ul> </ul>
</li> </li>
) : ( ) : (

View File

@ -59,6 +59,10 @@ export const CalendarIssueBlocks: React.FC<Props> = observer((props) => {
if (!issues?.[issueId]) return null; if (!issues?.[issueId]) return null;
const issue = issues?.[issueId]; const issue = issues?.[issueId];
const stateColor =
getProjectStates(issue?.project_id)?.find((state) => state?.id == issue?.state_id)?.color || "";
return ( return (
<Draggable key={issue.id} draggableId={issue.id} index={index}> <Draggable key={issue.id} draggableId={issue.id} index={index}>
{(provided, snapshot) => ( {(provided, snapshot) => (
@ -90,9 +94,7 @@ export const CalendarIssueBlocks: React.FC<Props> = observer((props) => {
<span <span
className="h-full w-0.5 flex-shrink-0 rounded" className="h-full w-0.5 flex-shrink-0 rounded"
style={{ style={{
backgroundColor: getProjectStates(issue?.project_id).find( backgroundColor: stateColor,
(state) => state?.id == issue?.state_id
)?.color,
}} }}
/> />
<div className="flex-shrink-0 text-xs text-custom-text-300"> <div className="flex-shrink-0 text-xs text-custom-text-300">

View File

@ -22,11 +22,13 @@ export const IssueGanttBlock = ({ data }: { data: TIssue }) => {
data.id && data.id &&
setPeekIssue({ workspaceSlug, projectId: data.project_id, issueId: data.id }); setPeekIssue({ workspaceSlug, projectId: data.project_id, issueId: data.id });
const stateColor = getProjectStates(data?.project_id)?.find((state) => state?.id == data?.state_id)?.color || "";
return ( return (
<div <div
className="relative flex h-full w-full cursor-pointer items-center rounded" className="relative flex h-full w-full cursor-pointer items-center rounded"
style={{ style={{
backgroundColor: getProjectStates(data?.project_id)?.find((state) => state?.id == data?.state_id)?.color, backgroundColor: stateColor,
}} }}
onClick={handleIssuePeekOverview} onClick={handleIssuePeekOverview}
> >

View File

@ -30,7 +30,7 @@ export const IssueLabelSelect: React.FC<Props> = observer((props) => {
const { workspaceSlug } = router.query; const { workspaceSlug } = router.query;
// store hooks // store hooks
const { const {
project: { projectLabels, fetchProjectLabels }, project: { getProjectLabels, fetchProjectLabels },
} = useLabel(); } = useLabel();
// states // states
const [query, setQuery] = useState(""); const [query, setQuery] = useState("");
@ -43,6 +43,9 @@ export const IssueLabelSelect: React.FC<Props> = observer((props) => {
const { styles, attributes } = usePopper(referenceElement, popperElement, { const { styles, attributes } = usePopper(referenceElement, popperElement, {
placement: "bottom-start", placement: "bottom-start",
}); });
const projectLabels = getProjectLabels(projectId);
// derived values // derived values
const filteredOptions = const filteredOptions =
query === "" ? projectLabels : projectLabels?.filter((l) => l.name.toLowerCase().includes(query.toLowerCase())); query === "" ? projectLabels : projectLabels?.filter((l) => l.name.toLowerCase().includes(query.toLowerCase()));

View File

@ -12,6 +12,8 @@ import { IssueService } from "services/issue";
import { CycleService } from "services/cycle.service"; import { CycleService } from "services/cycle.service";
export interface ICycleStore { export interface ICycleStore {
//Loaders
fetchedMap: Record<string, boolean>;
// observables // observables
cycleMap: Record<string, ICycle>; cycleMap: Record<string, ICycle>;
activeCycleIdMap: Record<string, boolean>; activeCycleIdMap: Record<string, boolean>;
@ -50,6 +52,8 @@ export class CycleStore implements ICycleStore {
// observables // observables
cycleMap: Record<string, ICycle> = {}; cycleMap: Record<string, ICycle> = {};
activeCycleIdMap: Record<string, boolean> = {}; activeCycleIdMap: Record<string, boolean> = {};
//loaders
fetchedMap: Record<string, boolean> = {};
// root store // root store
rootStore; rootStore;
// services // services
@ -62,6 +66,7 @@ export class CycleStore implements ICycleStore {
// observables // observables
cycleMap: observable, cycleMap: observable,
activeCycleIdMap: observable, activeCycleIdMap: observable,
fetchedMap: observable,
// computed // computed
currentProjectCycleIds: computed, currentProjectCycleIds: computed,
currentProjectCompletedCycleIds: computed, currentProjectCompletedCycleIds: computed,
@ -96,11 +101,11 @@ export class CycleStore implements ICycleStore {
*/ */
get currentProjectCycleIds() { get currentProjectCycleIds() {
const projectId = this.rootStore.app.router.projectId; const projectId = this.rootStore.app.router.projectId;
if (!projectId) return null; if (!projectId || !this.fetchedMap[projectId]) return null;
let allCycles = Object.values(this.cycleMap ?? {}).filter((c) => c?.project === projectId); let allCycles = Object.values(this.cycleMap ?? {}).filter((c) => c?.project === projectId);
allCycles = sortBy(allCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); allCycles = sortBy(allCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]);
const allCycleIds = allCycles.map((c) => c.id); const allCycleIds = allCycles.map((c) => c.id);
return allCycleIds || null; return allCycleIds;
} }
/** /**
@ -108,14 +113,14 @@ export class CycleStore implements ICycleStore {
*/ */
get currentProjectCompletedCycleIds() { get currentProjectCompletedCycleIds() {
const projectId = this.rootStore.app.router.projectId; const projectId = this.rootStore.app.router.projectId;
if (!projectId) return null; if (!projectId || !this.fetchedMap[projectId]) return null;
let completedCycles = Object.values(this.cycleMap ?? {}).filter((c) => { let completedCycles = Object.values(this.cycleMap ?? {}).filter((c) => {
const hasEndDatePassed = isPast(new Date(c.end_date ?? "")); const hasEndDatePassed = isPast(new Date(c.end_date ?? ""));
return c.project === projectId && hasEndDatePassed; return c.project === projectId && hasEndDatePassed;
}); });
completedCycles = sortBy(completedCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); completedCycles = sortBy(completedCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]);
const completedCycleIds = completedCycles.map((c) => c.id); const completedCycleIds = completedCycles.map((c) => c.id);
return completedCycleIds || null; return completedCycleIds;
} }
/** /**
@ -123,14 +128,14 @@ export class CycleStore implements ICycleStore {
*/ */
get currentProjectUpcomingCycleIds() { get currentProjectUpcomingCycleIds() {
const projectId = this.rootStore.app.router.projectId; const projectId = this.rootStore.app.router.projectId;
if (!projectId) return null; if (!projectId || !this.fetchedMap[projectId]) return null;
let upcomingCycles = Object.values(this.cycleMap ?? {}).filter((c) => { let upcomingCycles = Object.values(this.cycleMap ?? {}).filter((c) => {
const isStartDateUpcoming = isFuture(new Date(c.start_date ?? "")); const isStartDateUpcoming = isFuture(new Date(c.start_date ?? ""));
return c.project === projectId && isStartDateUpcoming; return c.project === projectId && isStartDateUpcoming;
}); });
upcomingCycles = sortBy(upcomingCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); upcomingCycles = sortBy(upcomingCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]);
const upcomingCycleIds = upcomingCycles.map((c) => c.id); const upcomingCycleIds = upcomingCycles.map((c) => c.id);
return upcomingCycleIds || null; return upcomingCycleIds;
} }
/** /**
@ -138,14 +143,14 @@ export class CycleStore implements ICycleStore {
*/ */
get currentProjectIncompleteCycleIds() { get currentProjectIncompleteCycleIds() {
const projectId = this.rootStore.app.router.projectId; const projectId = this.rootStore.app.router.projectId;
if (!projectId) return null; if (!projectId || !this.fetchedMap[projectId]) return null;
let incompleteCycles = Object.values(this.cycleMap ?? {}).filter((c) => { let incompleteCycles = Object.values(this.cycleMap ?? {}).filter((c) => {
const hasEndDatePassed = isPast(new Date(c.end_date ?? "")); const hasEndDatePassed = isPast(new Date(c.end_date ?? ""));
return c.project === projectId && !hasEndDatePassed; return c.project === projectId && !hasEndDatePassed;
}); });
incompleteCycles = sortBy(incompleteCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); incompleteCycles = sortBy(incompleteCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]);
const incompleteCycleIds = incompleteCycles.map((c) => c.id); const incompleteCycleIds = incompleteCycles.map((c) => c.id);
return incompleteCycleIds || null; return incompleteCycleIds;
} }
/** /**
@ -153,13 +158,13 @@ export class CycleStore implements ICycleStore {
*/ */
get currentProjectDraftCycleIds() { get currentProjectDraftCycleIds() {
const projectId = this.rootStore.app.router.projectId; const projectId = this.rootStore.app.router.projectId;
if (!projectId) return null; if (!projectId || !this.fetchedMap[projectId]) return null;
let draftCycles = Object.values(this.cycleMap ?? {}).filter( let draftCycles = Object.values(this.cycleMap ?? {}).filter(
(c) => c.project === projectId && !c.start_date && !c.end_date (c) => c.project === projectId && !c.start_date && !c.end_date
); );
draftCycles = sortBy(draftCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); draftCycles = sortBy(draftCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]);
const draftCycleIds = draftCycles.map((c) => c.id); const draftCycleIds = draftCycles.map((c) => c.id);
return draftCycleIds || null; return draftCycleIds;
} }
/** /**
@ -194,6 +199,8 @@ export class CycleStore implements ICycleStore {
* @param projectId * @param projectId
*/ */
getProjectCycleIds = (projectId: string): string[] | null => { getProjectCycleIds = (projectId: string): string[] | null => {
if (!this.fetchedMap[projectId]) return null;
let cycles = Object.values(this.cycleMap ?? {}).filter((c) => c.project === projectId); let cycles = Object.values(this.cycleMap ?? {}).filter((c) => c.project === projectId);
cycles = sortBy(cycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); cycles = sortBy(cycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]);
const cycleIds = cycles.map((c) => c.id); const cycleIds = cycles.map((c) => c.id);
@ -222,6 +229,7 @@ export class CycleStore implements ICycleStore {
response.forEach((cycle) => { response.forEach((cycle) => {
set(this.cycleMap, [cycle.id], cycle); set(this.cycleMap, [cycle.id], cycle);
}); });
set(this.fetchedMap, projectId, true);
}); });
return response; return response;
}); });

View File

@ -1,4 +1,4 @@
import { action, computed, makeObservable, runInAction } from "mobx"; import { action, computed, makeObservable, observable, runInAction } from "mobx";
import set from "lodash/set"; import set from "lodash/set";
// services // services
import { IssueLabelService } from "services/issue"; import { IssueLabelService } from "services/issue";
@ -10,9 +10,13 @@ import { IIssueLabel, IIssueLabelTree } from "@plane/types";
import { ILabelRootStore } from "store/label"; import { ILabelRootStore } from "store/label";
export interface IProjectLabelStore { export interface IProjectLabelStore {
//Loaders
fetchedMap: Record<string, boolean>;
// computed // computed
projectLabels: IIssueLabel[] | undefined; projectLabels: IIssueLabel[] | undefined;
projectLabelsTree: IIssueLabelTree[] | undefined; projectLabelsTree: IIssueLabelTree[] | undefined;
//computed actions
getProjectLabels: (projectId: string) => IIssueLabel[] | undefined;
// fetch actions // fetch actions
fetchProjectLabels: (workspaceSlug: string, projectId: string) => Promise<IIssueLabel[]>; fetchProjectLabels: (workspaceSlug: string, projectId: string) => Promise<IIssueLabel[]>;
// crud actions // crud actions
@ -40,15 +44,21 @@ export class ProjectLabelStore implements IProjectLabelStore {
rootStore; rootStore;
// root store labelMap // root store labelMap
labelMap: Record<string, IIssueLabel> = {}; labelMap: Record<string, IIssueLabel> = {};
//loaders
fetchedMap: Record<string, boolean> = {};
// services // services
issueLabelService; issueLabelService;
constructor(_labelRoot: ILabelRootStore, _rootStore: RootStore) { constructor(_labelRoot: ILabelRootStore, _rootStore: RootStore) {
makeObservable(this, { makeObservable(this, {
labelMap: observable,
fetchedMap: observable,
// computed // computed
projectLabels: computed, projectLabels: computed,
projectLabelsTree: computed, projectLabelsTree: computed,
// actions // actions
getProjectLabels: action,
fetchProjectLabels: action, fetchProjectLabels: action,
createLabel: action, createLabel: action,
updateLabel: action, updateLabel: action,
@ -68,7 +78,7 @@ export class ProjectLabelStore implements IProjectLabelStore {
*/ */
get projectLabels() { get projectLabels() {
const projectId = this.rootStore.app.router.projectId; const projectId = this.rootStore.app.router.projectId;
if (!projectId || !this.labelMap) return; if (!projectId || !this.fetchedMap[projectId] || !this.labelMap) return;
return Object.values(this.labelMap ?? {}).filter((label) => label.project === projectId); return Object.values(this.labelMap ?? {}).filter((label) => label.project === projectId);
} }
@ -80,6 +90,11 @@ export class ProjectLabelStore implements IProjectLabelStore {
return buildTree(this.projectLabels); return buildTree(this.projectLabels);
} }
getProjectLabels = (projectId: string) => {
if (!this.fetchedMap[projectId] || !this.labelMap) return;
return Object.values(this.labelMap ?? {}).filter((label) => label.project === projectId);
};
/** /**
* Fetches all the labelMap belongs to a specific project * Fetches all the labelMap belongs to a specific project
* @param workspaceSlug * @param workspaceSlug
@ -92,6 +107,7 @@ export class ProjectLabelStore implements IProjectLabelStore {
response.forEach((label) => { response.forEach((label) => {
set(this.labelMap, [label.id], label); set(this.labelMap, [label.id], label);
}); });
set(this.fetchedMap, projectId, true);
}); });
return response; return response;
}); });

View File

@ -120,7 +120,8 @@ export class ProjectMemberStore implements IProjectMemberStore {
* @param projectId * @param projectId
*/ */
getProjectMemberIds = (projectId: string): string[] | null => { getProjectMemberIds = (projectId: string): string[] | null => {
let members = Object.values(this.projectMemberMap?.[projectId] ?? {}); if (!this.projectMemberMap?.[projectId]) return null;
let members = Object.values(this.projectMemberMap?.[projectId]);
members = sortBy(members, [ members = sortBy(members, [
(m) => m.member !== this.userStore.currentUser?.id, (m) => m.member !== this.userStore.currentUser?.id,
(m) => this.memberRoot?.memberMap?.[m.member]?.display_name?.toLowerCase(), (m) => this.memberRoot?.memberMap?.[m.member]?.display_name?.toLowerCase(),

View File

@ -9,6 +9,8 @@ import { IModule, ILinkDetails } from "@plane/types";
import { RootStore } from "store/root.store"; import { RootStore } from "store/root.store";
export interface IModuleStore { export interface IModuleStore {
//Loaders
fetchedMap: Record<string, boolean>;
// observables // observables
moduleMap: Record<string, IModule>; moduleMap: Record<string, IModule>;
// computed // computed
@ -51,6 +53,8 @@ export interface IModuleStore {
export class ModulesStore implements IModuleStore { export class ModulesStore implements IModuleStore {
// observables // observables
moduleMap: Record<string, IModule> = {}; moduleMap: Record<string, IModule> = {};
//loaders
fetchedMap: Record<string, boolean> = {};
// root store // root store
rootStore; rootStore;
// services // services
@ -61,6 +65,7 @@ export class ModulesStore implements IModuleStore {
makeObservable(this, { makeObservable(this, {
// observables // observables
moduleMap: observable, moduleMap: observable,
fetchedMap: observable,
// computed // computed
projectModuleIds: computed, projectModuleIds: computed,
// computed actions // computed actions
@ -92,7 +97,7 @@ export class ModulesStore implements IModuleStore {
*/ */
get projectModuleIds() { get projectModuleIds() {
const projectId = this.rootStore.app.router.projectId; const projectId = this.rootStore.app.router.projectId;
if (!projectId) return null; if (!projectId || !this.fetchedMap[projectId]) return null;
let projectModules = Object.values(this.moduleMap).filter((m) => m.project === projectId); let projectModules = Object.values(this.moduleMap).filter((m) => m.project === projectId);
projectModules = sortBy(projectModules, [(m) => !m.is_favorite, (m) => m.name.toLowerCase()]); projectModules = sortBy(projectModules, [(m) => !m.is_favorite, (m) => m.name.toLowerCase()]);
const projectModuleIds = projectModules.map((m) => m.id); const projectModuleIds = projectModules.map((m) => m.id);
@ -111,10 +116,12 @@ export class ModulesStore implements IModuleStore {
* @param projectId * @param projectId
*/ */
getProjectModuleIds = (projectId: string) => { getProjectModuleIds = (projectId: string) => {
if (!this.fetchedMap[projectId]) return null;
let projectModules = Object.values(this.moduleMap).filter((m) => m.project === projectId); let projectModules = Object.values(this.moduleMap).filter((m) => m.project === projectId);
projectModules = sortBy(projectModules, [(m) => !m.is_favorite, (m) => m.name.toLowerCase()]); projectModules = sortBy(projectModules, [(m) => !m.is_favorite, (m) => m.name.toLowerCase()]);
const projectModuleIds = projectModules.map((m) => m.id); const projectModuleIds = projectModules.map((m) => m.id);
return projectModuleIds || null; return projectModuleIds;
}; };
/** /**
@ -129,6 +136,7 @@ export class ModulesStore implements IModuleStore {
response.forEach((module) => { response.forEach((module) => {
set(this.moduleMap, [module.id], { ...this.moduleMap[module.id], ...module }); set(this.moduleMap, [module.id], { ...this.moduleMap[module.id], ...module });
}); });
set(this.fetchedMap, projectId, true);
}); });
return response; return response;
}); });

View File

@ -7,6 +7,8 @@ import { RootStore } from "store/root.store";
import { IProjectView } from "@plane/types"; import { IProjectView } from "@plane/types";
export interface IProjectViewStore { export interface IProjectViewStore {
//Loaders
fetchedMap: Record<string, boolean>;
// observables // observables
viewMap: Record<string, IProjectView>; viewMap: Record<string, IProjectView>;
// computed // computed
@ -33,6 +35,8 @@ export interface IProjectViewStore {
export class ProjectViewStore implements IProjectViewStore { export class ProjectViewStore implements IProjectViewStore {
// observables // observables
viewMap: Record<string, IProjectView> = {}; viewMap: Record<string, IProjectView> = {};
//loaders
fetchedMap: Record<string, boolean> = {};
// root store // root store
rootStore; rootStore;
// services // services
@ -42,6 +46,7 @@ export class ProjectViewStore implements IProjectViewStore {
makeObservable(this, { makeObservable(this, {
// observables // observables
viewMap: observable, viewMap: observable,
fetchedMap: observable,
// computed // computed
projectViewIds: computed, projectViewIds: computed,
// computed actions // computed actions
@ -68,7 +73,7 @@ export class ProjectViewStore implements IProjectViewStore {
*/ */
get projectViewIds() { get projectViewIds() {
const projectId = this.rootStore.app.router.projectId; const projectId = this.rootStore.app.router.projectId;
if (!projectId) return null; if (!projectId || !this.fetchedMap[projectId]) return null;
const viewIds = Object.keys(this.viewMap ?? {})?.filter((viewId) => this.viewMap?.[viewId]?.project === projectId); const viewIds = Object.keys(this.viewMap ?? {})?.filter((viewId) => this.viewMap?.[viewId]?.project === projectId);
return viewIds; return viewIds;
} }
@ -90,6 +95,7 @@ export class ProjectViewStore implements IProjectViewStore {
response.forEach((view) => { response.forEach((view) => {
set(this.viewMap, [view.id], view); set(this.viewMap, [view.id], view);
}); });
set(this.fetchedMap, projectId, true);
}); });
return response; return response;
}); });

View File

@ -9,6 +9,8 @@ import { IState } from "@plane/types";
import { ProjectStateService } from "services/project"; import { ProjectStateService } from "services/project";
export interface IStateStore { export interface IStateStore {
//Loaders
fetchedMap: Record<string, boolean>;
// observables // observables
stateMap: Record<string, IState>; stateMap: Record<string, IState>;
// computed // computed
@ -16,7 +18,7 @@ export interface IStateStore {
groupedProjectStates: Record<string, IState[]> | undefined; groupedProjectStates: Record<string, IState[]> | undefined;
// computed actions // computed actions
getStateById: (stateId: string) => IState | undefined; getStateById: (stateId: string) => IState | undefined;
getProjectStates: (projectId: string) => IState[]; getProjectStates: (projectId: string) => IState[] | undefined;
// fetch actions // fetch actions
fetchProjectStates: (workspaceSlug: string, projectId: string) => Promise<IState[]>; fetchProjectStates: (workspaceSlug: string, projectId: string) => Promise<IState[]>;
// crud actions // crud actions
@ -40,6 +42,8 @@ export interface IStateStore {
export class StateStore implements IStateStore { export class StateStore implements IStateStore {
stateMap: Record<string, IState> = {}; stateMap: Record<string, IState> = {};
//loaders
fetchedMap: Record<string, boolean> = {};
router; router;
stateService; stateService;
@ -47,6 +51,7 @@ export class StateStore implements IStateStore {
makeObservable(this, { makeObservable(this, {
// observables // observables
stateMap: observable, stateMap: observable,
fetchedMap: observable,
// computed // computed
projectStates: computed, projectStates: computed,
groupedProjectStates: computed, groupedProjectStates: computed,
@ -71,7 +76,8 @@ export class StateStore implements IStateStore {
* Returns the stateMap belongs to a specific project * Returns the stateMap belongs to a specific project
*/ */
get projectStates() { get projectStates() {
if (!this.router.query?.projectId) return; const projectId = this.router.query?.projectId?.toString();
if (!projectId || !this.fetchedMap[projectId]) return;
return Object.values(this.stateMap).filter((state) => state.project === this.router.query.projectId); return Object.values(this.stateMap).filter((state) => state.project === this.router.query.projectId);
} }
@ -97,7 +103,10 @@ export class StateStore implements IStateStore {
* @param projectId * @param projectId
* @returns IState[] * @returns IState[]
*/ */
getProjectStates = (projectId: string) => Object.values(this.stateMap).filter((state) => state.project === projectId); getProjectStates = (projectId: string) => {
if (!projectId || !this.fetchedMap[projectId]) return;
return Object.values(this.stateMap).filter((state) => state.project === projectId);
};
/** /**
* fetches the stateMap of a project * fetches the stateMap of a project
@ -111,6 +120,7 @@ export class StateStore implements IStateStore {
statesResponse.forEach((state) => { statesResponse.forEach((state) => {
set(this.stateMap, [state.id], state); set(this.stateMap, [state.id], state);
}); });
set(this.fetchedMap, projectId, true);
}); });
return statesResponse; return statesResponse;
}; };