chore: implement the new label root store

This commit is contained in:
Aaryan Khandelwal 2023-12-15 14:58:40 +05:30
parent 2e74dfa12c
commit 960f170fd4
42 changed files with 768 additions and 392 deletions

View File

@ -1,9 +1,9 @@
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// cmdk
import { Command } from "cmdk";
// hooks
import { useMobxStore } from "lib/mobx/store-provider";
import { useProjectState } from "hooks/store";
// ui
import { Spinner, StateGroupIcon } from "@plane/ui";
// icons
@ -18,14 +18,14 @@ type Props = {
export const ChangeIssueState: React.FC<Props> = observer((props) => {
const { closePalette, issue } = props;
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// store hooks
const {
projectState: { projectStates },
projectIssues: { updateIssue },
} = useMobxStore();
const { projectStates } = useProjectState();
const submitChanges = async (formData: Partial<IIssue>) => {
if (!workspaceSlug || !projectId || !issue) return;

View File

@ -1,7 +1,8 @@
import { useRouter } from "next/router";
import { useEffect } from "react";
import { observer } from "mobx-react-lite";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useLabel } from "hooks/store";
// hook
import useEstimateOption from "hooks/use-estimate-option";
// icons
@ -27,7 +28,6 @@ import { renderShortDateWithYearFormat } from "helpers/date-time.helper";
import { capitalizeFirstLetter } from "helpers/string.helper";
// types
import { IIssueActivity } from "types";
import { useEffect } from "react";
const IssueLink = ({ activity }: { activity: IIssueActivity }) => {
const router = useRouter();
@ -74,11 +74,10 @@ const UserLink = ({ activity }: { activity: IIssueActivity }) => {
};
const LabelPill = observer(({ labelId, workspaceSlug }: { labelId: string; workspaceSlug: string }) => {
// store hooks
const {
workspace: { labels, fetchWorkspaceLabels },
} = useMobxStore();
const workspaceLabels = labels[workspaceSlug];
workspaceLabel: { workspaceLabels, fetchWorkspaceLabels },
} = useLabel();
useEffect(() => {
if (!workspaceLabels) fetchWorkspaceLabels(workspaceSlug);

View File

@ -1,9 +1,9 @@
import { useCallback, useState } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useMobxStore } from "lib/mobx/store-provider";
import { useApplication, useLabel, useProject, useProjectState, useUser } from "hooks/store";
import useLocalStorage from "hooks/use-local-storage";
// components
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
@ -25,27 +25,34 @@ import { EFilterType } from "store_legacy/issues/types";
import { EProjectStore } from "store_legacy/command-palette.store";
export const CycleIssuesHeader: React.FC = observer(() => {
// states
const [analyticsModal, setAnalyticsModal] = useState(false);
// router
const router = useRouter();
const { workspaceSlug, projectId, cycleId } = router.query as {
workspaceSlug: string;
projectId: string;
cycleId: string;
};
// store hooks
const {
cycle: cycleStore,
projectIssuesFilter: projectIssueFiltersStore,
project: { currentProjectDetails },
projectMember: { projectMembers },
projectLabel: { projectLabels },
projectState: projectStateStore,
commandPalette: commandPaletteStore,
trackEvent: { setTrackElement },
cycleIssuesFilter: { issueFilters, updateFilters },
user: { currentProjectRole },
} = useMobxStore();
const {
commandPalette: { toggleCreateIssueModal },
eventTracker: { setTrackElement },
} = useApplication();
const {
membership: { currentProjectRole },
} = useUser();
const { currentProjectDetails } = useProject();
const { projectStates } = useProjectState();
const {
project: { projectLabels },
} = useLabel();
const activeLayout = projectIssueFiltersStore.issueFilters?.displayFilters?.layout;
@ -180,9 +187,9 @@ export const CycleIssuesHeader: React.FC = observer(() => {
layoutDisplayFiltersOptions={
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined
}
labels={projectLabels ?? undefined}
labels={projectLabels}
members={projectMembers?.map((m) => m.member)}
states={projectStateStore.states?.[projectId ?? ""] ?? undefined}
states={projectStates}
/>
</FiltersDropdown>
<FiltersDropdown title="Display" placement="bottom-end">
@ -205,7 +212,7 @@ export const CycleIssuesHeader: React.FC = observer(() => {
<Button
onClick={() => {
setTrackElement("CYCLE_PAGE_HEADER");
commandPaletteStore.toggleCreateIssueModal(true, EProjectStore.CYCLE);
toggleCreateIssueModal(true, EProjectStore.CYCLE);
}}
size="sm"
prependIcon={<Plus />}

View File

@ -2,7 +2,8 @@ import { useCallback, useState } from "react";
import Link from "next/link";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
// mobx store
// hooks
import { useLabel, useUser } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
// components
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection } from "components/issues";
@ -29,19 +30,23 @@ type Props = {
export const GlobalIssuesHeader: React.FC<Props> = observer((props) => {
const { activeLayout } = props;
// states
const [createViewModal, setCreateViewModal] = useState(false);
// router
const router = useRouter();
const { workspaceSlug } = router.query as { workspaceSlug: string };
const { workspaceSlug } = router.query;
// store hooks
const {
workspace: { workspaceLabels },
workspaceMember: { workspaceMembers },
project: { workspaceProjects },
user: { currentWorkspaceRole },
workspaceGlobalIssuesFilter: { issueFilters, updateFilters },
} = useMobxStore();
const {
membership: { currentWorkspaceRole },
} = useUser();
const {
workspace: { workspaceLabels },
} = useLabel();
const handleFiltersUpdate = useCallback(
(key: keyof IIssueFilterOptions, value: string | string[]) => {
@ -57,7 +62,7 @@ export const GlobalIssuesHeader: React.FC<Props> = observer((props) => {
else newValues.push(value);
}
updateFilters(workspaceSlug, EFilterType.FILTERS, { [key]: newValues });
updateFilters(workspaceSlug.toString(), EFilterType.FILTERS, { [key]: newValues });
},
[workspaceSlug, issueFilters, updateFilters]
);
@ -65,7 +70,7 @@ export const GlobalIssuesHeader: React.FC<Props> = observer((props) => {
const handleDisplayFilters = useCallback(
(updatedDisplayFilter: Partial<IIssueDisplayFilterOptions>) => {
if (!workspaceSlug) return;
updateFilters(workspaceSlug, EFilterType.DISPLAY_FILTERS, updatedDisplayFilter);
updateFilters(workspaceSlug.toString(), EFilterType.DISPLAY_FILTERS, updatedDisplayFilter);
},
[workspaceSlug, updateFilters]
);
@ -73,7 +78,7 @@ export const GlobalIssuesHeader: React.FC<Props> = observer((props) => {
const handleDisplayProperties = useCallback(
(property: Partial<IIssueDisplayProperties>) => {
if (!workspaceSlug) return;
updateFilters(workspaceSlug, EFilterType.DISPLAY_PROPERTIES, property);
updateFilters(workspaceSlug.toString(), EFilterType.DISPLAY_PROPERTIES, property);
},
[workspaceSlug, updateFilters]
);

View File

@ -1,9 +1,9 @@
import { useCallback, useState } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useMobxStore } from "lib/mobx/store-provider";
import { useApplication, useLabel, useProject, useProjectState, useUser } from "hooks/store";
import useLocalStorage from "hooks/use-local-storage";
// components
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
@ -25,28 +25,33 @@ import { EFilterType } from "store_legacy/issues/types";
import { EProjectStore } from "store_legacy/command-palette.store";
export const ModuleIssuesHeader: React.FC = observer(() => {
// states
const [analyticsModal, setAnalyticsModal] = useState(false);
// router
const router = useRouter();
const { workspaceSlug, projectId, moduleId } = router.query as {
workspaceSlug: string;
projectId: string;
moduleId: string;
};
// store hooks
const {
module: moduleStore,
project: projectStore,
projectMember: { projectMembers },
projectState: projectStateStore,
commandPalette: commandPaletteStore,
trackEvent: { setTrackElement },
projectLabel: { projectLabels },
moduleIssuesFilter: { issueFilters, updateFilters },
user: { currentProjectRole },
} = useMobxStore();
const { currentProjectDetails } = projectStore;
const {
commandPalette: { toggleCreateIssueModal },
eventTracker: { setTrackElement },
} = useApplication();
const {
membership: { currentProjectRole },
} = useUser();
const { currentProjectDetails } = useProject();
const {
project: { projectLabels },
} = useLabel();
const { projectStates } = useProjectState();
const { setValue, storedValue } = useLocalStorage("module_sidebar_collapsed", "false");
@ -181,9 +186,9 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
layoutDisplayFiltersOptions={
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined
}
labels={projectLabels ?? undefined}
labels={projectLabels}
members={projectMembers?.map((m) => m.member)}
states={projectStateStore.states?.[projectId ?? ""] ?? undefined}
states={projectStates}
/>
</FiltersDropdown>
<FiltersDropdown title="Display" placement="bottom-end">
@ -206,7 +211,7 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
<Button
onClick={() => {
setTrackElement("MODULE_PAGE_HEADER");
commandPaletteStore.toggleCreateIssueModal(true, EProjectStore.MODULE);
toggleCreateIssueModal(true, EProjectStore.MODULE);
}}
size="sm"
prependIcon={<Plus />}

View File

@ -1,32 +1,35 @@
import { FC } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { ArrowLeft } from "lucide-react";
// hooks
import { useLabel, useProject, useProjectState } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
// constants
import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue";
// ui
import { Breadcrumbs, LayersIcon } from "@plane/ui";
// icons
import { ArrowLeft } from "lucide-react";
// components
import { DisplayFiltersSelection, FilterSelection, FiltersDropdown } from "components/issues";
// helpers
import { renderEmoji } from "helpers/emoji.helper";
// types
import type { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions } from "types";
// helper
import { renderEmoji } from "helpers/emoji.helper";
export const ProjectArchivedIssuesHeader: FC = observer(() => {
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// store hooks
const {
project: { currentProjectDetails },
projectLabel: { projectLabels },
projectMember: { projectMembers },
archivedIssueFilters: archivedIssueFiltersStore,
projectState: projectStateStore,
} = useMobxStore();
const { currentProjectDetails } = useProject();
const { projectStates } = useProjectState();
const {
project: { projectLabels },
} = useLabel();
// for archived issues list layout is the only option
const activeLayout = "list";
@ -118,9 +121,9 @@ export const ProjectArchivedIssuesHeader: FC = observer(() => {
layoutDisplayFiltersOptions={
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.archived_issues[activeLayout] : undefined
}
labels={projectLabels ?? undefined}
labels={projectLabels}
members={projectMembers?.map((m) => m.member)}
states={projectStateStore.states?.[projectId?.toString() ?? ""] ?? undefined}
states={projectStates}
/>
</FiltersDropdown>
<FiltersDropdown title="Display" placement="bottom-end">

View File

@ -2,6 +2,7 @@ import { FC, useCallback } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
// hooks
import { useLabel, useProject, useProjectState } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
// components
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
@ -14,16 +15,19 @@ import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOption
import { ISSUE_DISPLAY_FILTERS_BY_LAYOUT } from "constants/issue";
export const ProjectDraftIssueHeader: FC = observer(() => {
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
// store hooks
const {
project: { currentProjectDetails },
projectLabel: { projectLabels },
projectMember: { projectMembers },
projectState: projectStateStore,
projectDraftIssuesFilter: { issueFilters, updateFilters },
} = useMobxStore();
const { currentProjectDetails } = useProject();
const { projectStates } = useProjectState();
const {
project: { projectLabels },
} = useLabel();
const activeLayout = issueFilters?.displayFilters?.layout;
@ -112,9 +116,9 @@ export const ProjectDraftIssueHeader: FC = observer(() => {
layoutDisplayFiltersOptions={
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined
}
labels={projectLabels ?? undefined}
labels={projectLabels}
members={projectMembers?.map((m) => m.member)}
states={projectStateStore.states?.[projectId ?? ""] ?? undefined}
states={projectStates}
/>
</FiltersDropdown>
<FiltersDropdown title="Display" placement="bottom-end">

View File

@ -2,7 +2,8 @@ import { useCallback } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { Plus } from "lucide-react";
// mobx store
// hooks
import { useApplication, useLabel, useProject, useProjectState, useUser } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
// components
import { DisplayFiltersSelection, FiltersDropdown, FilterSelection, LayoutSelection } from "components/issues";
@ -21,24 +22,31 @@ import { EFilterType } from "store_legacy/issues/types";
import { EProjectStore } from "store_legacy/command-palette.store";
export const ProjectViewIssuesHeader: React.FC = observer(() => {
// router
const router = useRouter();
const { workspaceSlug, projectId, viewId } = router.query as {
workspaceSlug: string;
projectId: string;
viewId: string;
};
// store hooks
const {
project: { currentProjectDetails },
projectLabel: { projectLabels },
projectMember: { projectMembers },
projectState: projectStateStore,
projectViews: projectViewsStore,
viewIssuesFilter: { issueFilters, updateFilters },
commandPalette: commandPaletteStore,
trackEvent: { setTrackElement },
user: { currentProjectRole },
} = useMobxStore();
const {
commandPalette: { toggleCreateIssueModal },
eventTracker: { setTrackElement },
} = useApplication();
const {
membership: { currentProjectRole },
} = useUser();
const { currentProjectDetails } = useProject();
const { projectStates } = useProjectState();
const {
project: { projectLabels },
} = useLabel();
const activeLayout = issueFilters?.displayFilters?.layout;
@ -164,9 +172,9 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
layoutDisplayFiltersOptions={
activeLayout ? ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues[activeLayout] : undefined
}
labels={projectLabels ?? undefined}
labels={projectLabels}
members={projectMembers?.map((m) => m.member)}
states={projectStateStore.states?.[projectId ?? ""] ?? undefined}
states={projectStates}
/>
</FiltersDropdown>
<FiltersDropdown title="Display" placement="bottom-end">
@ -184,7 +192,7 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
<Button
onClick={() => {
setTrackElement("PROJECT_VIEW_PAGE_HEADER");
commandPaletteStore.toggleCreateIssueModal(true, EProjectStore.PROJECT_VIEW);
toggleCreateIssueModal(true, EProjectStore.PROJECT_VIEW);
}}
size="sm"
prependIcon={<Plus />}

View File

@ -4,9 +4,9 @@ import { observer } from "mobx-react-lite";
import useSWR from "swr";
import { useForm } from "react-hook-form";
import { AlertTriangle, CheckCircle2, Clock, Copy, ExternalLink, Inbox, XCircle } from "lucide-react";
// mobx store
// hooks
import { useMobxStore } from "lib/mobx/store-provider";
import { useProjectState, useUser } from "hooks/store";
// components
import { IssueDescriptionForm, IssueDetailsSidebar, IssueReaction, IssueUpdateStatus } from "components/issues";
import { InboxIssueActivity } from "components/inbox";
@ -28,19 +28,19 @@ const defaultValues: Partial<IInboxIssue> = {
};
export const InboxMainContent: React.FC = observer(() => {
const router = useRouter();
const { workspaceSlug, projectId, inboxId, inboxIssueId } = router.query;
// states
const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved");
// router
const router = useRouter();
const { workspaceSlug, projectId, inboxId, inboxIssueId } = router.query;
// store hooks
const { inboxIssues: inboxIssuesStore, inboxIssueDetails: inboxIssueDetailsStore } = useMobxStore();
const {
inboxIssues: inboxIssuesStore,
inboxIssueDetails: inboxIssueDetailsStore,
user: { currentUser, currentProjectRole },
projectState: { states },
} = useMobxStore();
currentUser,
membership: { currentProjectRole },
} = useUser();
const { projectStates } = useProjectState();
// form info
const { reset, control, watch } = useForm<IIssue>({
defaultValues,
});
@ -60,9 +60,7 @@ export const InboxMainContent: React.FC = observer(() => {
const issuesList = inboxId ? inboxIssuesStore.inboxIssues[inboxId.toString()] : undefined;
const issueDetails = inboxIssueId ? inboxIssueDetailsStore.issueDetails[inboxIssueId.toString()] : undefined;
const currentIssueState = projectId
? states[projectId.toString()]?.find((s) => s.id === issueDetails?.state)
: undefined;
const currentIssueState = projectStates?.find((s) => s.id === issueDetails?.state);
const submitChanges = useCallback(
async (formData: Partial<IInboxIssue>) => {

View File

@ -4,22 +4,22 @@ import { observer } from "mobx-react-lite";
import { Dialog, Transition } from "@headlessui/react";
import { Controller, useForm } from "react-hook-form";
import { RichTextEditorWithRef } from "@plane/rich-text-editor";
// mobx store
import { Sparkle } from "lucide-react";
// hooks
import { useApplication, useWorkspace } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
import useToast from "hooks/use-toast";
import useEditorSuggestions from "hooks/use-editor-suggestions";
// services
import { FileService } from "services/file.service";
import { AIService } from "services/ai.service";
// components
import { IssuePrioritySelect } from "components/issues/select";
import { GptAssistantModal } from "components/core";
// ui
import { Button, Input, ToggleSwitch } from "@plane/ui";
// types
import { IIssue } from "types";
import useEditorSuggestions from "hooks/use-editor-suggestions";
import { GptAssistantModal } from "components/core";
import { Sparkle } from "lucide-react";
import useToast from "hooks/use-toast";
import { AIService } from "services/ai.service";
type Props = {
isOpen: boolean;
@ -40,30 +40,29 @@ const fileService = new FileService();
export const CreateInboxIssueModal: React.FC<Props> = observer((props) => {
const { isOpen, onClose } = props;
// states
const [createMore, setCreateMore] = useState(false);
const [gptAssistantModal, setGptAssistantModal] = useState(false);
const [iAmFeelingLucky, setIAmFeelingLucky] = useState(false);
// refs
const editorRef = useRef<any>(null);
// toast alert
const { setToastAlert } = useToast();
const editorSuggestion = useEditorSuggestions();
// router
const router = useRouter();
const { workspaceSlug, projectId, inboxId } = router.query as {
workspaceSlug: string;
projectId: string;
inboxId: string;
};
// store hooks
const { inboxIssueDetails: inboxIssueDetailsStore } = useMobxStore();
const {
inboxIssueDetails: inboxIssueDetailsStore,
trackEvent: { postHogEventTracker },
appConfig: { envConfig },
workspace: { currentWorkspace },
} = useMobxStore();
config: { envConfig },
eventTracker: { postHogEventTracker },
} = useApplication();
const { currentWorkspace } = useWorkspace();
const {
control,

View File

@ -2,10 +2,9 @@ import React, { useState } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { Dialog, Transition } from "@headlessui/react";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useApplication, useWorkspace } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
import useToast from "hooks/use-toast";
// icons
import { AlertTriangle } from "lucide-react";
@ -21,16 +20,17 @@ type Props = {
};
export const DeleteInboxIssueModal: React.FC<Props> = observer(({ isOpen, onClose, data }) => {
// states
const [isDeleting, setIsDeleting] = useState(false);
// router
const router = useRouter();
const { workspaceSlug, projectId, inboxId } = router.query;
// store hooks
const { inboxIssueDetails: inboxIssueDetailsStore } = useMobxStore();
const {
inboxIssueDetails: inboxIssueDetailsStore,
trackEvent: { postHogEventTracker },
workspace: { currentWorkspace },
} = useMobxStore();
eventTracker: { postHogEventTracker },
} = useApplication();
const { currentWorkspace } = useWorkspace();
const { setToastAlert } = useToast();

View File

@ -1,9 +1,9 @@
import { useState } from "react";
import { observer } from "mobx-react-lite";
import { PlusIcon } from "lucide-react";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useApplication, useUser } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
import useToast from "hooks/use-toast";
// components
import { EmptyState } from "components/common";
@ -28,13 +28,15 @@ export const CycleEmptyState: React.FC<Props> = observer((props) => {
const { workspaceSlug, projectId, cycleId } = props;
// states
const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false);
// store hooks
const { cycleIssues: cycleIssueStore } = useMobxStore();
const {
cycleIssues: cycleIssueStore,
commandPalette: commandPaletteStore,
trackEvent: { setTrackElement },
user: { currentProjectRole: userRole },
} = useMobxStore();
commandPalette: { toggleCreateIssueModal },
eventTracker: { setTrackElement },
} = useApplication();
const {
membership: { currentProjectRole: userRole },
} = useUser();
const { setToastAlert } = useToast();
@ -72,7 +74,7 @@ export const CycleEmptyState: React.FC<Props> = observer((props) => {
icon: <PlusIcon className="h-3 w-3" strokeWidth={2} />,
onClick: () => {
setTrackElement("CYCLE_EMPTY_STATE");
commandPaletteStore.toggleCreateIssueModal(true, EProjectStore.CYCLE);
toggleCreateIssueModal(true, EProjectStore.CYCLE);
},
}}
secondaryButton={

View File

@ -1,15 +1,19 @@
import { useState } from "react";
import { observer } from "mobx-react-lite";
import { PlusIcon } from "lucide-react";
// hooks
import { useApplication, useUser } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
import useToast from "hooks/use-toast";
// components
import { EmptyState } from "components/common";
import { ExistingIssuesListModal } from "components/core";
// ui
import { Button } from "@plane/ui";
// assets
import emptyIssue from "public/empty-state/issue.svg";
import { ExistingIssuesListModal } from "components/core";
import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider";
// types
import { ISearchIssueResponse } from "types";
import useToast from "hooks/use-toast";
import { useState } from "react";
// constants
import { EUserWorkspaceRoles } from "constants/workspace";
@ -23,14 +27,16 @@ export const ModuleEmptyState: React.FC<Props> = observer((props) => {
const { workspaceSlug, projectId, moduleId } = props;
// states
const [moduleIssuesListModal, setModuleIssuesListModal] = useState(false);
// store hooks
const { moduleIssues: moduleIssueStore } = useMobxStore();
const {
moduleIssues: moduleIssueStore,
commandPalette: commandPaletteStore,
trackEvent: { setTrackElement },
user: { currentProjectRole: userRole },
} = useMobxStore();
commandPalette: { toggleCreateIssueModal },
eventTracker: { setTrackElement },
} = useApplication();
const {
membership: { currentProjectRole: userRole },
} = useUser();
// toast alert
const { setToastAlert } = useToast();
const handleAddIssuesToModule = async (data: ISearchIssueResponse[]) => {
@ -67,7 +73,7 @@ export const ModuleEmptyState: React.FC<Props> = observer((props) => {
icon: <PlusIcon className="h-3 w-3" strokeWidth={2} />,
onClick: () => {
setTrackElement("MODULE_EMPTY_STATE");
commandPaletteStore.toggleCreateIssueModal(true);
toggleCreateIssueModal(true);
},
}}
secondaryButton={

View File

@ -1,5 +1,7 @@
import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider";
import { X } from "lucide-react";
// hooks
import { useUser } from "hooks/store";
// components
import {
AppliedDateFilters,
@ -10,8 +12,6 @@ import {
AppliedStateFilters,
AppliedStateGroupFilters,
} from "components/issues";
// icons
import { X } from "lucide-react";
// helpers
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
// types
@ -34,10 +34,10 @@ const dateFilters = ["start_date", "target_date"];
export const AppliedFiltersList: React.FC<Props> = observer((props) => {
const { appliedFilters, handleClearAllFilters, handleRemoveFilter, labels, members, projects, states } = props;
// store hooks
const {
user: { currentProjectRole },
} = useMobxStore();
membership: { currentProjectRole },
} = useUser();
if (!appliedFilters) return null;

View File

@ -1,15 +1,14 @@
import { Fragment, useState } from "react";
import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { usePopper } from "react-popper";
import { Check, ChevronDown, Search, Tags } from "lucide-react";
// hooks
import { useApplication, useLabel } from "hooks/store";
// components
import { Combobox } from "@headlessui/react";
import { Tooltip } from "@plane/ui";
import { Check, ChevronDown, Search, Tags } from "lucide-react";
// types
import { Placement } from "@popperjs/core";
import { RootStore } from "store_legacy/root";
import { IIssueLabel } from "types";
export interface IIssuePropertyLabels {
@ -44,18 +43,19 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
noLabelBorder = false,
placeholderText,
} = props;
const {
workspace: workspaceStore,
projectLabel: { fetchProjectLabels, labels },
}: RootStore = useMobxStore();
const workspaceSlug = workspaceStore?.workspaceSlug;
// states
const [query, setQuery] = useState("");
// popper-js refs
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null);
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null);
const [isLoading, setIsLoading] = useState<Boolean>(false);
// store hooks
const {
router: { workspaceSlug },
} = useApplication();
const {
project: { fetchProjectLabels, projectLabels: storeLabels },
} = useLabel();
const fetchLabels = () => {
setIsLoading(true);
@ -65,7 +65,6 @@ export const IssuePropertyLabels: React.FC<IIssuePropertyLabels> = observer((pro
if (!value) return null;
let projectLabels: IIssueLabel[] = defaultOptions;
const storeLabels = projectId && labels ? labels[projectId] : [];
if (storeLabels && storeLabels.length > 0) projectLabels = storeLabels;
const options = projectLabels.map((label) => ({

View File

@ -3,12 +3,11 @@ import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import useSWR, { mutate } from "swr";
import { MinusCircle } from "lucide-react";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useApplication, useProject, useProjectState, useUser, useWorkspace } from "hooks/store";
import useToast from "hooks/use-toast";
// services
import { IssueService, IssueCommentService } from "services/issue";
// hooks
import useToast from "hooks/use-toast";
// components
import {
AddComment,
@ -49,19 +48,19 @@ export const IssueMainContent: React.FC<Props> = observer((props) => {
const { workspaceSlug, projectId, issueId } = router.query;
// toast alert
const { setToastAlert } = useToast();
// mobx store
const {
user: { currentUser, currentProjectRole },
project: projectStore,
projectState: { states },
trackEvent: { postHogEventTracker },
workspace: { currentWorkspace },
} = useMobxStore();
eventTracker: { postHogEventTracker },
} = useApplication();
const {
currentUser,
membership: { currentProjectRole },
} = useUser();
const { currentWorkspace } = useWorkspace();
const { getProjectById } = useProject();
const { projectStates } = useProjectState();
const projectDetails = projectId ? projectStore.project_details[projectId.toString()] : undefined;
const currentIssueState = projectId
? states[projectId.toString()]?.find((s) => s.id === issueDetails.state)
: undefined;
const projectDetails = projectId ? getProjectById(projectId.toString()) : null;
const currentIssueState = projectStates?.find((s) => s.id === issueDetails.state);
const { data: siblingIssues } = useSWR(
workspaceSlug && projectId && issueDetails?.parent ? SUB_ISSUES(issueDetails.parent) : null,
@ -94,7 +93,7 @@ export const IssueMainContent: React.FC<Props> = observer((props) => {
{
isGrouping: true,
groupType: "Workspace_metrics",
gorupId: currentWorkspace?.id!,
groupId: currentWorkspace?.id!,
}
);
});
@ -117,7 +116,7 @@ export const IssueMainContent: React.FC<Props> = observer((props) => {
{
isGrouping: true,
groupType: "Workspace_metrics",
gorupId: currentWorkspace?.id!,
groupId: currentWorkspace?.id!,
}
);
});
@ -139,7 +138,7 @@ export const IssueMainContent: React.FC<Props> = observer((props) => {
{
isGrouping: true,
groupType: "Workspace_metrics",
gorupId: currentWorkspace?.id!,
groupId: currentWorkspace?.id!,
}
);
})
@ -260,12 +259,12 @@ export const IssueMainContent: React.FC<Props> = observer((props) => {
activity={issueActivity}
handleCommentUpdate={handleCommentUpdate}
handleCommentDelete={handleCommentDelete}
showAccessSpecifier={projectDetails && projectDetails.is_deployed}
showAccessSpecifier={Boolean(projectDetails && projectDetails.is_deployed)}
/>
<AddComment
onSubmit={handleAddComment}
disabled={uneditable}
showAccessSpecifier={projectDetails && projectDetails.is_deployed}
showAccessSpecifier={Boolean(projectDetails && projectDetails.is_deployed)}
/>
</div>
</>

View File

@ -3,13 +3,13 @@ import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { mutate } from "swr";
import { Dialog, Transition } from "@headlessui/react";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// services
import { IssueDraftService } from "services/issue";
// hooks
import { useApplication, useUser, useWorkspace } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
import useToast from "hooks/use-toast";
import useLocalStorage from "hooks/use-local-storage";
// services
import { IssueDraftService } from "services/issue";
// components
import { IssueForm, ConfirmIssueDiscard } from "components/issues";
// types
@ -57,14 +57,13 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
handleSubmit,
currentStore = EProjectStore.PROJECT,
} = props;
// states
const [createMore, setCreateMore] = useState(false);
const [formDirtyState, setFormDirtyState] = useState<any>(null);
const [showConfirmDiscard, setShowConfirmDiscard] = useState(false);
const [activeProject, setActiveProject] = useState<string | null>(null);
const [prePopulateData, setPreloadedData] = useState<Partial<IIssue>>({});
// router
const router = useRouter();
const { workspaceSlug, projectId, cycleId, moduleId } = router.query as {
workspaceSlug: string;
@ -72,7 +71,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
cycleId: string | undefined;
moduleId: string | undefined;
};
// store hooks
const {
project: projectStore,
projectIssues: projectIssueStore,
@ -80,12 +79,12 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
workspaceProfileIssues: profileIssueStore,
cycleIssues: cycleIssueStore,
moduleIssues: moduleIssueStore,
user: userStore,
trackEvent: { postHogEventTracker },
workspace: { currentWorkspace },
} = useMobxStore();
const user = userStore.currentUser;
const {
eventTracker: { postHogEventTracker },
} = useApplication();
const { currentUser } = useUser();
const { currentWorkspace } = useWorkspace();
const issueStores = {
[EProjectStore.PROJECT]: {
@ -100,7 +99,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
},
[EProjectStore.PROFILE]: {
store: profileIssueStore,
dataIdToUpdate: user?.id || undefined,
dataIdToUpdate: currentUser?.id || undefined,
viewId: undefined,
},
[EProjectStore.CYCLE]: {
@ -150,10 +149,10 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
setPreloadedData((prevData) => ({
...(prevData ?? {}),
...prePopulateDataProps,
assignees: prePopulateDataProps?.assignees ?? [user?.id ?? ""],
assignees: prePopulateDataProps?.assignees ?? [currentUser?.id ?? ""],
}));
}
}, [prePopulateDataProps, cycleId, moduleId, router.asPath, user?.id]);
}, [prePopulateDataProps, cycleId, moduleId, router.asPath, currentUser?.id]);
/**
*
@ -260,7 +259,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
{
isGrouping: true,
groupType: "Workspace_metrics",
gorupId: currentWorkspace?.id!,
groupId: currentWorkspace?.id!,
}
);
if (payload.parent && payload.parent !== "") mutate(SUB_ISSUES(payload.parent));
@ -280,7 +279,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
{
isGrouping: true,
groupType: "Workspace_metrics",
gorupId: currentWorkspace?.id!,
groupId: currentWorkspace?.id!,
}
);
});
@ -289,7 +288,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
};
const createDraftIssue = async () => {
if (!workspaceSlug || !activeProject || !user) return;
if (!workspaceSlug || !activeProject || !currentUser) return;
const payload: Partial<IIssue> = {
...formDirtyState,
@ -308,7 +307,8 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
setFormDirtyState(null);
setShowConfirmDiscard(false);
if (payload.assignees?.some((assignee) => assignee === user?.id)) mutate(USER_ISSUE(workspaceSlug as string));
if (payload.assignees?.some((assignee) => assignee === currentUser?.id))
mutate(USER_ISSUE(workspaceSlug as string));
if (payload.parent && payload.parent !== "") mutate(SUB_ISSUES(payload.parent));
})
@ -343,7 +343,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
{
isGrouping: true,
groupType: "Workspace_metrics",
gorupId: currentWorkspace?.id!,
groupId: currentWorkspace?.id!,
}
);
})
@ -361,7 +361,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = observer((prop
{
isGrouping: true,
groupType: "Workspace_metrics",
gorupId: currentWorkspace?.id!,
groupId: currentWorkspace?.id!,
}
);
});

View File

@ -1,12 +1,12 @@
import { FC, useState } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
// mobx store
import { CalendarDays, Link2, Plus, Signal, Tag, Triangle, LayoutPanelTop } from "lucide-react";
// hooks
import { useProject, useUser } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
// ui icons
import { DiceIcon, DoubleCircleIcon, UserGroupIcon, ContrastIcon } from "@plane/ui";
import { CalendarDays, Link2, Plus, Signal, Tag, Triangle, LayoutPanelTop } from "lucide-react";
import {
SidebarAssigneeSelect,
SidebarCycleSelect,
@ -39,13 +39,15 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
// states
const [linkModal, setLinkModal] = useState(false);
const [selectedLinkToUpdate, setSelectedLinkToUpdate] = useState<ILinkDetails | null>(null);
// store hooks
const {
user: { currentProjectRole },
issueDetail: { fetchPeekIssueDetails },
project: { getProjectById },
} = useMobxStore();
const {
membership: { currentProjectRole },
} = useUser();
const { getProjectById } = useProject();
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;

View File

@ -4,15 +4,13 @@ import { observer } from "mobx-react-lite";
import { Controller, useForm } from "react-hook-form";
import { TwitterPicker } from "react-color";
import { Popover, Transition } from "@headlessui/react";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
import { Plus, X } from "lucide-react";
// hooks
import { useLabel } from "hooks/store";
import useToast from "hooks/use-toast";
// ui
import { Input } from "@plane/ui";
import { IssueLabelSelect } from "../select";
// icons
import { Plus, X } from "lucide-react";
// types
import { IIssue, IIssueLabel } from "types";
@ -40,8 +38,8 @@ export const SidebarLabelSelect: React.FC<Props> = observer((props) => {
const { setToastAlert } = useToast();
// mobx store
const {
projectLabel: { projectLabels, createLabel },
} = useMobxStore();
project: { projectLabels, createLabel },
} = useLabel();
// form info
const {
handleSubmit,

View File

@ -3,9 +3,10 @@ import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { mutate } from "swr";
import { Controller, UseFormWatch } from "react-hook-form";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
import { Bell, CalendarDays, LinkIcon, Plus, Signal, Tag, Trash2, Triangle, LayoutPanelTop } from "lucide-react";
// hooks
import { useProjectState, useUser } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
import useToast from "hooks/use-toast";
import useUserIssueNotificationSubscription from "hooks/use-issue-notification-subscription";
import useEstimateOption from "hooks/use-estimate-option";
@ -32,7 +33,6 @@ import {
// ui
import { CustomDatePicker } from "components/ui";
// icons
import { Bell, CalendarDays, LinkIcon, Plus, Signal, Tag, Trash2, Triangle, LayoutPanelTop } from "lucide-react";
import { Button, ContrastIcon, DiceIcon, DoubleCircleIcon, StateGroupIcon, UserGroupIcon } from "@plane/ui";
// helpers
import { copyTextToClipboard } from "helpers/string.helper";
@ -75,18 +75,21 @@ const moduleService = new ModuleService();
export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
const { control, submitChanges, issueDetail, watch: watchIssue, fieldsToShow = ["all"], uneditable = false } = props;
// states
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
const [linkModal, setLinkModal] = useState(false);
const [selectedLinkToUpdate, setSelectedLinkToUpdate] = useState<ILinkDetails | null>(null);
// store hooks
const {
user: { currentUser, currentProjectRole },
projectState: { states },
projectIssues: { removeIssue },
issueDetail: { createIssueLink, updateIssueLink, deleteIssueLink },
} = useMobxStore();
const {
currentUser,
membership: { currentProjectRole },
} = useUser();
const { projectStates } = useProjectState();
// router
const router = useRouter();
const { workspaceSlug, projectId, issueId, inboxIssueId } = router.query;
@ -190,9 +193,7 @@ export const IssueDetailsSidebar: React.FC<Props> = observer((props) => {
const isAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
const currentIssueState = projectId
? states[projectId.toString()]?.find((s) => s.id === issueDetail?.state)
: undefined;
const currentIssueState = projectStates?.find((s) => s.id === issueDetail?.state);
return (
<>

View File

@ -3,8 +3,10 @@ import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import useSWR, { mutate } from "swr";
import { Plus, ChevronRight, ChevronDown } from "lucide-react";
// mobx store
// hooks
import { useUser } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
import useToast from "hooks/use-toast";
// components
import { ExistingIssuesListModal } from "components/core";
import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
@ -12,8 +14,6 @@ import { SubIssuesRootList } from "./issues-list";
import { ProgressBar } from "./progressbar";
// ui
import { CustomMenu } from "@plane/ui";
// hooks
import useToast from "hooks/use-toast";
// helpers
import { copyTextToClipboard } from "helpers/string.helper";
// types
@ -43,13 +43,15 @@ const issueService = new IssueService();
export const SubIssuesRoot: React.FC<ISubIssuesRoot> = observer((props) => {
const { parentIssue, user } = props;
// store hooks
const {
user: { currentProjectRole },
issue: { updateIssueStructure },
projectIssues: { updateIssue, removeIssue },
} = useMobxStore();
const {
membership: { currentProjectRole },
} = useUser();
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;

View File

@ -1,21 +1,19 @@
import React, { useEffect } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { Controller, useForm } from "react-hook-form";
import { TwitterPicker } from "react-color";
import { Dialog, Popover, Transition } from "@headlessui/react";
// store
import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider";
import { ChevronDown } from "lucide-react";
// hooks
import { useLabel } from "hooks/store";
import useToast from "hooks/use-toast";
// ui
import { Button, Input } from "@plane/ui";
// icons
import { ChevronDown } from "lucide-react";
// types
import type { IIssueLabel, IState } from "types";
// constants
import { LABEL_COLOR_OPTIONS, getRandomLabelColor } from "constants/label";
import useToast from "hooks/use-toast";
// types
type Props = {
@ -32,13 +30,14 @@ const defaultValues: Partial<IState> = {
export const CreateLabelModal: React.FC<Props> = observer((props) => {
const { isOpen, projectId, handleClose, onSuccess } = props;
// router
const router = useRouter();
const { workspaceSlug } = router.query;
// store
const { projectLabel: projectLabelStore } = useMobxStore();
// store hooks
const {
project: { createLabel },
} = useLabel();
// form info
const {
formState: { errors, isSubmitting },
handleSubmit,
@ -72,8 +71,7 @@ export const CreateLabelModal: React.FC<Props> = observer((props) => {
const onSubmit = async (formData: IIssueLabel) => {
if (!workspaceSlug) return;
await projectLabelStore
.createLabel(workspaceSlug.toString(), projectId.toString(), formData)
await createLabel(workspaceSlug.toString(), projectId.toString(), formData)
.then((res) => {
onClose();
if (onSuccess) onSuccess(res);

View File

@ -1,20 +1,18 @@
import React, { forwardRef, useEffect } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { TwitterPicker } from "react-color";
import { Controller, SubmitHandler, useForm } from "react-hook-form";
// stores
import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider";
// headless ui
import { Popover, Transition } from "@headlessui/react";
// hooks
import { useLabel } from "hooks/store";
import useToast from "hooks/use-toast";
// ui
import { Button, Input } from "@plane/ui";
// types
import { IIssueLabel } from "types";
// fetch-keys
import { getRandomLabelColor, LABEL_COLOR_OPTIONS } from "constants/label";
import useToast from "hooks/use-toast";
type Props = {
labelForm: boolean;
@ -32,16 +30,16 @@ const defaultValues: Partial<IIssueLabel> = {
export const CreateUpdateLabelInline = observer(
forwardRef<HTMLFormElement, Props>(function CreateUpdateLabelInline(props, ref) {
const { labelForm, setLabelForm, isUpdating, labelToUpdate, onClose } = props;
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// store
const { projectLabel: projectLabelStore } = useMobxStore();
// store hooks
const {
project: { createLabel, updateLabel },
} = useLabel();
// toast alert
const { setToastAlert } = useToast();
// form info
const {
handleSubmit,
control,
@ -63,8 +61,7 @@ export const CreateUpdateLabelInline = observer(
const handleLabelCreate: SubmitHandler<IIssueLabel> = async (formData) => {
if (!workspaceSlug || !projectId || isSubmitting) return;
await projectLabelStore
.createLabel(workspaceSlug.toString(), projectId.toString(), formData)
await createLabel(workspaceSlug.toString(), projectId.toString(), formData)
.then(() => {
handleClose();
reset(defaultValues);
@ -82,8 +79,7 @@ export const CreateUpdateLabelInline = observer(
const handleLabelUpdate: SubmitHandler<IIssueLabel> = async (formData) => {
if (!workspaceSlug || !projectId || isSubmitting) return;
await projectLabelStore
.updateLabel(workspaceSlug.toString(), projectId.toString(), labelToUpdate?.id!, formData)
await updateLabel(workspaceSlug.toString(), projectId.toString(), labelToUpdate?.id!, formData)
.then(() => {
reset(defaultValues);
handleClose();

View File

@ -1,15 +1,13 @@
import React, { useState } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import useSWR from "swr";
import { Combobox, Dialog, Transition } from "@headlessui/react";
// store
import { observer } from "mobx-react-lite";
import { useMobxStore } from "lib/mobx/store-provider";
import { Search } from "lucide-react";
// hooks
import { useLabel } from "hooks/store";
// icons
import { LayerStackIcon } from "@plane/ui";
import { Search } from "lucide-react";
// types
import { IIssueLabel } from "types";
@ -21,18 +19,15 @@ type Props = {
export const LabelsListModal: React.FC<Props> = observer((props) => {
const { isOpen, handleClose, parent } = props;
// states
const [query, setQuery] = useState("");
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// store
// store hooks
const {
projectLabel: { projectLabels, fetchProjectLabels, updateLabel },
} = useMobxStore();
// states
const [query, setQuery] = useState("");
project: { projectLabels, fetchProjectLabels, updateLabel },
} = useLabel();
// api call to fetch project details
useSWR(

View File

@ -1,12 +1,12 @@
import React, { Dispatch, SetStateAction, useState } from "react";
import { useRouter } from "next/router";
import { useMobxStore } from "lib/mobx/store-provider";
import { DraggableProvidedDragHandleProps, DraggableStateSnapshot } from "@hello-pangea/dnd";
import { X, Pencil } from "lucide-react";
// hooks
import { useLabel } from "hooks/store";
// types
import { IIssueLabel } from "types";
//icons
import { X, Pencil } from "lucide-react";
//components
// components
import { ICustomMenuItem, LabelItemBlock } from "./label-block/label-item-block";
import { CreateUpdateLabelInline } from "./create-update-label-inline";
@ -21,23 +21,21 @@ type Props = {
export const ProjectSettingLabelItem: React.FC<Props> = (props) => {
const { label, setIsUpdating, handleLabelDelete, draggableSnapshot, dragHandleProps, isChild } = props;
const { combineTargetFor, isDragging } = draggableSnapshot;
// states
const [isEditLabelForm, setEditLabelForm] = useState(false);
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// store
const { projectLabel: projectLabelStore } = useMobxStore();
//state
const [isEditLabelForm, setEditLabelForm] = useState(false);
// store hooks
const {
project: { updateLabel },
} = useLabel();
const removeFromGroup = (label: IIssueLabel) => {
if (!workspaceSlug || !projectId) return;
projectLabelStore.updateLabel(workspaceSlug.toString(), projectId.toString(), label.id, {
updateLabel(workspaceSlug.toString(), projectId.toString(), label.id, {
parent: null,
});
};

View File

@ -10,9 +10,8 @@ import {
DropResult,
Droppable,
} from "@hello-pangea/dnd";
// store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useLabel } from "hooks/store";
import useDraggableInPortal from "hooks/use-draggable-portal";
// components
import {
@ -32,23 +31,22 @@ import { IIssueLabel } from "types";
const LABELS_ROOT = "labels.root";
export const ProjectSettingsLabelList: React.FC = observer(() => {
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const renderDraggable = useDraggableInPortal();
// store
const {
projectLabel: { fetchProjectLabels, projectLabels, updateLabelPosition, projectLabelsTree },
} = useMobxStore();
// states
const [showLabelForm, setLabelForm] = useState(false);
const [isUpdating, setIsUpdating] = useState(false);
const [selectDeleteLabel, setSelectDeleteLabel] = useState<IIssueLabel | null>(null);
const [isDraggingGroup, setIsDraggingGroup] = useState(false);
// ref
// refs
const scrollToRef = useRef<HTMLFormElement>(null);
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// store hooks
const {
project: { fetchProjectLabels, projectLabels, updateLabelPosition, projectLabelsTree },
} = useLabel();
// portal
const renderDraggable = useDraggableInPortal();
// api call to fetch project details
useSWR(

View File

@ -2,9 +2,9 @@ import { useState } from "react";
import { useRouter } from "next/router";
import Link from "next/link";
import { observer } from "mobx-react-lite";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useProject, useUser } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
import useToast from "hooks/use-toast";
// components
import { ConfirmProjectMemberRemove } from "components/project";
@ -28,14 +28,16 @@ export const ProjectMemberListItem: React.FC<Props> = observer((props) => {
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// store
// store hooks
const {
user: { currentUser, currentProjectMemberInfo, currentProjectRole, leaveProject },
projectMember: { removeMemberFromProject, updateMember },
project: { fetchProjects },
} = useMobxStore();
// hooks
const {
currentUser,
membership: { currentProjectMemberInfo, currentProjectRole, leaveProject },
} = useUser();
const { fetchProjects } = useProject();
// toast alert
const { setToastAlert } = useToast();
// derived values

View File

@ -2,7 +2,8 @@ import { useState } from "react";
import { useRouter } from "next/router";
import { mutate } from "swr";
import { observer } from "mobx-react-lite";
// mobx store
// hooks
import { useApplication } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
// components
import { ProjectMemberListItem, SendProjectInvitationModal } from "components/project";
@ -12,19 +13,19 @@ import { Button, Loader } from "@plane/ui";
import { Search } from "lucide-react";
export const ProjectMemberList: React.FC = observer(() => {
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// store
const {
projectMember: { projectMembers, fetchProjectMembers },
trackEvent: { setTrackElement },
} = useMobxStore();
// states
const [inviteModal, setInviteModal] = useState(false);
const [searchQuery, setSearchQuery] = useState("");
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// store hooks
const {
projectMember: { projectMembers, fetchProjectMembers },
} = useMobxStore();
const {
eventTracker: { setTrackElement },
} = useApplication();
const searchedMembers = (projectMembers ?? []).filter((member) => {
const fullName = `${member.member.first_name} ${member.member.last_name}`.toLowerCase();

View File

@ -4,14 +4,14 @@ import { observer } from "mobx-react-lite";
import { useForm, Controller, useFieldArray } from "react-hook-form";
import { Dialog, Transition } from "@headlessui/react";
import { ChevronDown, Plus, X } from "lucide-react";
// mobx store
// hooks
import { useApplication, useUser, useWorkspace } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
import useToast from "hooks/use-toast";
// ui
import { Avatar, Button, CustomSelect, CustomSearchSelect } from "@plane/ui";
// services
import { ProjectMemberService } from "services/project";
// hooks
import useToast from "hooks/use-toast";
// types
import { IProjectMember, TUserProjectRole } from "types";
// constants
@ -47,19 +47,23 @@ const projectMemberService = new ProjectMemberService();
export const SendProjectInvitationModal: React.FC<Props> = observer((props) => {
const { isOpen, members, onClose, onSuccess } = props;
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// toast alert
const { setToastAlert } = useToast();
// store hooks
const {
user: { currentProjectRole },
workspaceMember: { workspaceMembers },
trackEvent: { postHogEventTracker },
workspace: { currentWorkspace },
} = useMobxStore();
const {
eventTracker: { postHogEventTracker },
} = useApplication();
const {
membership: { currentProjectRole },
} = useUser();
const { currentWorkspace } = useWorkspace();
// form info
const {
formState: { errors, isSubmitting },
reset,

View File

@ -1,8 +1,8 @@
import { useEffect } from "react";
import { observer } from "mobx-react-lite";
import { Controller, useForm } from "react-hook-form";
// mobx store
// hooks
import { useLabel, useProjectState } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
// components
import { AppliedFiltersList, FilterSelection, FiltersDropdown } from "components/issues";
@ -25,12 +25,16 @@ const defaultValues: Partial<IProjectView> = {
description: "",
};
export const ProjectViewForm: React.FC<Props> = observer(({ handleFormSubmit, handleClose, data, preLoadedData }) => {
export const ProjectViewForm: React.FC<Props> = observer((props) => {
const { handleFormSubmit, handleClose, data, preLoadedData } = props;
// store hooks
const {
projectLabel: { projectLabels },
projectState: projectStateStore,
projectMember: { projectMembers },
} = useMobxStore();
const { projectStates } = useProjectState();
const {
project: { projectLabels },
} = useLabel();
const {
control,
@ -176,7 +180,7 @@ export const ProjectViewForm: React.FC<Props> = observer(({ handleFormSubmit, ha
layoutDisplayFiltersOptions={ISSUE_DISPLAY_FILTERS_BY_LAYOUT.issues.list}
labels={projectLabels ?? undefined}
members={projectMembers?.map((m) => m.member) ?? undefined}
states={projectStateStore.projectStates ?? undefined}
states={projectStates}
/>
</FiltersDropdown>
)}
@ -190,7 +194,7 @@ export const ProjectViewForm: React.FC<Props> = observer(({ handleFormSubmit, ha
handleRemoveFilter={handleRemoveFilter}
labels={projectLabels ?? []}
members={projectMembers?.map((m) => m.member) ?? []}
states={projectStateStore.projectStates ?? []}
states={projectStates}
/>
</div>
)}
@ -206,8 +210,8 @@ export const ProjectViewForm: React.FC<Props> = observer(({ handleFormSubmit, ha
? "Updating View..."
: "Update View"
: isSubmitting
? "Creating View..."
: "Create View"}
? "Creating View..."
: "Create View"}
</Button>
</div>
</form>

View File

@ -3,9 +3,9 @@ import Link from "next/link";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { LinkIcon, PencilIcon, StarIcon, TrashIcon } from "lucide-react";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useUser } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
import useToast from "hooks/use-toast";
// components
import { CreateUpdateProjectViewModal, DeleteProjectViewModal } from "components/views";
@ -25,19 +25,19 @@ type Props = {
export const ProjectViewListItem: React.FC<Props> = observer((props) => {
const { view } = props;
// states
const [createUpdateViewModal, setCreateUpdateViewModal] = useState(false);
const [deleteViewModal, setDeleteViewModal] = useState(false);
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
// toast alert
const { setToastAlert } = useToast();
// store hooks
const { projectViews: projectViewsStore } = useMobxStore();
const {
projectViews: projectViewsStore,
user: { currentProjectRole },
} = useMobxStore();
membership: { currentProjectRole },
} = useUser();
const handleAddToFavorites = () => {
if (!workspaceSlug || !projectId) return;

View File

@ -1,8 +1,9 @@
import { useState } from "react";
import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
// mobx store
import { Plus, Search } from "lucide-react";
// hooks
import { useApplication, useUser } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
// components
import { ProjectViewListItem } from "components/views";
@ -11,22 +12,23 @@ import { NewEmptyState } from "components/common/new-empty-state";
import { Input, Loader } from "@plane/ui";
// assets
import emptyView from "public/empty-state/empty_view.webp";
// icons
import { Plus, Search } from "lucide-react";
// constants
import { EUserWorkspaceRoles } from "constants/workspace";
export const ProjectViewsList = observer(() => {
// states
const [query, setQuery] = useState("");
// router
const router = useRouter();
const { projectId } = router.query;
// store hooks
const { projectViews: projectViewsStore } = useMobxStore();
const {
projectViews: projectViewsStore,
commandPalette: commandPaletteStore,
user: { currentProjectRole },
} = useMobxStore();
commandPalette: { toggleCreateViewModal },
} = useApplication();
const {
membership: { currentProjectRole },
} = useUser();
const viewsList = projectId ? projectViewsStore.viewsList[projectId.toString()] : undefined;
@ -79,7 +81,7 @@ export const ProjectViewsList = observer(() => {
primaryButton={{
icon: <Plus size={14} strokeWidth={2} />,
text: "Build your first view",
onClick: () => commandPaletteStore.toggleCreateViewModal(true),
onClick: () => toggleCreateViewModal(true),
}}
disabled={!isEditingAllowed}
/>

View File

@ -4,9 +4,9 @@ import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import { mutate } from "swr";
import { ChevronDown, Dot, XCircle } from "lucide-react";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// hooks
import { useUser } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
import useToast from "hooks/use-toast";
// components
import { ConfirmWorkspaceMemberRemove } from "components/workspace";
@ -35,17 +35,21 @@ type Props = {
export const WorkspaceMembersListItem: FC<Props> = observer((props) => {
const { member } = props;
// states
const [removeMemberModal, setRemoveMemberModal] = useState(false);
// router
const router = useRouter();
const { workspaceSlug } = router.query;
// store
// store hooks
const {
workspaceMember: { removeMember, updateMember, updateMemberInvitation, deleteWorkspaceInvitation },
user: { currentWorkspaceMemberInfo, currentWorkspaceRole, currentUser, currentUserSettings, leaveWorkspace },
} = useMobxStore();
// states
const [removeMemberModal, setRemoveMemberModal] = useState(false);
// hooks
const {
currentUser,
currentUserSettings,
membership: { currentWorkspaceMemberInfo, currentWorkspaceRole, leaveWorkspace },
} = useUser();
// toast alert
const { setToastAlert } = useToast();
const handleLeaveWorkspace = async () => {

View File

@ -2,10 +2,10 @@ import { useContext } from "react";
// mobx store
import { MobxStoreContext } from "lib/mobx/store-provider";
// types
import { ILabelStore } from "store/label.store";
import { ILabelRootStore } from "store/label";
export const useLabel = (): ILabelStore => {
export const useLabel = (): ILabelRootStore => {
const context = useContext(MobxStoreContext);
if (context === undefined) throw new Error("useMobxStore must be used within MobxStoreProvider");
return context.label;
return context.labelRoot;
};

View File

@ -3,6 +3,7 @@ import { useRouter } from "next/router";
import { observer } from "mobx-react-lite";
import useSWR from "swr";
// hooks
import { useApplication, useCycle, useLabel, useModule, useProjectState, useUser } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
// components
import { Spinner } from "@plane/ui";
@ -10,7 +11,6 @@ import { JoinProject } from "components/auth-screens";
import { EmptyState } from "components/common";
// images
import emptyProject from "public/empty-state/project.svg";
import { useApplication, useCycle, useModule, useProjectState, useUser } from "hooks/store";
interface IProjectAuthWrapper {
children: ReactNode;
@ -21,7 +21,6 @@ export const ProjectAuthWrapper: FC<IProjectAuthWrapper> = observer((props) => {
// store
const {
project: { fetchProjectDetails, workspaceProjects },
projectLabel: { fetchProjectLabels },
projectMember: { fetchProjectMembers },
projectEstimates: { fetchProjectEstimates },
projectViews: { fetchAllViews },
@ -36,6 +35,9 @@ export const ProjectAuthWrapper: FC<IProjectAuthWrapper> = observer((props) => {
const { fetchAllCycles } = useCycle();
const { fetchModules } = useModule();
const { fetchProjectStates } = useProjectState();
const {
project: { fetchProjectLabels },
} = useLabel();
// router
const router = useRouter();
const { workspaceSlug, projectId } = router.query;

View File

@ -4,7 +4,7 @@ import Link from "next/link";
import useSWR from "swr";
import { observer } from "mobx-react-lite";
// hooks
import { useProject, useUser } from "hooks/store";
import { useLabel, useProject, useUser } from "hooks/store";
import { useMobxStore } from "lib/mobx/store-provider";
// icons
import { Button, Spinner } from "@plane/ui";
@ -17,13 +17,15 @@ export const WorkspaceAuthWrapper: FC<IWorkspaceAuthWrapper> = observer((props)
const { children } = props;
// store hooks
const {
workspace: { fetchWorkspaceLabels },
workspaceMember: { fetchWorkspaceMembers, fetchWorkspaceUserProjectsRole },
} = useMobxStore();
const {
membership: { currentWorkspaceMemberInfo, hasPermissionToCurrentWorkspace, fetchUserWorkspaceInfo },
} = useUser();
const { fetchProjects } = useProject();
const {
workspace: { fetchWorkspaceLabels },
} = useLabel();
// router
const router = useRouter();
const { workspaceSlug } = router.query;

47
web/store/label/index.ts Normal file
View File

@ -0,0 +1,47 @@
import { computed, observable, makeObservable } from "mobx";
import { RootStore } from "../root.store";
// types
import { IIssueLabel } from "types";
import { IProjectLabelStore, ProjectLabelStore } from "./project-label.store";
import { IWorkspaceLabelStore, WorkspaceLabelStore } from "./workspace-label.store";
export interface ILabelRootStore {
// observables
labelMap: Record<string, IIssueLabel>;
// computed actions
getLabelById: (labelId: string) => IIssueLabel | null;
// sub-stores
project: IProjectLabelStore;
workspace: IWorkspaceLabelStore;
}
export class LabelRootStore implements ILabelRootStore {
// observables
labelMap: Record<string, IIssueLabel> = {};
// root store
rootStore;
// sub-stores
project: IProjectLabelStore;
workspace: IWorkspaceLabelStore;
constructor(_rootStore: RootStore) {
makeObservable(this, {
// observables
labelMap: observable,
// computed actions
getLabelById: computed,
});
// root store
this.rootStore = _rootStore;
// sub-stores
this.project = new ProjectLabelStore(_rootStore);
this.workspace = new WorkspaceLabelStore(_rootStore);
}
/**
* get label info from the map of labels in the store using label id
* @param labelId
*/
getLabelById = (labelId: string): IIssueLabel | null => this.labelMap?.[labelId] || null;
}

View File

@ -0,0 +1,231 @@
import { action, computed, makeObservable, runInAction } from "mobx";
import { set } from "lodash";
// services
import { IssueLabelService } from "services/issue";
// helpers
import { buildTree } from "helpers/array.helper";
// types
import { RootStore } from "store/root.store";
import { IIssueLabel } from "types";
export interface IProjectLabelStore {
// computed
projectLabels: IIssueLabel[] | undefined;
projectLabelsTree: IIssueLabel[] | undefined;
// actions
fetchProjectLabels: (workspaceSlug: string, projectId: string) => Promise<IIssueLabel[]>;
createLabel: (workspaceSlug: string, projectId: string, data: Partial<IIssueLabel>) => Promise<IIssueLabel>;
updateLabel: (
workspaceSlug: string,
projectId: string,
labelId: string,
data: Partial<IIssueLabel>
) => Promise<IIssueLabel>;
updateLabelPosition: (
workspaceSlug: string,
projectId: string,
labelId: string,
parentId: string | null | undefined,
index: number,
isSameParent: boolean,
prevIndex: number | undefined
) => Promise<IIssueLabel | undefined>;
deleteLabel: (workspaceSlug: string, projectId: string, labelId: string) => Promise<void>;
}
export class ProjectLabelStore implements IProjectLabelStore {
// root store
rootStore;
// root store labelMap
labelMap: Record<string, IIssueLabel> = {};
// services
issueLabelService;
constructor(_rootStore: RootStore) {
makeObservable(this, {
// computed
projectLabels: computed,
projectLabelsTree: computed,
// actions
fetchProjectLabels: action,
createLabel: action,
updateLabel: action,
updateLabelPosition: action,
deleteLabel: action,
});
// root store
this.rootStore = _rootStore;
this.labelMap = this.rootStore.labelRoot.labelMap;
// services
this.issueLabelService = new IssueLabelService();
}
/**
* Returns the labelMap belongs to a specific project
*/
get projectLabels() {
const projectId = this.rootStore.app.router.query?.projectId;
if (!projectId) return;
return Object.values(this.labelMap).filter((label) => label.project === projectId);
}
/**
* Returns the labelMap in a tree format
*/
get projectLabelsTree() {
if (!this.projectLabels) return;
return buildTree(this.projectLabels);
}
/**
* Fetches all the labelMap belongs to a specific project
* @param workspaceSlug
* @param projectId
* @returns Promise<IIssueLabel[]>
*/
fetchProjectLabels = async (workspaceSlug: string, projectId: string) => {
const response = await this.issueLabelService.getProjectIssueLabels(workspaceSlug, projectId);
runInAction(() => {
response.forEach((label) => {
set(this.labelMap, [label.id], label);
});
});
return response;
};
/**
* Creates a new label for a specific project and add it to the store
* @param workspaceSlug
* @param projectId
* @param data
* @returns Promise<IIssueLabel>
*/
createLabel = async (workspaceSlug: string, projectId: string, data: Partial<IIssueLabel>) => {
const response = await this.issueLabelService.createIssueLabel(workspaceSlug, projectId, data);
runInAction(() => {
set(this.labelMap, [response.id], response);
});
return response;
};
/**
* Updates a label for a specific project and update it in the store
* @param workspaceSlug
* @param projectId
* @param labelId
* @param data
* @returns Promise<IIssueLabel>
*/
updateLabel = async (workspaceSlug: string, projectId: string, labelId: string, data: Partial<IIssueLabel>) => {
const originalLabel = this.labelMap[labelId];
try {
runInAction(() => {
set(this.labelMap, [labelId], { ...this.labelMap[labelId], ...data });
});
const response = await this.issueLabelService.patchIssueLabel(workspaceSlug, projectId, labelId, data);
return response;
} catch (error) {
console.log("Failed to update label from project store");
runInAction(() => {
set(this.labelMap, [labelId], originalLabel);
});
throw error;
}
};
/**
* updates the sort order of a label and updates the label information using API.
* @param workspaceSlug
* @param projectId
* @param labelId
* @param parentId
* @param index
* @param isSameParent
* @param prevIndex
* @returns
*/
updateLabelPosition = async (
workspaceSlug: string,
projectId: string,
labelId: string,
parentId: string | null | undefined,
index: number,
isSameParent: boolean,
prevIndex: number | undefined
) => {
const currLabel = this.labelMap?.[labelId];
const labelTree = this.projectLabelsTree;
let currentArray: IIssueLabel[];
if (!currLabel || !labelTree) return;
const data: Partial<IIssueLabel> = { parent: parentId };
//find array in which the label is to be added
if (!parentId) currentArray = labelTree;
else currentArray = labelTree?.find((label) => label.id === parentId)?.children || [];
//Add the array at the destination
if (isSameParent && prevIndex !== undefined) currentArray.splice(prevIndex, 1);
currentArray.splice(index, 0, currLabel);
//if currently adding to a new array, then let backend assign a sort order
if (currentArray.length > 1) {
let prevSortOrder: number | undefined, nextSortOrder: number | undefined;
if (typeof currentArray[index - 1] !== "undefined") {
prevSortOrder = currentArray[index - 1].sort_order;
}
if (typeof currentArray[index + 1] !== "undefined") {
nextSortOrder = currentArray[index + 1].sort_order;
}
let sortOrder: number;
//based on the next and previous labelMap calculate current sort order
if (prevSortOrder && nextSortOrder) {
sortOrder = (prevSortOrder + nextSortOrder) / 2;
} else if (nextSortOrder) {
sortOrder = nextSortOrder + 10000;
} else {
sortOrder = prevSortOrder! / 2;
}
data.sort_order = sortOrder;
}
return this.updateLabel(workspaceSlug, projectId, labelId, data);
};
/**
* Delete the label from the project and remove it from the labelMap object
* @param workspaceSlug
* @param projectId
* @param labelId
*/
deleteLabel = async (workspaceSlug: string, projectId: string, labelId: string) => {
const originalLabel = this.labelMap[labelId];
try {
if (!this.labelMap[labelId]) return;
runInAction(() => {
delete this.labelMap[labelId];
});
// deleting using api
await this.issueLabelService.deleteIssueLabel(workspaceSlug, projectId, labelId);
} catch (error) {
console.log("Failed to delete label from project store");
// reverting back to original label list
runInAction(() => {
set(this.labelMap, [labelId], originalLabel);
});
}
};
}

View File

@ -0,0 +1,63 @@
import { action, computed, makeObservable, runInAction } from "mobx";
import { set } from "lodash";
// services
import { IssueLabelService } from "services/issue";
// types
import { RootStore } from "store/root.store";
import { IIssueLabel } from "types";
export interface IWorkspaceLabelStore {
// computed
workspaceLabels: IIssueLabel[] | undefined;
// actions
fetchWorkspaceLabels: (workspaceSlug: string) => Promise<IIssueLabel[]>;
}
export class WorkspaceLabelStore implements IWorkspaceLabelStore {
// root store
rootStore;
// root store labelMap
labelMap: Record<string, IIssueLabel> = {};
// services
issueLabelService;
constructor(_rootStore: RootStore) {
makeObservable(this, {
// computed
workspaceLabels: computed,
// actions
fetchWorkspaceLabels: action,
});
// root store
this.rootStore = _rootStore;
this.labelMap = this.rootStore.labelRoot.labelMap;
// services
this.issueLabelService = new IssueLabelService();
}
/**
* Returns the labelMap belongs to a specific workspace
*/
get workspaceLabels() {
const currentWorkspaceDetails = this.rootStore.workspaceRoot.currentWorkspace;
if (!currentWorkspaceDetails) return;
return Object.values(this.labelMap).filter((label) => label.workspace === currentWorkspaceDetails.id);
}
/**
* Fetches all the labelMap belongs to a specific project
* @param workspaceSlug
* @param projectId
* @returns Promise<IIssueLabel[]>
*/
fetchWorkspaceLabels = async (workspaceSlug: string) => {
const response = await this.issueLabelService.getWorkspaceIssueLabels(workspaceSlug);
runInAction(() => {
response.forEach((label) => {
set(this.labelMap, [label.id], label);
});
});
return response;
};
}

View File

@ -8,22 +8,20 @@ import { IssueLabelService, IssueService } from "services/issue";
import { ProjectService, ProjectStateService } from "services/project";
export interface IProjectStore {
// states
loader: boolean;
error: any | null;
// observables
searchQuery: string;
projectId: string | null;
projectMap: {
[projectId: string]: IProject; // projectId: project Info
};
// computed
searchedProjects: string[];
workspaceProjects: string[] | null;
joinedProjects: string[];
favoriteProjects: string[];
currentProjectDetails: IProject | undefined;
// actions
setSearchQuery: (query: string) => void;
getProjectById: (projectId: string) => IProject | null;
@ -43,15 +41,14 @@ export interface IProjectStore {
}
export class ProjectStore implements IProjectStore {
// states
loader: boolean = false;
error: any | null = null;
projectId: string | null = null;
// observables
searchQuery: string = "";
projectMap: {
[projectId: string]: IProject; // projectId: project Info
} = {};
// root store
rootStore: RootStore;
// service
@ -62,24 +59,19 @@ export class ProjectStore implements IProjectStore {
constructor(_rootStore: RootStore) {
makeObservable(this, {
// observable
// states
loader: observable.ref,
error: observable.ref,
// observables
searchQuery: observable.ref,
projectId: observable.ref,
projectMap: observable,
// computed
searchedProjects: computed,
workspaceProjects: computed,
currentProjectDetails: computed,
joinedProjects: computed,
favoriteProjects: computed,
// action
// actions
setSearchQuery: action,
fetchProjects: action,
fetchProjectDetails: action,

View File

@ -6,11 +6,11 @@ import { CycleStore, ICycleStore } from "./cycle.store";
import { IProjectViewsStore, ProjectViewsStore } from "./project-view.store";
import { IModuleStore, ModulesStore } from "./module.store";
import { IUserStore, UserStore } from "./user";
import { ILabelStore, LabelStore } from "./label.store";
import { IWorkspaceRootStore, WorkspaceRootStore } from "./workspace";
import { IssueRootStore, IIssueRootStore } from "./issue/root.store";
import { IStateStore, StateStore } from "./state.store";
import { IPageStore, PageStore } from "./page.store";
import { ILabelRootStore, LabelRootStore } from "./label";
enableStaticRendering(typeof window === "undefined");
@ -19,11 +19,11 @@ export class RootStore {
user: IUserStore;
workspaceRoot: IWorkspaceRootStore;
projectRoot: IProjectRootStore;
labelRoot: ILabelRootStore;
cycle: ICycleStore;
module: IModuleStore;
projectView: IProjectViewsStore;
page: IPageStore;
label: ILabelStore;
issue: IIssueRootStore;
state: IStateStore;
@ -32,8 +32,8 @@ export class RootStore {
this.user = new UserStore(this);
this.workspaceRoot = new WorkspaceRootStore(this);
this.projectRoot = new ProjectRootStore(this);
this.labelRoot = new LabelRootStore(this);
// independent stores
this.label = new LabelStore(this);
this.state = new StateStore(this);
this.issue = new IssueRootStore(this);
this.cycle = new CycleStore(this);

View File

@ -42,8 +42,8 @@ export class WorkspaceRootStore implements IWorkspaceRootStore {
// root store
rootStore;
// sub-stores
webhook: WebhookStore;
apiToken: ApiTokenStore;
webhook: IWebhookStore;
apiToken: IApiTokenStore;
constructor(_rootStore: RootStore) {
makeObservable(this, {