diff --git a/web/components/cycles/active-cycle-details.tsx b/web/components/cycles/active-cycle-details.tsx index ed946bd82..56c5e1bc9 100644 --- a/web/components/cycles/active-cycle-details.tsx +++ b/web/components/cycles/active-cycle-details.tsx @@ -464,7 +464,7 @@ export const ActiveCycleDetails: React.FC = observer((props { issueIds?.filter( (issueId) => - getProjectStates(issueMap[issueId]?.project_id).find( + getProjectStates(issueMap[issueId]?.project_id)?.find( (issue) => issue.id === issueMap[issueId]?.state_id )?.group === "completed" )?.length diff --git a/web/components/inbox/modals/select-duplicate.tsx b/web/components/inbox/modals/select-duplicate.tsx index 415882daa..e4acca626 100644 --- a/web/components/inbox/modals/select-duplicate.tsx +++ b/web/components/inbox/modals/select-duplicate.tsx @@ -129,34 +129,37 @@ export const SelectDuplicateInboxIssueModal: React.FC = (props) => {

Select issue

)} ) : ( diff --git a/web/components/issues/issue-layouts/calendar/issue-blocks.tsx b/web/components/issues/issue-layouts/calendar/issue-blocks.tsx index 34bcac188..5711c89f6 100644 --- a/web/components/issues/issue-layouts/calendar/issue-blocks.tsx +++ b/web/components/issues/issue-layouts/calendar/issue-blocks.tsx @@ -59,6 +59,10 @@ export const CalendarIssueBlocks: React.FC = observer((props) => { if (!issues?.[issueId]) return null; const issue = issues?.[issueId]; + + const stateColor = + getProjectStates(issue?.project_id)?.find((state) => state?.id == issue?.state_id)?.color || ""; + return ( {(provided, snapshot) => ( @@ -90,9 +94,7 @@ export const CalendarIssueBlocks: React.FC = observer((props) => { state?.id == issue?.state_id - )?.color, + backgroundColor: stateColor, }} />
diff --git a/web/components/issues/issue-layouts/gantt/blocks.tsx b/web/components/issues/issue-layouts/gantt/blocks.tsx index 5622cd672..fefee880d 100644 --- a/web/components/issues/issue-layouts/gantt/blocks.tsx +++ b/web/components/issues/issue-layouts/gantt/blocks.tsx @@ -22,11 +22,13 @@ export const IssueGanttBlock = ({ data }: { data: TIssue }) => { 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 (
state?.id == data?.state_id)?.color, + backgroundColor: stateColor, }} onClick={handleIssuePeekOverview} > diff --git a/web/components/issues/select/label.tsx b/web/components/issues/select/label.tsx index 485adfcaa..fab1c04fd 100644 --- a/web/components/issues/select/label.tsx +++ b/web/components/issues/select/label.tsx @@ -30,7 +30,7 @@ export const IssueLabelSelect: React.FC = observer((props) => { const { workspaceSlug } = router.query; // store hooks const { - project: { projectLabels, fetchProjectLabels }, + project: { getProjectLabels, fetchProjectLabels }, } = useLabel(); // states const [query, setQuery] = useState(""); @@ -43,6 +43,9 @@ export const IssueLabelSelect: React.FC = observer((props) => { const { styles, attributes } = usePopper(referenceElement, popperElement, { placement: "bottom-start", }); + + const projectLabels = getProjectLabels(projectId); + // derived values const filteredOptions = query === "" ? projectLabels : projectLabels?.filter((l) => l.name.toLowerCase().includes(query.toLowerCase())); diff --git a/web/store/cycle.store.ts b/web/store/cycle.store.ts index 5b663f79b..c8ab68f50 100644 --- a/web/store/cycle.store.ts +++ b/web/store/cycle.store.ts @@ -12,6 +12,8 @@ import { IssueService } from "services/issue"; import { CycleService } from "services/cycle.service"; export interface ICycleStore { + //Loaders + fetchedMap: Record; // observables cycleMap: Record; activeCycleIdMap: Record; @@ -50,6 +52,8 @@ export class CycleStore implements ICycleStore { // observables cycleMap: Record = {}; activeCycleIdMap: Record = {}; + //loaders + fetchedMap: Record = {}; // root store rootStore; // services @@ -62,6 +66,7 @@ export class CycleStore implements ICycleStore { // observables cycleMap: observable, activeCycleIdMap: observable, + fetchedMap: observable, // computed currentProjectCycleIds: computed, currentProjectCompletedCycleIds: computed, @@ -96,11 +101,11 @@ export class CycleStore implements ICycleStore { */ get currentProjectCycleIds() { 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); allCycles = sortBy(allCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); const allCycleIds = allCycles.map((c) => c.id); - return allCycleIds || null; + return allCycleIds; } /** @@ -108,14 +113,14 @@ export class CycleStore implements ICycleStore { */ get currentProjectCompletedCycleIds() { 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) => { const hasEndDatePassed = isPast(new Date(c.end_date ?? "")); return c.project === projectId && hasEndDatePassed; }); completedCycles = sortBy(completedCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); const completedCycleIds = completedCycles.map((c) => c.id); - return completedCycleIds || null; + return completedCycleIds; } /** @@ -123,14 +128,14 @@ export class CycleStore implements ICycleStore { */ get currentProjectUpcomingCycleIds() { 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) => { const isStartDateUpcoming = isFuture(new Date(c.start_date ?? "")); return c.project === projectId && isStartDateUpcoming; }); upcomingCycles = sortBy(upcomingCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); const upcomingCycleIds = upcomingCycles.map((c) => c.id); - return upcomingCycleIds || null; + return upcomingCycleIds; } /** @@ -138,14 +143,14 @@ export class CycleStore implements ICycleStore { */ get currentProjectIncompleteCycleIds() { 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) => { const hasEndDatePassed = isPast(new Date(c.end_date ?? "")); return c.project === projectId && !hasEndDatePassed; }); incompleteCycles = sortBy(incompleteCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); const incompleteCycleIds = incompleteCycles.map((c) => c.id); - return incompleteCycleIds || null; + return incompleteCycleIds; } /** @@ -153,13 +158,13 @@ export class CycleStore implements ICycleStore { */ get currentProjectDraftCycleIds() { 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( (c) => c.project === projectId && !c.start_date && !c.end_date ); draftCycles = sortBy(draftCycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); const draftCycleIds = draftCycles.map((c) => c.id); - return draftCycleIds || null; + return draftCycleIds; } /** @@ -194,6 +199,8 @@ export class CycleStore implements ICycleStore { * @param projectId */ getProjectCycleIds = (projectId: string): string[] | null => { + if (!this.fetchedMap[projectId]) return null; + let cycles = Object.values(this.cycleMap ?? {}).filter((c) => c.project === projectId); cycles = sortBy(cycles, [(c) => !c.is_favorite, (c) => c.name.toLowerCase()]); const cycleIds = cycles.map((c) => c.id); @@ -222,6 +229,7 @@ export class CycleStore implements ICycleStore { response.forEach((cycle) => { set(this.cycleMap, [cycle.id], cycle); }); + set(this.fetchedMap, projectId, true); }); return response; }); diff --git a/web/store/label/project-label.store.ts b/web/store/label/project-label.store.ts index d8782530d..fdbf342c3 100644 --- a/web/store/label/project-label.store.ts +++ b/web/store/label/project-label.store.ts @@ -1,4 +1,4 @@ -import { action, computed, makeObservable, runInAction } from "mobx"; +import { action, computed, makeObservable, observable, runInAction } from "mobx"; import set from "lodash/set"; // services import { IssueLabelService } from "services/issue"; @@ -10,9 +10,13 @@ import { IIssueLabel, IIssueLabelTree } from "@plane/types"; import { ILabelRootStore } from "store/label"; export interface IProjectLabelStore { + //Loaders + fetchedMap: Record; // computed projectLabels: IIssueLabel[] | undefined; projectLabelsTree: IIssueLabelTree[] | undefined; + //computed actions + getProjectLabels: (projectId: string) => IIssueLabel[] | undefined; // fetch actions fetchProjectLabels: (workspaceSlug: string, projectId: string) => Promise; // crud actions @@ -40,15 +44,21 @@ export class ProjectLabelStore implements IProjectLabelStore { rootStore; // root store labelMap labelMap: Record = {}; + //loaders + fetchedMap: Record = {}; // services issueLabelService; constructor(_labelRoot: ILabelRootStore, _rootStore: RootStore) { makeObservable(this, { + labelMap: observable, + fetchedMap: observable, // computed projectLabels: computed, projectLabelsTree: computed, // actions + getProjectLabels: action, + fetchProjectLabels: action, createLabel: action, updateLabel: action, @@ -68,7 +78,7 @@ export class ProjectLabelStore implements IProjectLabelStore { */ get projectLabels() { 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); } @@ -80,6 +90,11 @@ export class ProjectLabelStore implements IProjectLabelStore { 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 * @param workspaceSlug @@ -92,6 +107,7 @@ export class ProjectLabelStore implements IProjectLabelStore { response.forEach((label) => { set(this.labelMap, [label.id], label); }); + set(this.fetchedMap, projectId, true); }); return response; }); diff --git a/web/store/member/project-member.store.ts b/web/store/member/project-member.store.ts index 84718fdae..eaac78f7b 100644 --- a/web/store/member/project-member.store.ts +++ b/web/store/member/project-member.store.ts @@ -120,7 +120,8 @@ export class ProjectMemberStore implements IProjectMemberStore { * @param projectId */ 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, [ (m) => m.member !== this.userStore.currentUser?.id, (m) => this.memberRoot?.memberMap?.[m.member]?.display_name?.toLowerCase(), diff --git a/web/store/module.store.ts b/web/store/module.store.ts index bbe649353..f37d41eaa 100644 --- a/web/store/module.store.ts +++ b/web/store/module.store.ts @@ -9,6 +9,8 @@ import { IModule, ILinkDetails } from "@plane/types"; import { RootStore } from "store/root.store"; export interface IModuleStore { + //Loaders + fetchedMap: Record; // observables moduleMap: Record; // computed @@ -51,6 +53,8 @@ export interface IModuleStore { export class ModulesStore implements IModuleStore { // observables moduleMap: Record = {}; + //loaders + fetchedMap: Record = {}; // root store rootStore; // services @@ -61,6 +65,7 @@ export class ModulesStore implements IModuleStore { makeObservable(this, { // observables moduleMap: observable, + fetchedMap: observable, // computed projectModuleIds: computed, // computed actions @@ -92,7 +97,7 @@ export class ModulesStore implements IModuleStore { */ get projectModuleIds() { 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); projectModules = sortBy(projectModules, [(m) => !m.is_favorite, (m) => m.name.toLowerCase()]); const projectModuleIds = projectModules.map((m) => m.id); @@ -111,10 +116,12 @@ export class ModulesStore implements IModuleStore { * @param projectId */ getProjectModuleIds = (projectId: string) => { + if (!this.fetchedMap[projectId]) return null; + let projectModules = Object.values(this.moduleMap).filter((m) => m.project === projectId); projectModules = sortBy(projectModules, [(m) => !m.is_favorite, (m) => m.name.toLowerCase()]); const projectModuleIds = projectModules.map((m) => m.id); - return projectModuleIds || null; + return projectModuleIds; }; /** @@ -129,6 +136,7 @@ export class ModulesStore implements IModuleStore { response.forEach((module) => { set(this.moduleMap, [module.id], { ...this.moduleMap[module.id], ...module }); }); + set(this.fetchedMap, projectId, true); }); return response; }); diff --git a/web/store/project-view.store.ts b/web/store/project-view.store.ts index 674257321..6946271da 100644 --- a/web/store/project-view.store.ts +++ b/web/store/project-view.store.ts @@ -7,6 +7,8 @@ import { RootStore } from "store/root.store"; import { IProjectView } from "@plane/types"; export interface IProjectViewStore { + //Loaders + fetchedMap: Record; // observables viewMap: Record; // computed @@ -33,6 +35,8 @@ export interface IProjectViewStore { export class ProjectViewStore implements IProjectViewStore { // observables viewMap: Record = {}; + //loaders + fetchedMap: Record = {}; // root store rootStore; // services @@ -42,6 +46,7 @@ export class ProjectViewStore implements IProjectViewStore { makeObservable(this, { // observables viewMap: observable, + fetchedMap: observable, // computed projectViewIds: computed, // computed actions @@ -68,7 +73,7 @@ export class ProjectViewStore implements IProjectViewStore { */ get projectViewIds() { 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); return viewIds; } @@ -90,6 +95,7 @@ export class ProjectViewStore implements IProjectViewStore { response.forEach((view) => { set(this.viewMap, [view.id], view); }); + set(this.fetchedMap, projectId, true); }); return response; }); diff --git a/web/store/state.store.ts b/web/store/state.store.ts index fe9a62930..9671a3bf5 100644 --- a/web/store/state.store.ts +++ b/web/store/state.store.ts @@ -9,6 +9,8 @@ import { IState } from "@plane/types"; import { ProjectStateService } from "services/project"; export interface IStateStore { + //Loaders + fetchedMap: Record; // observables stateMap: Record; // computed @@ -16,7 +18,7 @@ export interface IStateStore { groupedProjectStates: Record | undefined; // computed actions getStateById: (stateId: string) => IState | undefined; - getProjectStates: (projectId: string) => IState[]; + getProjectStates: (projectId: string) => IState[] | undefined; // fetch actions fetchProjectStates: (workspaceSlug: string, projectId: string) => Promise; // crud actions @@ -40,6 +42,8 @@ export interface IStateStore { export class StateStore implements IStateStore { stateMap: Record = {}; + //loaders + fetchedMap: Record = {}; router; stateService; @@ -47,6 +51,7 @@ export class StateStore implements IStateStore { makeObservable(this, { // observables stateMap: observable, + fetchedMap: observable, // computed projectStates: computed, groupedProjectStates: computed, @@ -71,7 +76,8 @@ export class StateStore implements IStateStore { * Returns the stateMap belongs to a specific project */ 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); } @@ -97,7 +103,10 @@ export class StateStore implements IStateStore { * @param projectId * @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 @@ -111,6 +120,7 @@ export class StateStore implements IStateStore { statesResponse.forEach((state) => { set(this.stateMap, [state.id], state); }); + set(this.fetchedMap, projectId, true); }); return statesResponse; };