From bdacdc32a91817ed6137db936c12faa96fde3980 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Thu, 14 Dec 2023 17:26:16 +0530 Subject: [PATCH] chore: implemented hooks for all the created stores --- .../account/deactivate-account-modal.tsx | 8 +- .../account/sign-in-forms/o-auth-options.tsx | 7 +- web/components/account/sign-in-forms/root.tsx | 7 +- .../analytics/custom-analytics/select-bar.tsx | 19 +- .../custom-analytics/select/project.tsx | 44 +-- .../sidebar/projects-list.tsx | 95 +++--- .../sidebar/sidebar-header.tsx | 20 +- .../custom-analytics/sidebar/sidebar.tsx | 303 +++++++++--------- .../auth-screens/not-authorized-view.tsx | 19 +- .../auth-screens/project/join-project.tsx | 22 +- .../automation/auto-archive-automation.tsx | 32 +- .../automation/auto-close-automation.tsx | 43 +-- .../command-palette/actions/help-actions.tsx | 6 +- .../actions/issue-actions/actions-list.tsx | 8 +- .../actions/project-actions.tsx | 8 +- .../command-palette/actions/theme-actions.tsx | 6 +- .../command-palette/command-modal.tsx | 8 +- ...mmand-pallette.tsx => command-palette.tsx} | 13 +- web/components/command-palette/index.ts | 2 +- web/components/core/image-picker-popover.tsx | 19 +- .../core/modals/user-image-upload-modal.tsx | 12 +- .../modals/workspace-image-upload-modal.tsx | 10 +- .../core/theme/custom-theme-selector.tsx | 10 +- .../cycles/active-cycle-details.tsx | 41 +-- web/components/cycles/cycles-board.tsx | 8 +- web/components/cycles/cycles-list.tsx | 10 +- .../cycles/gantt-chart/cycles-list-layout.tsx | 29 +- .../estimates/estimate-list-item.tsx | 11 +- web/components/exporter/export-modal.tsx | 50 +-- web/components/headers/cycles.tsx | 21 +- web/components/headers/modules-list.tsx | 14 +- web/components/headers/page-details.tsx | 19 +- web/components/headers/pages.tsx | 14 +- .../project-archived-issue-details.tsx | 9 +- web/components/headers/project-inbox.tsx | 12 +- .../headers/project-issue-details.tsx | 13 +- web/components/headers/project-settings.tsx | 15 +- web/components/headers/project-views.tsx | 14 +- web/components/headers/projects.tsx | 26 +- web/components/inbox/issue-activity.tsx | 33 +- web/components/instance/ai-form.tsx | 8 +- web/components/instance/email-form.tsx | 7 +- web/components/instance/general-form.tsx | 6 +- .../instance/github-config-form.tsx | 10 +- .../instance/google-config-form.tsx | 10 +- web/components/instance/help-section.tsx | 8 +- web/components/instance/image-config-form.tsx | 10 +- web/components/instance/setup-done-view.tsx | 7 +- .../instance/setup-form/sign-in-form.tsx | 12 +- web/components/instance/setup-view.tsx | 8 +- web/components/instance/sidebar-dropdown.tsx | 13 +- web/components/instance/sidebar-menu.tsx | 8 +- web/components/integration/github/auth.tsx | 9 +- .../integration/github/import-data.tsx | 36 +-- .../integration/jira/give-details.tsx | 41 +-- .../integration/single-integration-card.tsx | 27 +- .../integration/slack/select-channel.tsx | 13 +- .../issues/attachment/attachment-upload.tsx | 15 +- .../issues/comment/comment-card.tsx | 32 +- .../issues/comment/comment-reaction.tsx | 26 +- .../issues/delete-draft-issue-modal.tsx | 20 +- web/components/issues/draft-issue-form.tsx | 23 +- web/components/issues/form.tsx | 25 +- .../calendar/quick-add-issue-form.tsx | 30 +- .../empty-states/global-view.tsx | 29 +- .../empty-states/project-view.tsx | 9 +- .../issue-layouts/empty-states/project.tsx | 9 +- .../issue-layouts/gantt/base-gantt-root.tsx | 12 +- .../gantt/quick-add-issue-form.tsx | 25 +- .../issue-layouts/kanban/base-kanban-root.tsx | 18 +- .../kanban/quick-add-issue-form.tsx | 26 +- .../list/quick-add-issue-form.tsx | 30 +- .../issues/issue-layouts/properties/state.tsx | 32 +- .../quick-action-dropdowns/project-issue.tsx | 14 +- .../spreadsheet/columns/columns-list.tsx | 9 +- .../spreadsheet/quick-add-issue-form.tsx | 37 +-- web/components/issues/issue-reaction.tsx | 8 +- .../issues/peek-overview/issue-detail.tsx | 8 +- web/components/issues/select/module.tsx | 52 +-- web/components/issues/select/project.tsx | 47 +-- web/components/issues/select/state.tsx | 24 +- .../issues/sidebar-select/blocked.tsx | 26 +- .../issues/sidebar-select/blocker.tsx | 27 +- .../issues/sidebar-select/duplicate.tsx | 29 +- .../issues/sidebar-select/relates-to.tsx | 30 +- .../modules/delete-module-modal.tsx | 21 +- web/components/modules/modal.tsx | 45 ++- web/components/modules/module-card-item.tsx | 110 +++---- web/components/modules/module-list-item.tsx | 89 ++--- .../modules/module-peek-overview.tsx | 14 +- web/components/modules/modules-list-view.tsx | 23 +- web/components/modules/sidebar.tsx | 35 +- .../notifications/notification-popover.tsx | 6 +- web/components/onboarding/invitations.tsx | 27 +- web/components/onboarding/join-workspaces.tsx | 16 +- .../onboarding/onboarding-sidebar.tsx | 18 +- .../switch-delete-account-modal.tsx | 12 +- web/components/onboarding/tour/root.tsx | 18 +- web/components/onboarding/user-details.tsx | 18 +- web/components/onboarding/workspace.tsx | 25 +- web/components/page-views/signin.tsx | 11 +- .../page-views/workspace-dashboard.tsx | 38 +-- .../pages/create-update-block-inline.tsx | 8 +- .../pages/create-update-page-modal.tsx | 23 +- web/components/pages/delete-page-modal.tsx | 18 +- .../pages/pages-list/all-pages-list.tsx | 8 +- .../pages/pages-list/archived-pages-list.tsx | 6 +- .../pages/pages-list/favorite-pages-list.tsx | 6 +- web/components/pages/pages-list/list-item.tsx | 92 +++--- web/components/pages/pages-list/list-view.tsx | 25 +- .../pages/pages-list/private-page-list.tsx | 8 +- .../pages/pages-list/recent-pages-list.tsx | 12 +- .../pages/pages-list/shared-pages-list.tsx | 10 +- web/components/profile/sidebar.tsx | 23 +- web/components/project/card-list.tsx | 38 +-- web/components/project/card.tsx | 24 +- .../project/confirm-project-member-remove.tsx | 16 +- .../project/create-project-modal.tsx | 32 +- .../project/delete-project-modal.tsx | 27 +- web/components/project/form.tsx | 33 +- web/components/project/join-project-modal.tsx | 15 +- .../project/leave-project-modal.tsx | 17 +- .../project-settings-member-defaults.tsx | 29 +- .../project/publish-project/modal.tsx | 91 +++--- .../project/settings/features-list.tsx | 21 +- web/components/project/sidebar-list-item.tsx | 19 +- web/components/project/sidebar-list.tsx | 13 +- web/components/states/create-state-modal.tsx | 18 +- .../states/create-update-state-inline.tsx | 25 +- web/components/states/delete-state-modal.tsx | 25 +- .../project-setting-state-list-item.tsx | 20 +- .../states/project-setting-state-list.tsx | 10 +- .../web-hooks/delete-webhook-modal.tsx | 9 +- web/components/web-hooks/form/form.tsx | 17 +- web/components/web-hooks/form/secret-key.tsx | 17 +- .../web-hooks/webhooks-list-item.tsx | 10 +- web/components/web-hooks/webhooks-list.tsx | 9 +- .../confirm-workspace-member-remove.tsx | 16 +- .../workspace/create-workspace-form.tsx | 22 +- .../workspace/delete-workspace-modal.tsx | 20 +- web/components/workspace/help-section.tsx | 7 +- .../send-workspace-invitation-modal.tsx | 8 +- .../workspace/settings/workspace-details.tsx | 18 +- web/components/workspace/sidebar-dropdown.tsx | 25 +- web/components/workspace/sidebar-menu.tsx | 13 +- .../workspace/sidebar-quick-action.tsx | 15 +- web/contexts/profile-issues-context.tsx | 191 ----------- web/contexts/workspace-member.context.tsx | 64 ---- web/hooks/store/index.ts | 11 + web/hooks/store/use-application.ts | 11 + web/hooks/store/use-cycle.ts | 11 + web/hooks/store/use-label.ts | 11 + web/hooks/store/use-module.ts | 11 + web/hooks/store/use-page.ts | 11 + web/hooks/store/use-project-publish.ts | 11 + web/hooks/store/use-project-state.ts | 11 + web/hooks/store/use-project.ts | 11 + web/hooks/store/use-user.ts | 11 + web/hooks/store/use-webhook.ts | 11 + web/hooks/store/use-workspace.ts | 11 + web/hooks/use-page.tsx | 8 - web/hooks/use-sign-in-redirection.ts | 8 +- web/hooks/use-user-auth.tsx | 27 +- web/layouts/admin-layout/layout.tsx | 8 +- web/layouts/admin-layout/sidebar.tsx | 12 +- web/layouts/app-layout/sidebar.tsx | 8 +- web/layouts/auth-layout/admin-wrapper.tsx | 12 +- web/layouts/auth-layout/user-wrapper.tsx | 24 +- web/layouts/auth-layout/workspace-wrapper.tsx | 14 +- web/layouts/instance-layout/index.tsx | 11 +- .../settings-layout/profile/sidebar.tsx | 23 +- .../settings-layout/project/layout.tsx | 17 +- .../settings-layout/workspace/sidebar.tsx | 8 +- web/layouts/user-profile-layout/layout.tsx | 10 +- web/lib/app-provider.tsx | 15 +- web/lib/mobx/store-provider.tsx | 2 +- web/lib/mobx_legacy/store-provider.tsx | 28 ++ web/lib/wrappers/store-wrapper.tsx | 60 +--- web/lib/wrappers/store-wrapper_legacy.tsx | 101 ++++++ web/pages/[workspaceSlug]/analytics.tsx | 12 +- .../[projectId]/modules/[moduleId].tsx | 9 +- .../projects/[projectId]/pages/[pageId].tsx | 55 ++-- .../projects/[projectId]/pages/index.tsx | 161 +++++----- .../[projectId]/settings/automations.tsx | 16 +- .../[projectId]/settings/estimates.tsx | 9 +- .../[projectId]/settings/features.tsx | 8 +- .../projects/[projectId]/settings/index.tsx | 15 +- .../[workspaceSlug]/settings/api-tokens.tsx | 10 +- .../[workspaceSlug]/settings/billing.tsx | 9 +- .../[workspaceSlug]/settings/exports.tsx | 9 +- .../[workspaceSlug]/settings/imports.tsx | 9 +- .../[workspaceSlug]/settings/integrations.tsx | 10 +- .../[workspaceSlug]/settings/members.tsx | 27 +- .../settings/webhooks/[webhookId].tsx | 10 +- .../settings/webhooks/create.tsx | 8 +- .../settings/webhooks/index.tsx | 10 +- web/pages/create-workspace.tsx | 10 +- web/pages/god-mode/ai.tsx | 6 +- web/pages/god-mode/authorization.tsx | 6 +- web/pages/god-mode/email.tsx | 6 +- web/pages/god-mode/image.tsx | 6 +- web/pages/god-mode/index.tsx | 8 +- web/pages/invitations/index.tsx | 29 +- web/pages/onboarding/index.tsx | 39 ++- web/pages/profile/change-password.tsx | 8 +- web/pages/profile/preferences.tsx | 7 +- web/pages/workspace-invitations/index.tsx | 20 +- web/services/module.service.ts | 2 +- web/services/page.service.ts | 4 +- web/services/project/project.service.ts | 2 +- web/store/application/index.ts | 10 +- web/store/module.store.ts | 107 +++---- web/store/page.store.ts | 249 ++++++++------ web/store/project/index.ts | 8 +- .../{projects.store.ts => project.store.ts} | 76 ++--- web/store/root.store.ts | 24 +- web/store/state.store.ts | 27 +- web/store/workspace/index.ts | 5 +- web/types/pages.d.ts | 10 +- 219 files changed, 2573 insertions(+), 2675 deletions(-) rename web/components/command-palette/{command-pallette.tsx => command-palette.tsx} (97%) delete mode 100644 web/contexts/profile-issues-context.tsx delete mode 100644 web/contexts/workspace-member.context.tsx create mode 100644 web/hooks/store/index.ts create mode 100644 web/hooks/store/use-application.ts create mode 100644 web/hooks/store/use-cycle.ts create mode 100644 web/hooks/store/use-label.ts create mode 100644 web/hooks/store/use-module.ts create mode 100644 web/hooks/store/use-page.ts create mode 100644 web/hooks/store/use-project-publish.ts create mode 100644 web/hooks/store/use-project-state.ts create mode 100644 web/hooks/store/use-project.ts create mode 100644 web/hooks/store/use-user.ts create mode 100644 web/hooks/store/use-webhook.ts create mode 100644 web/hooks/store/use-workspace.ts delete mode 100644 web/hooks/use-page.tsx create mode 100644 web/lib/mobx_legacy/store-provider.tsx create mode 100644 web/lib/wrappers/store-wrapper_legacy.tsx rename web/store/project/{projects.store.ts => project.store.ts} (72%) diff --git a/web/components/account/deactivate-account-modal.tsx b/web/components/account/deactivate-account-modal.tsx index 53ac1df50..307a65ad2 100644 --- a/web/components/account/deactivate-account-modal.tsx +++ b/web/components/account/deactivate-account-modal.tsx @@ -4,8 +4,8 @@ import { useTheme } from "next-themes"; import { Dialog, Transition } from "@headlessui/react"; import { Trash2 } from "lucide-react"; import { mutate } from "swr"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useUser } from "hooks/store"; // ui import { Button } from "@plane/ui"; // hooks @@ -22,9 +22,7 @@ export const DeactivateAccountModal: React.FC = (props) => { // states const [isDeactivating, setIsDeactivating] = useState(false); - const { - user: { deactivateAccount }, - } = useMobxStore(); + const { deactivateAccount } = useUser(); const router = useRouter(); diff --git a/web/components/account/sign-in-forms/o-auth-options.tsx b/web/components/account/sign-in-forms/o-auth-options.tsx index aec82cfa5..9ed4e7e5f 100644 --- a/web/components/account/sign-in-forms/o-auth-options.tsx +++ b/web/components/account/sign-in-forms/o-auth-options.tsx @@ -1,9 +1,8 @@ import { observer } from "mobx-react-lite"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; // services import { AuthService } from "services/auth.service"; // hooks +import { useApplication } from "hooks/store"; import useToast from "hooks/use-toast"; // components import { GitHubSignInButton, GoogleSignInButton } from "components/account"; @@ -21,8 +20,8 @@ export const OAuthOptions: React.FC = observer((props) => { const { setToastAlert } = useToast(); // mobx store const { - appConfig: { envConfig }, - } = useMobxStore(); + config: { envConfig }, + } = useApplication(); const handleGoogleSignIn = async ({ clientId, credential }: any) => { try { diff --git a/web/components/account/sign-in-forms/root.tsx b/web/components/account/sign-in-forms/root.tsx index f7ec6b593..616f4809f 100644 --- a/web/components/account/sign-in-forms/root.tsx +++ b/web/components/account/sign-in-forms/root.tsx @@ -1,8 +1,7 @@ import React, { useState } from "react"; import { observer } from "mobx-react-lite"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; // hooks +import { useApplication } from "hooks/store"; import useSignInRedirection from "hooks/use-sign-in-redirection"; // components import { LatestFeatureBlock } from "components/common"; @@ -38,8 +37,8 @@ export const SignInRoot = observer(() => { const { handleRedirection } = useSignInRedirection(); // mobx store const { - appConfig: { envConfig }, - } = useMobxStore(); + config: { envConfig }, + } = useApplication(); const isOAuthEnabled = envConfig && (envConfig.google_client_id || envConfig.github_client_id); diff --git a/web/components/analytics/custom-analytics/select-bar.tsx b/web/components/analytics/custom-analytics/select-bar.tsx index f3d7a9993..91cd4a3e7 100644 --- a/web/components/analytics/custom-analytics/select-bar.tsx +++ b/web/components/analytics/custom-analytics/select-bar.tsx @@ -1,9 +1,7 @@ -import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { Control, Controller, UseFormSetValue } from "react-hook-form"; - -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useProject } from "hooks/store"; // components import { SelectProject, SelectSegment, SelectXAxis, SelectYAxis } from "components/analytics"; // types @@ -20,12 +18,7 @@ type Props = { export const CustomAnalyticsSelectBar: React.FC = observer((props) => { const { control, setValue, params, fullScreen, isProjectLevel } = props; - const router = useRouter(); - const { workspaceSlug } = router.query; - - const { project: projectStore } = useMobxStore(); - - const projectsList = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : null; + const { workspaceProjects } = useProject(); return (
= observer((props) => { name="project" control={control} render={({ field: { value, onChange } }) => ( - + )} />
diff --git a/web/components/analytics/custom-analytics/select/project.tsx b/web/components/analytics/custom-analytics/select/project.tsx index 7251c5073..ee3dce6d6 100644 --- a/web/components/analytics/custom-analytics/select/project.tsx +++ b/web/components/analytics/custom-analytics/select/project.tsx @@ -1,25 +1,33 @@ +import { observer } from "mobx-react-lite"; +// hooks +import { useProject } from "hooks/store"; // ui import { CustomSearchSelect } from "@plane/ui"; -// types -import { IProject } from "types"; type Props = { value: string[] | undefined; onChange: (val: string[] | null) => void; - projects: IProject[] | undefined; + projectIds: string[] | undefined; }; -export const SelectProject: React.FC = ({ value, onChange, projects }) => { - const options = projects?.map((project) => ({ - value: project.id, - query: project.name + project.identifier, - content: ( -
- {project.identifier} - {project.name} -
- ), - })); +export const SelectProject: React.FC = observer((props) => { + const { value, onChange, projectIds } = props; + const { getProjectById } = useProject(); + + const options = projectIds?.map((projectId) => { + const projectDetails = getProjectById(projectId); + + return { + value: projectDetails?.id, + query: `${projectDetails?.name} ${projectDetails?.identifier}`, + content: ( +
+ {projectDetails?.identifier} + {projectDetails?.name} +
+ ), + }; + }); return ( = ({ value, onChange, projects }) => options={options} label={ value && value.length > 0 - ? projects - ?.filter((p) => value.includes(p.id)) - .map((p) => p.identifier) + ? projectIds + ?.filter((p) => value.includes(p)) + .map((p) => getProjectById(p)?.name) .join(", ") : "All projects" } @@ -38,4 +46,4 @@ export const SelectProject: React.FC = ({ value, onChange, projects }) => multiple /> ); -}; +}); diff --git a/web/components/analytics/custom-analytics/sidebar/projects-list.tsx b/web/components/analytics/custom-analytics/sidebar/projects-list.tsx index 41770eec8..d09e8def4 100644 --- a/web/components/analytics/custom-analytics/sidebar/projects-list.tsx +++ b/web/components/analytics/custom-analytics/sidebar/projects-list.tsx @@ -1,65 +1,74 @@ +import { observer } from "mobx-react-lite"; +// hooks +import { useProject } from "hooks/store"; // icons import { Contrast, LayoutGrid, Users } from "lucide-react"; // helpers import { renderEmoji } from "helpers/emoji.helper"; import { truncateText } from "helpers/string.helper"; -// types -import { IProject } from "types"; type Props = { - projects: IProject[]; + projectIds: string[]; }; -export const CustomAnalyticsSidebarProjectsList: React.FC = (props) => { - const { projects } = props; +export const CustomAnalyticsSidebarProjectsList: React.FC = observer((props) => { + const { projectIds } = props; + + const { getProjectById } = useProject(); return (

Selected Projects

- {projects.map((project) => ( -
-
- {project.emoji ? ( - {renderEmoji(project.emoji)} - ) : project.icon_prop ? ( -
{renderEmoji(project.icon_prop)}
- ) : ( - - {project?.name.charAt(0)} - - )} -
-

{truncateText(project.name, 20)}

- ({project.identifier}) -
-
-
-
-
- -
Total members
-
- {project.total_members} + {projectIds.map((projectId) => { + const project = getProjectById(projectId); + + if (!project) return; + + return ( +
+
+ {project.emoji ? ( + {renderEmoji(project.emoji)} + ) : project.icon_prop ? ( +
{renderEmoji(project.icon_prop)}
+ ) : ( + + {project?.name.charAt(0)} + + )} +
+

{truncateText(project.name, 20)}

+ ({project.identifier}) +
-
-
- -
Total cycles
+
+
+
+ +
Total members
+
+ {project.total_members}
- {project.total_cycles} -
-
-
- -
Total modules
+
+
+ +
Total cycles
+
+ {project.total_cycles} +
+
+
+ +
Total modules
+
+ {project.total_modules}
- {project.total_modules}
-
- ))} + ); + })}
); -}; +}); diff --git a/web/components/analytics/custom-analytics/sidebar/sidebar-header.tsx b/web/components/analytics/custom-analytics/sidebar/sidebar-header.tsx index 2eaaac7fb..df982449e 100644 --- a/web/components/analytics/custom-analytics/sidebar/sidebar-header.tsx +++ b/web/components/analytics/custom-analytics/sidebar/sidebar-header.tsx @@ -1,8 +1,7 @@ import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; - -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useCycle, useModule, useProject } from "hooks/store"; // helpers import { renderEmoji } from "helpers/emoji.helper"; import { renderShortDate } from "helpers/date-time.helper"; @@ -11,16 +10,15 @@ import { NETWORK_CHOICES } from "constants/project"; export const CustomAnalyticsSidebarHeader = observer(() => { const router = useRouter(); - const { workspaceSlug, projectId, cycleId, moduleId } = router.query; + const { projectId, cycleId, moduleId } = router.query; - const { cycle: cycleStore, module: moduleStore, project: projectStore } = useMobxStore(); + const { getProjectById } = useProject(); + const { getCycleById } = useCycle(); + const { getModuleById } = useModule(); - const cycleDetails = cycleId ? cycleStore.getCycleById(cycleId.toString()) : undefined; - const moduleDetails = moduleId ? moduleStore.getModuleById(moduleId.toString()) : undefined; - const projectDetails = - workspaceSlug && projectId - ? projectStore.getProjectById(workspaceSlug.toString(), projectId.toString()) - : undefined; + const cycleDetails = cycleId ? getCycleById(cycleId.toString()) : undefined; + const moduleDetails = moduleId ? getModuleById(moduleId.toString()) : undefined; + const projectDetails = projectId ? getProjectById(projectId.toString()) : undefined; return ( <> diff --git a/web/components/analytics/custom-analytics/sidebar/sidebar.tsx b/web/components/analytics/custom-analytics/sidebar/sidebar.tsx index 7d1a6a3eb..4177bd156 100644 --- a/web/components/analytics/custom-analytics/sidebar/sidebar.tsx +++ b/web/components/analytics/custom-analytics/sidebar/sidebar.tsx @@ -5,8 +5,8 @@ import { mutate } from "swr"; // services import { AnalyticsService } from "services/analytics.service"; // hooks +import { useCycle, useModule, useProject, useUser } from "hooks/store"; import useToast from "hooks/use-toast"; -import { useMobxStore } from "lib/mobx/store-provider"; // components import { CustomAnalyticsSidebarHeader, CustomAnalyticsSidebarProjectsList } from "components/analytics"; // ui @@ -29,172 +29,167 @@ type Props = { const analyticsService = new AnalyticsService(); -export const CustomAnalyticsSidebar: React.FC = observer( - ({ analytics, params, fullScreen, isProjectLevel = false }) => { - const router = useRouter(); - const { workspaceSlug, projectId, cycleId, moduleId } = router.query; +export const CustomAnalyticsSidebar: React.FC = observer((props) => { + const { analytics, params, fullScreen, isProjectLevel = false } = props; + // router + const router = useRouter(); + const { workspaceSlug, projectId, cycleId, moduleId } = router.query; + // toast alert + const { setToastAlert } = useToast(); + // store hooks + const { currentUser } = useUser(); + const { workspaceProjects, getProjectById } = useProject(); + const { fetchCycleDetails, getCycleById } = useCycle(); + const { fetchModuleDetails, getModuleById } = useModule(); - const { setToastAlert } = useToast(); + const projectDetails = projectId ? getProjectById(projectId.toString()) ?? undefined : undefined; - const { user: userStore, project: projectStore, cycle: cycleStore, module: moduleStore } = useMobxStore(); + const trackExportAnalytics = () => { + if (!currentUser) return; - const user = userStore.currentUser; - - const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : undefined; - const projectDetails = - workspaceSlug && projectId - ? projectStore.getProjectById(workspaceSlug.toString(), projectId.toString()) ?? undefined - : undefined; - - const trackExportAnalytics = () => { - if (!user) return; - - const eventPayload: any = { - workspaceSlug: workspaceSlug?.toString(), - params: { - x_axis: params.x_axis, - y_axis: params.y_axis, - group: params.segment, - project: params.project, - }, - }; - - if (projectDetails) { - const workspaceDetails = projectDetails.workspace as IWorkspace; - - eventPayload.workspaceId = workspaceDetails.id; - eventPayload.workspaceName = workspaceDetails.name; - eventPayload.projectId = projectDetails.id; - eventPayload.projectIdentifier = projectDetails.identifier; - eventPayload.projectName = projectDetails.name; - } - - if (cycleDetails || moduleDetails) { - const details = cycleDetails || moduleDetails; - - eventPayload.workspaceId = details?.workspace_detail?.id; - eventPayload.workspaceName = details?.workspace_detail?.name; - eventPayload.projectId = details?.project_detail.id; - eventPayload.projectIdentifier = details?.project_detail.identifier; - eventPayload.projectName = details?.project_detail.name; - } - - if (cycleDetails) { - eventPayload.cycleId = cycleDetails.id; - eventPayload.cycleName = cycleDetails.name; - } - - if (moduleDetails) { - eventPayload.moduleId = moduleDetails.id; - eventPayload.moduleName = moduleDetails.name; - } - }; - - const exportAnalytics = () => { - if (!workspaceSlug) return; - - const data: IExportAnalyticsFormData = { + const eventPayload: any = { + workspaceSlug: workspaceSlug?.toString(), + params: { x_axis: params.x_axis, y_axis: params.y_axis, - }; - - if (params.segment) data.segment = params.segment; - if (params.project) data.project = params.project; - - analyticsService - .exportAnalytics(workspaceSlug.toString(), data) - .then((res) => { - setToastAlert({ - type: "success", - title: "Success!", - message: res.message, - }); - - trackExportAnalytics(); - }) - .catch(() => - setToastAlert({ - type: "error", - title: "Error!", - message: "There was some error in exporting the analytics. Please try again.", - }) - ); + group: params.segment, + project: params.project, + }, }; - const cycleDetails = cycleId ? cycleStore.getCycleById(cycleId.toString()) : undefined; - const moduleDetails = moduleId ? moduleStore.getModuleById(moduleId.toString()) : undefined; + if (projectDetails) { + const workspaceDetails = projectDetails.workspace as IWorkspace; - // fetch cycle details - useEffect(() => { - if (!workspaceSlug || !projectId || !cycleId || cycleDetails) return; + eventPayload.workspaceId = workspaceDetails.id; + eventPayload.workspaceName = workspaceDetails.name; + eventPayload.projectId = projectDetails.id; + eventPayload.projectIdentifier = projectDetails.identifier; + eventPayload.projectName = projectDetails.name; + } - cycleStore.fetchCycleWithId(workspaceSlug.toString(), projectId.toString(), cycleId.toString()); - }, [cycleId, cycleDetails, cycleStore, projectId, workspaceSlug]); + if (cycleDetails || moduleDetails) { + const details = cycleDetails || moduleDetails; - // fetch module details - useEffect(() => { - if (!workspaceSlug || !projectId || !moduleId || moduleDetails) return; + eventPayload.workspaceId = details?.workspace_detail?.id; + eventPayload.workspaceName = details?.workspace_detail?.name; + eventPayload.projectId = details?.project_detail.id; + eventPayload.projectIdentifier = details?.project_detail.identifier; + eventPayload.projectName = details?.project_detail.name; + } - moduleStore.fetchModuleDetails(workspaceSlug.toString(), projectId.toString(), moduleId.toString()); - }, [moduleId, moduleDetails, moduleStore, projectId, workspaceSlug]); + if (cycleDetails) { + eventPayload.cycleId = cycleDetails.id; + eventPayload.cycleName = cycleDetails.name; + } - const selectedProjects = params.project && params.project.length > 0 ? params.project : projects?.map((p) => p.id); + if (moduleDetails) { + eventPayload.moduleId = moduleDetails.id; + eventPayload.moduleName = moduleDetails.name; + } + }; - return ( -
-
+ const exportAnalytics = () => { + if (!workspaceSlug) return; + + const data: IExportAnalyticsFormData = { + x_axis: params.x_axis, + y_axis: params.y_axis, + }; + + if (params.segment) data.segment = params.segment; + if (params.project) data.project = params.project; + + analyticsService + .exportAnalytics(workspaceSlug.toString(), data) + .then((res) => { + setToastAlert({ + type: "success", + title: "Success!", + message: res.message, + }); + + trackExportAnalytics(); + }) + .catch(() => + setToastAlert({ + type: "error", + title: "Error!", + message: "There was some error in exporting the analytics. Please try again.", + }) + ); + }; + + const cycleDetails = cycleId ? getCycleById(cycleId.toString()) : undefined; + const moduleDetails = moduleId ? getModuleById(moduleId.toString()) : undefined; + + // fetch cycle details + useEffect(() => { + if (!workspaceSlug || !projectId || !cycleId || cycleDetails) return; + + fetchCycleDetails(workspaceSlug.toString(), projectId.toString(), cycleId.toString()); + }, [cycleId, cycleDetails, fetchCycleDetails, projectId, workspaceSlug]); + + // fetch module details + useEffect(() => { + if (!workspaceSlug || !projectId || !moduleId || moduleDetails) return; + + fetchModuleDetails(workspaceSlug.toString(), projectId.toString(), moduleId.toString()); + }, [moduleId, moduleDetails, fetchModuleDetails, projectId, workspaceSlug]); + + const selectedProjects = params.project && params.project.length > 0 ? params.project : workspaceProjects; + + return ( +
+
+
+ + {analytics ? analytics.total : "..."} Issues +
+ {isProjectLevel && (
- - {analytics ? analytics.total : "..."} Issues + + {renderShortDate( + (cycleId + ? cycleDetails?.created_at + : moduleId + ? moduleDetails?.created_at + : projectDetails?.created_at) ?? "" + )}
- {isProjectLevel && ( -
- - {renderShortDate( - (cycleId - ? cycleDetails?.created_at - : moduleId - ? moduleDetails?.created_at - : projectDetails?.created_at) ?? "" - )} -
- )} -
-
- {fullScreen ? ( - <> - {!isProjectLevel && selectedProjects && selectedProjects.length > 0 && ( - selectedProjects.includes(p.id)) ?? []} - /> - )} - - - ) : null} -
-
- - -
+ )}
- ); - } -); +
+ {fullScreen ? ( + <> + {!isProjectLevel && selectedProjects && selectedProjects.length > 0 && ( + + )} + + + ) : null} +
+
+ + +
+
+ ); +}); diff --git a/web/components/auth-screens/not-authorized-view.tsx b/web/components/auth-screens/not-authorized-view.tsx index 353bb8fce..c504fd179 100644 --- a/web/components/auth-screens/not-authorized-view.tsx +++ b/web/components/auth-screens/not-authorized-view.tsx @@ -1,12 +1,12 @@ import React from "react"; -// next import Link from "next/link"; import Image from "next/image"; import { useRouter } from "next/router"; +import { observer } from "mobx-react-lite"; +// hooks +import { useUser } from "hooks/store"; // layouts import DefaultLayout from "layouts/default-layout"; -// hooks -import useUser from "hooks/use-user"; // images import ProjectNotAuthorizedImg from "public/auth/project-not-authorized.svg"; import WorkspaceNotAuthorizedImg from "public/auth/workspace-not-authorized.svg"; @@ -16,9 +16,12 @@ type Props = { type: "project" | "workspace"; }; -export const NotAuthorizedView: React.FC = ({ actionButton, type }) => { - const { user } = useUser(); +export const NotAuthorizedView: React.FC = observer((props) => { + const { actionButton, type } = props; + // router const { asPath: currentPath } = useRouter(); + // store hooks + const { currentUser } = useUser(); return ( @@ -34,9 +37,9 @@ export const NotAuthorizedView: React.FC = ({ actionButton, type }) => {

Oops! You are not authorized to view this page

- {user ? ( + {currentUser ? (

- You have signed in as {user.email}.
+ You have signed in as {currentUser.email}.
Sign in {" "} @@ -57,4 +60,4 @@ export const NotAuthorizedView: React.FC = ({ actionButton, type }) => {

); -}; +}); diff --git a/web/components/auth-screens/project/join-project.tsx b/web/components/auth-screens/project/join-project.tsx index 8f9233946..35b0b9b49 100644 --- a/web/components/auth-screens/project/join-project.tsx +++ b/web/components/auth-screens/project/join-project.tsx @@ -1,9 +1,8 @@ import { useState } from "react"; import Image from "next/image"; import { useRouter } from "next/router"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; -import { RootStore } from "store_legacy/root"; +// hooks +import { useProject, useUser } from "hooks/store"; // ui import { Button } from "@plane/ui"; // icons @@ -12,12 +11,13 @@ import { ClipboardList } from "lucide-react"; import JoinProjectImg from "public/auth/project-not-authorized.svg"; export const JoinProject: React.FC = () => { + // states const [isJoiningProject, setIsJoiningProject] = useState(false); - + // store hooks const { - project: projectStore, - user: { joinProject }, - }: RootStore = useMobxStore(); + membership: { joinProject }, + } = useUser(); + const { fetchProjects } = useProject(); const router = useRouter(); const { workspaceSlug, projectId } = router.query; @@ -28,12 +28,8 @@ export const JoinProject: React.FC = () => { setIsJoiningProject(true); joinProject(workspaceSlug.toString(), [projectId.toString()]) - .then(() => { - projectStore.fetchProjects(workspaceSlug.toString()); - }) - .finally(() => { - setIsJoiningProject(false); - }); + .then(() => fetchProjects(workspaceSlug.toString())) + .finally(() => setIsJoiningProject(false)); }; return ( diff --git a/web/components/automation/auto-archive-automation.tsx b/web/components/automation/auto-archive-automation.tsx index 6471bc9cf..9beabde66 100644 --- a/web/components/automation/auto-archive-automation.tsx +++ b/web/components/automation/auto-archive-automation.tsx @@ -1,7 +1,7 @@ import React, { useState } from "react"; import { observer } from "mobx-react-lite"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useProject, useUser } from "hooks/store"; // component import { CustomSelect, Loader, ToggleSwitch } from "@plane/ui"; import { SelectMonthModal } from "components/automation"; @@ -23,13 +23,13 @@ export const AutoArchiveAutomation: React.FC = observer((props) => { const { handleChange } = props; // states const [monthModal, setmonthModal] = useState(false); + // store hooks + const { + membership: { currentProjectRole }, + } = useUser(); + const { currentProjectDetails } = useProject(); - const { user: userStore, project: projectStore } = useMobxStore(); - - const projectDetails = projectStore.currentProjectDetails; - const userRole = userStore.currentProjectRole; - - const isAdmin = userRole === EUserWorkspaceRoles.ADMIN; + const isAdmin = currentProjectRole === EUserWorkspaceRoles.ADMIN; return ( <> @@ -54,24 +54,28 @@ export const AutoArchiveAutomation: React.FC = observer((props) => {
- projectDetails?.archive_in === 0 ? handleChange({ archive_in: 1 }) : handleChange({ archive_in: 0 }) + currentProjectDetails?.archive_in === 0 + ? handleChange({ archive_in: 1 }) + : handleChange({ archive_in: 0 }) } size="sm" disabled={!isAdmin} />
- {projectDetails ? ( - projectDetails.archive_in !== 0 && ( + {currentProjectDetails ? ( + currentProjectDetails.archive_in !== 0 && (
Auto-archive issues that are closed for
{ handleChange({ archive_in: val }); }} diff --git a/web/components/automation/auto-close-automation.tsx b/web/components/automation/auto-close-automation.tsx index d21eb8b80..d4b2239fa 100644 --- a/web/components/automation/auto-close-automation.tsx +++ b/web/components/automation/auto-close-automation.tsx @@ -1,7 +1,7 @@ import React, { useState } from "react"; import { observer } from "mobx-react-lite"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useProject, useProjectState, useUser } from "hooks/store"; // component import { SelectMonthModal } from "components/automation"; import { CustomSelect, CustomSearchSelect, ToggleSwitch, StateGroupIcon, DoubleCircleIcon, Loader } from "@plane/ui"; @@ -21,15 +21,16 @@ export const AutoCloseAutomation: React.FC = observer((props) => { const { handleChange } = props; // states const [monthModal, setmonthModal] = useState(false); + // store hooks + const { + membership: { currentProjectRole }, + } = useUser(); + const { currentProjectDetails } = useProject(); + const { projectStates } = useProjectState(); - const { user: userStore, project: projectStore, projectState: projectStateStore } = useMobxStore(); - - const userRole = userStore.currentProjectRole; - const projectDetails = projectStore.currentProjectDetails; // const stateGroups = projectStateStore.groupedProjectStates ?? undefined; - const states = projectStateStore.projectStates; - const options = states + const options = projectStates ?.filter((state) => state.group === "cancelled") .map((state) => ({ value: state.id, @@ -44,17 +45,17 @@ export const AutoCloseAutomation: React.FC = observer((props) => { const multipleOptions = (options ?? []).length > 1; - const defaultState = states?.find((s) => s.group === "cancelled")?.id || null; + const defaultState = projectStates?.find((s) => s.group === "cancelled")?.id || null; - const selectedOption = states?.find((s) => s.id === projectDetails?.default_state ?? defaultState); - const currentDefaultState = states?.find((s) => s.id === defaultState); + const selectedOption = projectStates?.find((s) => s.id === currentProjectDetails?.default_state ?? defaultState); + const currentDefaultState = projectStates?.find((s) => s.id === defaultState); const initialValues: Partial = { close_in: 1, default_state: defaultState, }; - const isAdmin = userRole === EUserWorkspaceRoles.ADMIN; + const isAdmin = currentProjectRole === EUserWorkspaceRoles.ADMIN; return ( <> @@ -79,9 +80,9 @@ export const AutoCloseAutomation: React.FC = observer((props) => {
- projectDetails?.close_in === 0 + currentProjectDetails?.close_in === 0 ? handleChange({ close_in: 1, default_state: defaultState }) : handleChange({ close_in: 0, default_state: null }) } @@ -90,16 +91,18 @@ export const AutoCloseAutomation: React.FC = observer((props) => { />
- {projectDetails ? ( - projectDetails.close_in !== 0 && ( + {currentProjectDetails ? ( + currentProjectDetails.close_in !== 0 && (
Auto-close issues that are inactive for
{ handleChange({ close_in: val }); }} @@ -118,7 +121,7 @@ export const AutoCloseAutomation: React.FC = observer((props) => { className="flex w-full select-none items-center rounded px-1 py-1.5 text-custom-text-200 hover:bg-custom-background-80" onClick={() => setmonthModal(true)} > - Customise Time Range + Customize Time Range @@ -129,7 +132,7 @@ export const AutoCloseAutomation: React.FC = observer((props) => {
Auto-close Status
{selectedOption ? ( diff --git a/web/components/command-palette/actions/help-actions.tsx b/web/components/command-palette/actions/help-actions.tsx index 859a6d23a..4aaaab33a 100644 --- a/web/components/command-palette/actions/help-actions.tsx +++ b/web/components/command-palette/actions/help-actions.tsx @@ -1,7 +1,7 @@ import { Command } from "cmdk"; import { FileText, GithubIcon, MessageSquare, Rocket } from "lucide-react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication } from "hooks/store"; // ui import { DiscordIcon } from "@plane/ui"; @@ -14,7 +14,7 @@ export const CommandPaletteHelpActions: React.FC = (props) => { const { commandPalette: { toggleShortcutModal }, - } = useMobxStore(); + } = useApplication(); return ( diff --git a/web/components/command-palette/actions/issue-actions/actions-list.tsx b/web/components/command-palette/actions/issue-actions/actions-list.tsx index 8e188df7b..8d4031080 100644 --- a/web/components/command-palette/actions/issue-actions/actions-list.tsx +++ b/web/components/command-palette/actions/issue-actions/actions-list.tsx @@ -5,6 +5,8 @@ import { LinkIcon, Signal, Trash2, UserMinus2, UserPlus2 } from "lucide-react"; // mobx store import { useMobxStore } from "lib/mobx/store-provider"; // hooks +import { useApplication, useUser } from "hooks/store"; +// hooks import useToast from "hooks/use-toast"; // ui import { DoubleCircleIcon, UserGroupIcon } from "@plane/ui"; @@ -29,10 +31,12 @@ export const CommandPaletteIssueActions: React.FC = observer((props) => { const { workspaceSlug, projectId } = router.query; const { - commandPalette: { toggleCommandPaletteModal, toggleDeleteIssueModal }, projectIssues: { updateIssue }, - user: { currentUser }, } = useMobxStore(); + const { + commandPalette: { toggleCommandPaletteModal, toggleDeleteIssueModal }, + } = useApplication(); + const { currentUser } = useUser(); const { setToastAlert } = useToast(); diff --git a/web/components/command-palette/actions/project-actions.tsx b/web/components/command-palette/actions/project-actions.tsx index 1e10b3a46..44b5e6111 100644 --- a/web/components/command-palette/actions/project-actions.tsx +++ b/web/components/command-palette/actions/project-actions.tsx @@ -1,7 +1,7 @@ import { Command } from "cmdk"; import { ContrastIcon, FileText } from "lucide-react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication } from "hooks/store"; // ui import { DiceIcon, PhotoFilterIcon } from "@plane/ui"; @@ -14,8 +14,8 @@ export const CommandPaletteProjectActions: React.FC = (props) => { const { commandPalette: { toggleCreateCycleModal, toggleCreateModuleModal, toggleCreatePageModal, toggleCreateViewModal }, - trackEvent: { setTrackElement }, - } = useMobxStore(); + eventTracker: { setTrackElement }, + } = useApplication(); return ( <> diff --git a/web/components/command-palette/actions/theme-actions.tsx b/web/components/command-palette/actions/theme-actions.tsx index f7266a48a..976a63c87 100644 --- a/web/components/command-palette/actions/theme-actions.tsx +++ b/web/components/command-palette/actions/theme-actions.tsx @@ -4,8 +4,8 @@ import { useTheme } from "next-themes"; import { Settings } from "lucide-react"; import { observer } from "mobx-react-lite"; // hooks +import { useUser } from "hooks/store"; import useToast from "hooks/use-toast"; -import { useMobxStore } from "lib/mobx/store-provider"; // constants import { THEME_OPTIONS } from "constants/themes"; @@ -18,9 +18,7 @@ export const CommandPaletteThemeActions: FC = observer((props) => { // states const [mounted, setMounted] = useState(false); // store - const { - user: { updateCurrentUserTheme }, - } = useMobxStore(); + const { updateCurrentUserTheme } = useUser(); // hooks const { setTheme } = useTheme(); const { setToastAlert } = useToast(); diff --git a/web/components/command-palette/command-modal.tsx b/web/components/command-palette/command-modal.tsx index 005e570e7..401204fc7 100644 --- a/web/components/command-palette/command-modal.tsx +++ b/web/components/command-palette/command-modal.tsx @@ -5,8 +5,8 @@ import { Command } from "cmdk"; import { Dialog, Transition } from "@headlessui/react"; import { observer } from "mobx-react-lite"; import { FolderPlus, Search, Settings } from "lucide-react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication } from "hooks/store"; // services import { WorkspaceService } from "services/workspace.service"; import { IssueService } from "services/issue"; @@ -62,8 +62,8 @@ export const CommandModal: React.FC = observer(() => { toggleCreateIssueModal, toggleCreateProjectModal, }, - trackEvent: { setTrackElement }, - } = useMobxStore(); + eventTracker: { setTrackElement }, + } = useApplication(); // router const router = useRouter(); diff --git a/web/components/command-palette/command-pallette.tsx b/web/components/command-palette/command-palette.tsx similarity index 97% rename from web/components/command-palette/command-pallette.tsx rename to web/components/command-palette/command-palette.tsx index 0488455fb..01cf80348 100644 --- a/web/components/command-palette/command-pallette.tsx +++ b/web/components/command-palette/command-palette.tsx @@ -3,6 +3,7 @@ import { useRouter } from "next/router"; import useSWR from "swr"; import { observer } from "mobx-react-lite"; // hooks +import { useApplication, useUser } from "hooks/store"; import useToast from "hooks/use-toast"; // components import { CommandModal, ShortcutsModal } from "components/command-palette"; @@ -19,8 +20,6 @@ import { copyTextToClipboard } from "helpers/string.helper"; import { IssueService } from "services/issue"; // fetch keys import { ISSUE_DETAILS } from "constants/fetch-keys"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; // services const issueService = new IssueService(); @@ -28,14 +27,14 @@ const issueService = new IssueService(); export const CommandPalette: FC = observer(() => { const router = useRouter(); const { workspaceSlug, projectId, issueId, cycleId, moduleId } = router.query; - // store + const { commandPalette, theme: { toggleSidebar }, - user: { currentUser }, - trackEvent: { setTrackElement }, - projectIssues: { removeIssue }, - } = useMobxStore(); + eventTracker: { setTrackElement }, + } = useApplication(); + const { currentUser } = useUser(); + const { toggleCommandPaletteModal, isCreateIssueModalOpen, diff --git a/web/components/command-palette/index.ts b/web/components/command-palette/index.ts index 192ef8ef9..0d2e042a7 100644 --- a/web/components/command-palette/index.ts +++ b/web/components/command-palette/index.ts @@ -1,5 +1,5 @@ export * from "./actions"; export * from "./shortcuts-modal"; export * from "./command-modal"; -export * from "./command-pallette"; +export * from "./command-palette"; export * from "./helpers"; diff --git a/web/components/core/image-picker-popover.tsx b/web/components/core/image-picker-popover.tsx index 41fe05b3f..73b720ad9 100644 --- a/web/components/core/image-picker-popover.tsx +++ b/web/components/core/image-picker-popover.tsx @@ -6,8 +6,8 @@ import useSWR from "swr"; import { useDropzone } from "react-dropzone"; import { Tab, Transition, Popover } from "@headlessui/react"; import { Control, Controller } from "react-hook-form"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication, useWorkspace } from "hooks/store"; // services import { FileService } from "services/file.service"; // hooks @@ -45,25 +45,24 @@ const fileService = new FileService(); export const ImagePickerPopover: React.FC = observer((props) => { const { label, value, control, onChange, disabled = false } = props; - + // states const [image, setImage] = useState(null); const [isImageUploading, setIsImageUploading] = useState(false); - const [isOpen, setIsOpen] = useState(false); const [searchParams, setSearchParams] = useState(""); const [formData, setFormData] = useState({ search: "", }); - + // refs const ref = useRef(null); - + // router const router = useRouter(); const { workspaceSlug } = router.query; - + // store hooks const { - workspace: { currentWorkspace }, - appConfig: { envConfig }, - } = useMobxStore(); + config: { envConfig }, + } = useApplication(); + const { currentWorkspace } = useWorkspace(); const { data: unsplashImages, error: unsplashError } = useSWR( `UNSPLASH_IMAGES_${searchParams}`, diff --git a/web/components/core/modals/user-image-upload-modal.tsx b/web/components/core/modals/user-image-upload-modal.tsx index 11bda44ce..6debc2c15 100644 --- a/web/components/core/modals/user-image-upload-modal.tsx +++ b/web/components/core/modals/user-image-upload-modal.tsx @@ -2,8 +2,8 @@ import React, { useState } from "react"; import { observer } from "mobx-react-lite"; import { useDropzone } from "react-dropzone"; import { Transition, Dialog } from "@headlessui/react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication } from "hooks/store"; // services import { FileService } from "services/file.service"; // hooks @@ -32,12 +32,12 @@ export const UserImageUploadModal: React.FC = observer((props) => { // states const [image, setImage] = useState(null); const [isImageUploading, setIsImageUploading] = useState(false); - + // toast alert const { setToastAlert } = useToast(); - + // store hooks const { - appConfig: { envConfig }, - } = useMobxStore(); + config: { envConfig }, + } = useApplication(); const onDrop = (acceptedFiles: File[]) => setImage(acceptedFiles[0]); diff --git a/web/components/core/modals/workspace-image-upload-modal.tsx b/web/components/core/modals/workspace-image-upload-modal.tsx index 166e911f5..e04ccf820 100644 --- a/web/components/core/modals/workspace-image-upload-modal.tsx +++ b/web/components/core/modals/workspace-image-upload-modal.tsx @@ -3,8 +3,8 @@ import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { useDropzone } from "react-dropzone"; import { Transition, Dialog } from "@headlessui/react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication, useWorkspace } from "hooks/store"; // services import { FileService } from "services/file.service"; // hooks @@ -40,9 +40,9 @@ export const WorkspaceImageUploadModal: React.FC = observer((props) => { const { setToastAlert } = useToast(); const { - workspace: { currentWorkspace }, - appConfig: { envConfig }, - } = useMobxStore(); + config: { envConfig }, + } = useApplication(); + const { currentWorkspace } = useWorkspace(); const onDrop = (acceptedFiles: File[]) => setImage(acceptedFiles[0]); diff --git a/web/components/core/theme/custom-theme-selector.tsx b/web/components/core/theme/custom-theme-selector.tsx index c55170702..69cb4e2a9 100644 --- a/web/components/core/theme/custom-theme-selector.tsx +++ b/web/components/core/theme/custom-theme-selector.tsx @@ -1,8 +1,8 @@ import { observer } from "mobx-react-lite"; import { Controller, useForm } from "react-hook-form"; import { useTheme } from "next-themes"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useUser } from "hooks/store"; // ui import { Button, InputColorPicker } from "@plane/ui"; // types @@ -25,8 +25,8 @@ const inputRules = { }; export const CustomThemeSelector: React.FC = observer(() => { - const { user: userStore } = useMobxStore(); - const userTheme = userStore?.currentUser?.theme; + const { currentUser, updateCurrentUser } = useUser(); + const userTheme = currentUser?.theme; // hooks const { setTheme } = useTheme(); @@ -61,7 +61,7 @@ export const CustomThemeSelector: React.FC = observer(() => { setTheme("custom"); - return userStore.updateCurrentUser({ theme: payload }); + return updateCurrentUser({ theme: payload }); }; const handleValueChange = (val: string | undefined, onChange: any) => { diff --git a/web/components/cycles/active-cycle-details.tsx b/web/components/cycles/active-cycle-details.tsx index 64ed76132..1b8878995 100644 --- a/web/components/cycles/active-cycle-details.tsx +++ b/web/components/cycles/active-cycle-details.tsx @@ -3,9 +3,9 @@ import Link from "next/link"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import useSWR from "swr"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; // hooks +import { useMobxStore } from "lib/mobx/store-provider"; +import { useApplication } from "hooks/store"; import useToast from "hooks/use-toast"; // ui import { SingleProgressStats } from "components/core"; @@ -67,12 +67,15 @@ interface IActiveCycleDetails { } export const ActiveCycleDetails: React.FC = observer((props) => { + // router const router = useRouter(); - const { workspaceSlug, projectId } = props; - - const { cycle: cycleStore, commandPalette: commandPaletteStore } = useMobxStore(); - + // store hooks + const { cycle: cycleStore } = useMobxStore(); + const { + commandPalette: { toggleCreateCycleModal }, + } = useApplication(); + // toast alert const { setToastAlert } = useToast(); useSWR( @@ -118,7 +121,7 @@ export const ActiveCycleDetails: React.FC = observer((props @@ -187,12 +190,12 @@ export const ActiveCycleDetails: React.FC = observer((props cycleStatus === "current" ? "#09A953" : cycleStatus === "upcoming" - ? "#F7AE59" - : cycleStatus === "completed" - ? "#3F76FF" - : cycleStatus === "draft" - ? "rgb(var(--color-text-200))" - : "" + ? "#F7AE59" + : cycleStatus === "completed" + ? "#3F76FF" + : cycleStatus === "draft" + ? "rgb(var(--color-text-200))" + : "" }`} /> @@ -207,12 +210,12 @@ export const ActiveCycleDetails: React.FC = observer((props cycleStatus === "current" ? "bg-green-600/5 text-green-600" : cycleStatus === "upcoming" - ? "bg-orange-300/5 text-orange-300" - : cycleStatus === "completed" - ? "bg-blue-500/5 text-blue-500" - : cycleStatus === "draft" - ? "bg-neutral-400/5 text-neutral-400" - : "" + ? "bg-orange-300/5 text-orange-300" + : cycleStatus === "completed" + ? "bg-blue-500/5 text-blue-500" + : cycleStatus === "draft" + ? "bg-neutral-400/5 text-neutral-400" + : "" }`} > {cycleStatus === "current" ? ( diff --git a/web/components/cycles/cycles-board.tsx b/web/components/cycles/cycles-board.tsx index af234b9dc..e69089664 100644 --- a/web/components/cycles/cycles-board.tsx +++ b/web/components/cycles/cycles-board.tsx @@ -1,7 +1,7 @@ import { FC } from "react"; import { observer } from "mobx-react-lite"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication } from "hooks/store"; // components import { CyclePeekOverview, CyclesBoardCard } from "components/cycles"; // types @@ -17,8 +17,8 @@ export interface ICyclesBoard { export const CyclesBoard: FC = observer((props) => { const { cycles, filter, workspaceSlug, projectId, peekCycle } = props; - - const { commandPalette: commandPaletteStore } = useMobxStore(); + // store hooks + const { commandPalette: commandPaletteStore } = useApplication(); return ( <> diff --git a/web/components/cycles/cycles-list.tsx b/web/components/cycles/cycles-list.tsx index 226807b78..05fa9b92f 100644 --- a/web/components/cycles/cycles-list.tsx +++ b/web/components/cycles/cycles-list.tsx @@ -1,7 +1,7 @@ import { FC } from "react"; import { observer } from "mobx-react-lite"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication } from "hooks/store"; // components import { CyclePeekOverview, CyclesListItem } from "components/cycles"; // ui @@ -18,11 +18,11 @@ export interface ICyclesList { export const CyclesList: FC = observer((props) => { const { cycles, filter, workspaceSlug, projectId } = props; - + // store hooks const { commandPalette: commandPaletteStore, - trackEvent: { setTrackElement }, - } = useMobxStore(); + eventTracker: { setTrackElement }, + } = useApplication(); return ( <> diff --git a/web/components/cycles/gantt-chart/cycles-list-layout.tsx b/web/components/cycles/gantt-chart/cycles-list-layout.tsx index 9671c22af..4085b4e34 100644 --- a/web/components/cycles/gantt-chart/cycles-list-layout.tsx +++ b/web/components/cycles/gantt-chart/cycles-list-layout.tsx @@ -1,19 +1,18 @@ import { FC } from "react"; - import { useRouter } from "next/router"; - +import { observer } from "mobx-react-lite"; import { KeyedMutator } from "swr"; - +// hooks +import { useUser } from "hooks/store"; // services import { CycleService } from "services/cycle.service"; -// hooks -import useUser from "hooks/use-user"; -import useProjectDetails from "hooks/use-project-details"; // components import { GanttChartRoot, IBlockUpdateData, CycleGanttSidebar } from "components/gantt-chart"; import { CycleGanttBlock } from "components/cycles"; // types import { ICycle } from "types"; +// constants +import { EUserWorkspaceRoles } from "constants/workspace"; type Props = { workspaceSlug: string; @@ -24,15 +23,18 @@ type Props = { // services const cycleService = new CycleService(); -export const CyclesListGanttChartView: FC = ({ cycles, mutateCycles }) => { +export const CyclesListGanttChartView: FC = observer((props) => { + const { cycles, mutateCycles } = props; + // router const router = useRouter(); const { workspaceSlug } = router.query; - - const { user } = useUser(); - const { projectDetails } = useProjectDetails(); + // store hooks + const { + membership: { currentProjectRole }, + } = useUser(); const handleCycleUpdate = (cycle: ICycle, payload: IBlockUpdateData) => { - if (!workspaceSlug || !user) return; + if (!workspaceSlug) return; mutateCycles && mutateCycles((prevData: any) => { if (!prevData) return prevData; @@ -76,7 +78,8 @@ export const CyclesListGanttChartView: FC = ({ cycles, mutateCycles }) => })) : []; - const isAllowed = projectDetails?.member_role === 20 || projectDetails?.member_role === 15; + const isAllowed = + currentProjectRole && [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER].includes(currentProjectRole); return (
@@ -94,4 +97,4 @@ export const CyclesListGanttChartView: FC = ({ cycles, mutateCycles }) => />
); -}; +}); diff --git a/web/components/estimates/estimate-list-item.tsx b/web/components/estimates/estimate-list-item.tsx index 65764e5d2..831920e69 100644 --- a/web/components/estimates/estimate-list-item.tsx +++ b/web/components/estimates/estimate-list-item.tsx @@ -1,11 +1,8 @@ import React from "react"; - import { useRouter } from "next/router"; - -// store import { observer } from "mobx-react-lite"; -import { useMobxStore } from "lib/mobx/store-provider"; // hooks +import { useProject } from "hooks/store"; import useToast from "hooks/use-toast"; // ui import { Button, CustomMenu } from "@plane/ui"; @@ -27,10 +24,8 @@ export const EstimateListItem: React.FC = observer((props) => { // router const router = useRouter(); const { workspaceSlug, projectId } = router.query; - // store - const { - project: { currentProjectDetails, updateProject }, - } = useMobxStore(); + // store hooks + const { currentProjectDetails, updateProject } = useProject(); // hooks const { setToastAlert } = useToast(); diff --git a/web/components/exporter/export-modal.tsx b/web/components/exporter/export-modal.tsx index 136cc6e8f..6338831a1 100644 --- a/web/components/exporter/export-modal.tsx +++ b/web/components/exporter/export-modal.tsx @@ -2,8 +2,8 @@ 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 { useProject } from "hooks/store"; // services import { ProjectExportService } from "services/project"; // hooks @@ -26,28 +26,30 @@ const projectExportService = new ProjectExportService(); export const Exporter: React.FC = observer((props) => { const { isOpen, handleClose, user, provider, mutateServices } = props; - + // states const [exportLoading, setExportLoading] = useState(false); - + // router const router = useRouter(); const { workspaceSlug } = router.query; - - const { project: projectStore } = useMobxStore(); - - const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : undefined; - + // store hooks + const { workspaceProjects, getProjectById } = useProject(); + // toast alert const { setToastAlert } = useToast(); - const options = projects?.map((project) => ({ - value: project.id, - query: project.name + project.identifier, - content: ( -
- {project.identifier} - {project.name} -
- ), - })); + const options = workspaceProjects?.map((projectId) => { + const projectDetails = getProjectById(projectId); + + return { + value: projectDetails?.id, + query: `${projectDetails?.name} ${projectDetails?.identifier}`, + content: ( +
+ {projectDetails?.identifier} + {projectDetails?.name} +
+ ), + }; + }); const [value, setValue] = React.useState([]); const [multiple, setMultiple] = React.useState(false); @@ -131,10 +133,12 @@ export const Exporter: React.FC = observer((props) => { input label={ value && value.length > 0 - ? projects && - projects - .filter((p) => value.includes(p.id)) - .map((p) => p.identifier) + ? value + .map((projectId) => { + const projectDetails = getProjectById(projectId); + + return projectDetails?.identifier; + }) .join(", ") : "All projects" } diff --git a/web/components/headers/cycles.tsx b/web/components/headers/cycles.tsx index 459eb9b30..a53ecd5eb 100644 --- a/web/components/headers/cycles.tsx +++ b/web/components/headers/cycles.tsx @@ -2,8 +2,8 @@ import { FC } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { Plus } from "lucide-react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication, useProject, useUser } from "hooks/store"; // ui import { Breadcrumbs, Button, ContrastIcon } from "@plane/ui"; // helpers @@ -14,14 +14,15 @@ export const CyclesHeader: FC = observer(() => { // router const router = useRouter(); const { workspaceSlug } = router.query; - // store + // store hooks const { - project: projectStore, - user: { currentProjectRole }, - commandPalette: commandPaletteStore, - trackEvent: { setTrackElement }, - } = useMobxStore(); - const { currentProjectDetails } = projectStore; + commandPalette: { toggleCreateCycleModal }, + eventTracker: { setTrackElement }, + } = useApplication(); + const { + membership: { currentProjectRole }, + } = useUser(); + const { currentProjectDetails } = useProject(); const canUserCreateCycle = currentProjectRole && [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER].includes(currentProjectRole); @@ -63,7 +64,7 @@ export const CyclesHeader: FC = observer(() => { prependIcon={} onClick={() => { setTrackElement("CYCLES_PAGE_HEADER"); - commandPaletteStore.toggleCreateCycleModal(true); + toggleCreateCycleModal(true); }} > Add Cycle diff --git a/web/components/headers/modules-list.tsx b/web/components/headers/modules-list.tsx index ebe0c5868..bc441c83e 100644 --- a/web/components/headers/modules-list.tsx +++ b/web/components/headers/modules-list.tsx @@ -1,9 +1,8 @@ import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { Plus } from "lucide-react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; // hooks +import { useApplication, useProject, useUser } from "hooks/store"; import useLocalStorage from "hooks/use-local-storage"; // ui import { Breadcrumbs, Button, Tooltip, DiceIcon } from "@plane/ui"; @@ -17,13 +16,12 @@ export const ModulesListHeader: React.FC = observer(() => { // router const router = useRouter(); const { workspaceSlug } = router.query; - // store + // store hooks + const { commandPalette: commandPaletteStore } = useApplication(); const { - project: projectStore, - commandPalette: commandPaletteStore, - user: { currentProjectRole }, - } = useMobxStore(); - const { currentProjectDetails } = projectStore; + membership: { currentProjectRole }, + } = useUser(); + const { currentProjectDetails } = useProject(); const { storedValue: modulesView, setValue: setModulesView } = useLocalStorage("modules_view", "grid"); diff --git a/web/components/headers/page-details.tsx b/web/components/headers/page-details.tsx index f8c48de2e..9a7d08f96 100644 --- a/web/components/headers/page-details.tsx +++ b/web/components/headers/page-details.tsx @@ -1,21 +1,18 @@ import { FC } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; +import useSWR from "swr"; import { FileText, Plus } from "lucide-react"; +// hooks +import { useApplication, useProject } from "hooks/store"; // services import { PageService } from "services/page.service"; - -// constants -import { PAGE_DETAILS } from "constants/fetch-keys"; - -// hooks -import { useMobxStore } from "lib/mobx/store-provider"; // ui import { Breadcrumbs, Button } from "@plane/ui"; -// helper +// helpers import { renderEmoji } from "helpers/emoji.helper"; - -import useSWR from "swr"; +// fetch-keys +import { PAGE_DETAILS } from "constants/fetch-keys"; export interface IPagesHeaderProps { showButton?: boolean; @@ -28,8 +25,8 @@ export const PageDetailsHeader: FC = observer((props) => { const router = useRouter(); const { workspaceSlug, pageId } = router.query; - const { project: projectStore, commandPalette: commandPaletteStore } = useMobxStore(); - const { currentProjectDetails } = projectStore; + const { commandPalette: commandPaletteStore } = useApplication(); + const { currentProjectDetails } = useProject(); const { data: pageDetails } = useSWR( workspaceSlug && currentProjectDetails?.id && pageId ? PAGE_DETAILS(pageId as string) : null, diff --git a/web/components/headers/pages.tsx b/web/components/headers/pages.tsx index 71853f53d..00564cb7d 100644 --- a/web/components/headers/pages.tsx +++ b/web/components/headers/pages.tsx @@ -2,10 +2,10 @@ import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { FileText, Plus } from "lucide-react"; // hooks -import { useMobxStore } from "lib/mobx/store-provider"; +import { useApplication, useProject, useUser } from "hooks/store"; // ui import { Breadcrumbs, Button } from "@plane/ui"; -// helper +// helpers import { renderEmoji } from "helpers/emoji.helper"; // constants import { EUserWorkspaceRoles } from "constants/workspace"; @@ -14,12 +14,14 @@ export const PagesHeader = observer(() => { // router const router = useRouter(); const { workspaceSlug } = router.query; - // mobx store + // store hooks const { - user: { currentProjectRole }, - project: { currentProjectDetails }, commandPalette: { toggleCreatePageModal }, - } = useMobxStore(); + } = useApplication(); + const { + membership: { currentProjectRole }, + } = useUser(); + const { currentProjectDetails } = useProject(); const canUserCreatePage = currentProjectRole && [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER].includes(currentProjectRole); diff --git a/web/components/headers/project-archived-issue-details.tsx b/web/components/headers/project-archived-issue-details.tsx index 70b4f91a8..abd741ff6 100644 --- a/web/components/headers/project-archived-issue-details.tsx +++ b/web/components/headers/project-archived-issue-details.tsx @@ -3,7 +3,7 @@ import useSWR from "swr"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; // hooks -import { useMobxStore } from "lib/mobx/store-provider"; +import { useProject } from "hooks/store"; // ui import { Breadcrumbs, LayersIcon } from "@plane/ui"; // types @@ -18,12 +18,11 @@ import { renderEmoji } from "helpers/emoji.helper"; const issueArchiveService = new IssueArchiveService(); export const ProjectArchivedIssueDetailsHeader: FC = observer(() => { + // router const router = useRouter(); const { workspaceSlug, projectId, archivedIssueId } = router.query; - - const { project: projectStore } = useMobxStore(); - - const { currentProjectDetails } = projectStore; + // store hooks + const { currentProjectDetails } = useProject(); const { data: issueDetails } = useSWR( workspaceSlug && projectId && archivedIssueId ? ISSUE_DETAILS(archivedIssueId as string) : null, diff --git a/web/components/headers/project-inbox.tsx b/web/components/headers/project-inbox.tsx index 174d04a8f..ae16eff89 100644 --- a/web/components/headers/project-inbox.tsx +++ b/web/components/headers/project-inbox.tsx @@ -3,7 +3,7 @@ import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { Plus } from "lucide-react"; // hooks -import { useMobxStore } from "lib/mobx/store-provider"; +import { useProject } from "hooks/store"; // ui import { Breadcrumbs, Button, LayersIcon } from "@plane/ui"; // components @@ -12,13 +12,13 @@ import { CreateInboxIssueModal } from "components/inbox"; import { renderEmoji } from "helpers/emoji.helper"; export const ProjectInboxHeader: FC = observer(() => { + // states + const [createIssueModal, setCreateIssueModal] = useState(false); + // router const router = useRouter(); const { workspaceSlug } = router.query; - const [createIssueModal, setCreateIssueModal] = useState(false); - - const { project: projectStore } = useMobxStore(); - - const { currentProjectDetails } = projectStore; + // store hooks + const { currentProjectDetails } = useProject(); return (
diff --git a/web/components/headers/project-issue-details.tsx b/web/components/headers/project-issue-details.tsx index 4eee7d8eb..a374790c4 100644 --- a/web/components/headers/project-issue-details.tsx +++ b/web/components/headers/project-issue-details.tsx @@ -2,27 +2,26 @@ import { FC } from "react"; import useSWR from "swr"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; - +// hooks +import { useProject } from "hooks/store"; // ui import { Breadcrumbs, LayersIcon } from "@plane/ui"; -// helper +// helpers import { renderEmoji } from "helpers/emoji.helper"; // services import { IssueService } from "services/issue"; // constants import { ISSUE_DETAILS } from "constants/fetch-keys"; -import { useMobxStore } from "lib/mobx/store-provider"; // services const issueService = new IssueService(); export const ProjectIssueDetailsHeader: FC = observer(() => { + // router const router = useRouter(); const { workspaceSlug, projectId, issueId } = router.query; - - const { project: projectStore } = useMobxStore(); - - const { currentProjectDetails } = projectStore; + // store hooks + const { currentProjectDetails } = useProject(); const { data: issueDetails } = useSWR( workspaceSlug && projectId && issueId ? ISSUE_DETAILS(issueId as string) : null, diff --git a/web/components/headers/project-settings.tsx b/web/components/headers/project-settings.tsx index b7db6cd71..6e13a1bc7 100644 --- a/web/components/headers/project-settings.tsx +++ b/web/components/headers/project-settings.tsx @@ -1,13 +1,12 @@ import { FC } from "react"; import { useRouter } from "next/router"; - +import { observer } from "mobx-react-lite"; // ui import { Breadcrumbs } from "@plane/ui"; // helper import { renderEmoji } from "helpers/emoji.helper"; // hooks -import { useMobxStore } from "lib/mobx/store-provider"; -import { observer } from "mobx-react-lite"; +import { useProject, useUser } from "hooks/store"; // constants import { EUserWorkspaceRoles } from "constants/workspace"; @@ -17,14 +16,14 @@ export interface IProjectSettingHeader { export const ProjectSettingHeader: FC = observer((props) => { const { title } = props; + // router const router = useRouter(); const { workspaceSlug } = router.query; - // store + // store hooks const { - project: projectStore, - user: { currentProjectRole }, - } = useMobxStore(); - const { currentProjectDetails } = projectStore; + membership: { currentProjectRole }, + } = useUser(); + const { currentProjectDetails } = useProject(); if (currentProjectRole && currentProjectRole <= EUserWorkspaceRoles.VIEWER) return null; diff --git a/web/components/headers/project-views.tsx b/web/components/headers/project-views.tsx index 964110967..17726e52b 100644 --- a/web/components/headers/project-views.tsx +++ b/web/components/headers/project-views.tsx @@ -1,8 +1,8 @@ import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { Plus } from "lucide-react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication, useProject } from "hooks/store"; // components import { Breadcrumbs, PhotoFilterIcon, Button } from "@plane/ui"; // helpers @@ -12,9 +12,11 @@ export const ProjectViewsHeader: React.FC = observer(() => { // router const router = useRouter(); const { workspaceSlug } = router.query; - - const { project: projectStore, commandPalette } = useMobxStore(); - const { currentProjectDetails } = projectStore; + // store hooks + const { + commandPalette: { toggleCreateViewModal }, + } = useApplication(); + const { currentProjectDetails } = useProject(); return ( <> @@ -56,7 +58,7 @@ export const ProjectViewsHeader: React.FC = observer(() => { variant="primary" size="sm" prependIcon={} - onClick={() => commandPalette.toggleCreateViewModal(true)} + onClick={() => toggleCreateViewModal(true)} > Create View diff --git a/web/components/headers/projects.tsx b/web/components/headers/projects.tsx index 2ba0c0184..0dd0a3758 100644 --- a/web/components/headers/projects.tsx +++ b/web/components/headers/projects.tsx @@ -1,23 +1,17 @@ -import { useRouter } from "next/router"; +import { observer } from "mobx-react-lite"; import { Search, Plus, Briefcase } from "lucide-react"; +// hooks +import { useApplication, useProject } from "hooks/store"; // ui import { Breadcrumbs, Button } from "@plane/ui"; -// hooks -import { useMobxStore } from "lib/mobx/store-provider"; -import { observer } from "mobx-react-lite"; export const ProjectsHeader = observer(() => { - const router = useRouter(); - const { workspaceSlug } = router.query; - - // store + // store hooks const { - project: projectStore, commandPalette: commandPaletteStore, - trackEvent: { setTrackElement }, - } = useMobxStore(); - - const projectsList = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : []; + eventTracker: { setTrackElement }, + } = useApplication(); + const { workspaceProjects, searchQuery, setSearchQuery } = useProject(); return (
@@ -33,13 +27,13 @@ export const ProjectsHeader = observer(() => {
- {projectsList?.length > 0 && ( + {workspaceProjects && workspaceProjects?.length > 0 && (
projectStore.setSearchQuery(e.target.value)} + value={searchQuery} + onChange={(e) => setSearchQuery(e.target.value)} placeholder="Search" />
diff --git a/web/components/inbox/issue-activity.tsx b/web/components/inbox/issue-activity.tsx index 2b8fe7d9b..08d0bbc1b 100644 --- a/web/components/inbox/issue-activity.tsx +++ b/web/components/inbox/issue-activity.tsx @@ -1,8 +1,8 @@ import { useRouter } from "next/router"; import useSWR, { mutate } from "swr"; import { observer } from "mobx-react-lite"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication, useUser, useWorkspace } from "hooks/store"; // components import { AddComment, IssueActivitySection } from "components/issues"; // services @@ -21,14 +21,15 @@ const issueService = new IssueService(); const issueCommentService = new IssueCommentService(); export const InboxIssueActivity: React.FC = observer(({ issueDetails }) => { + // router const router = useRouter(); const { workspaceSlug, projectId } = router.query; - + // store hooks const { - user: userStore, - trackEvent: { postHogEventTracker }, - workspace: { currentWorkspace }, - } = useMobxStore(); + eventTracker: { postHogEventTracker }, + } = useApplication(); + const { currentUser } = useUser(); + const { currentWorkspace } = useWorkspace(); const { setToastAlert } = useToast(); @@ -39,13 +40,11 @@ export const InboxIssueActivity: React.FC = observer(({ issueDetails }) = : null ); - const user = userStore.currentUser; - const handleCommentUpdate = async (commentId: string, data: Partial) => { - if (!workspaceSlug || !projectId || !issueDetails.id || !user) return; + if (!workspaceSlug || !projectId || !issueDetails.id || !currentUser) return; await issueCommentService - .patchIssueComment(workspaceSlug as string, projectId as string, issueDetails.id as string, commentId, data) + .patchIssueComment(workspaceSlug.toString(), projectId.toString(), issueDetails.id, commentId, data) .then((res) => { mutateIssueActivity(); postHogEventTracker( @@ -57,19 +56,19 @@ export const InboxIssueActivity: React.FC = observer(({ issueDetails }) = { isGrouping: true, groupType: "Workspace_metrics", - gorupId: currentWorkspace?.id!, + groupId: currentWorkspace?.id!, } ); }); }; const handleCommentDelete = async (commentId: string) => { - if (!workspaceSlug || !projectId || !issueDetails.id || !user) return; + if (!workspaceSlug || !projectId || !issueDetails.id || !currentUser) return; mutateIssueActivity((prevData: any) => prevData?.filter((p: any) => p.id !== commentId), false); await issueCommentService - .deleteIssueComment(workspaceSlug as string, projectId as string, issueDetails.id as string, commentId) + .deleteIssueComment(workspaceSlug.toString(), projectId.toString(), issueDetails.id, commentId) .then(() => { mutateIssueActivity(); postHogEventTracker( @@ -80,14 +79,14 @@ export const InboxIssueActivity: React.FC = observer(({ issueDetails }) = { isGrouping: true, groupType: "Workspace_metrics", - gorupId: currentWorkspace?.id!, + groupId: currentWorkspace?.id!, } ); }); }; const handleAddComment = async (formData: IIssueActivity) => { - if (!workspaceSlug || !issueDetails || !user) return; + if (!workspaceSlug || !issueDetails || !currentUser) return; await issueCommentService .createIssueComment(workspaceSlug.toString(), issueDetails.project, issueDetails.id, formData) @@ -102,7 +101,7 @@ export const InboxIssueActivity: React.FC = observer(({ issueDetails }) = { isGrouping: true, groupType: "Workspace_metrics", - gorupId: currentWorkspace?.id!, + groupId: currentWorkspace?.id!, } ); }) diff --git a/web/components/instance/ai-form.tsx b/web/components/instance/ai-form.tsx index f52cc9c0f..bb72d2329 100644 --- a/web/components/instance/ai-form.tsx +++ b/web/components/instance/ai-form.tsx @@ -1,15 +1,13 @@ import { FC, useState } from "react"; import { Controller, useForm } from "react-hook-form"; +import { Eye, EyeOff } from "lucide-react"; // ui import { Button, Input } from "@plane/ui"; // types import { IFormattedInstanceConfiguration } from "types/instance"; // hooks +import { useApplication } from "hooks/store"; import useToast from "hooks/use-toast"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; -// icons -import { Eye, EyeOff } from "lucide-react"; export interface IInstanceAIForm { config: IFormattedInstanceConfiguration; @@ -25,7 +23,7 @@ export const InstanceAIForm: FC = (props) => { // states const [showPassword, setShowPassword] = useState(false); // store - const { instance: instanceStore } = useMobxStore(); + const { instance: instanceStore } = useApplication(); // toast const { setToastAlert } = useToast(); // form data diff --git a/web/components/instance/email-form.tsx b/web/components/instance/email-form.tsx index 60e6c7bf6..15913f79b 100644 --- a/web/components/instance/email-form.tsx +++ b/web/components/instance/email-form.tsx @@ -6,9 +6,8 @@ import { Eye, EyeOff } from "lucide-react"; // types import { IFormattedInstanceConfiguration } from "types/instance"; // hooks +import { useApplication } from "hooks/store"; import useToast from "hooks/use-toast"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; export interface IInstanceEmailForm { config: IFormattedInstanceConfiguration; @@ -27,8 +26,8 @@ export const InstanceEmailForm: FC = (props) => { const { config } = props; // states const [showPassword, setShowPassword] = useState(false); - // store - const { instance: instanceStore } = useMobxStore(); + // store hooks + const { instance: instanceStore } = useApplication(); // toast const { setToastAlert } = useToast(); // form data diff --git a/web/components/instance/general-form.tsx b/web/components/instance/general-form.tsx index 13be21fa2..c757d19c6 100644 --- a/web/components/instance/general-form.tsx +++ b/web/components/instance/general-form.tsx @@ -5,8 +5,8 @@ import { Button, Input } from "@plane/ui"; // types import { IInstance, IInstanceAdmin } from "types/instance"; // hooks +import { useApplication } from "hooks/store"; import useToast from "hooks/use-toast"; -import { useMobxStore } from "lib/mobx/store-provider"; export interface IInstanceGeneralForm { instance: IInstance; @@ -20,8 +20,8 @@ export interface GeneralFormValues { export const InstanceGeneralForm: FC = (props) => { const { instance, instanceAdmins } = props; - // store - const { instance: instanceStore } = useMobxStore(); + // store hooks + const { instance: instanceStore } = useApplication(); // toast const { setToastAlert } = useToast(); // form data diff --git a/web/components/instance/github-config-form.tsx b/web/components/instance/github-config-form.tsx index 293f9465e..dbda821ce 100644 --- a/web/components/instance/github-config-form.tsx +++ b/web/components/instance/github-config-form.tsx @@ -1,15 +1,13 @@ import { FC, useState } from "react"; import { Controller, useForm } from "react-hook-form"; +import { Copy, Eye, EyeOff } from "lucide-react"; // ui import { Button, Input } from "@plane/ui"; // types import { IFormattedInstanceConfiguration } from "types/instance"; // hooks +import { useApplication } from "hooks/store"; import useToast from "hooks/use-toast"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; -// icons -import { Copy, Eye, EyeOff } from "lucide-react"; export interface IInstanceGithubConfigForm { config: IFormattedInstanceConfiguration; @@ -24,8 +22,8 @@ export const InstanceGithubConfigForm: FC = (props) = const { config } = props; // states const [showPassword, setShowPassword] = useState(false); - // store - const { instance: instanceStore } = useMobxStore(); + // store hooks + const { instance: instanceStore } = useApplication(); // toast const { setToastAlert } = useToast(); // form data diff --git a/web/components/instance/google-config-form.tsx b/web/components/instance/google-config-form.tsx index 88b1fd12e..41455290a 100644 --- a/web/components/instance/google-config-form.tsx +++ b/web/components/instance/google-config-form.tsx @@ -1,15 +1,13 @@ import { FC } from "react"; import { Controller, useForm } from "react-hook-form"; +import { Copy } from "lucide-react"; // ui import { Button, Input } from "@plane/ui"; // types import { IFormattedInstanceConfiguration } from "types/instance"; // hooks +import { useApplication } from "hooks/store"; import useToast from "hooks/use-toast"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; -// icons -import { Copy } from "lucide-react"; export interface IInstanceGoogleConfigForm { config: IFormattedInstanceConfiguration; @@ -22,8 +20,8 @@ export interface GoogleConfigFormValues { export const InstanceGoogleConfigForm: FC = (props) => { const { config } = props; - // store - const { instance: instanceStore } = useMobxStore(); + // store hooks + const { instance: instanceStore } = useApplication(); // toast const { setToastAlert } = useToast(); // form data diff --git a/web/components/instance/help-section.tsx b/web/components/instance/help-section.tsx index 635dc8264..30cc2a363 100644 --- a/web/components/instance/help-section.tsx +++ b/web/components/instance/help-section.tsx @@ -1,10 +1,10 @@ import { FC, useState, useRef } from "react"; import { Transition } from "@headlessui/react"; import Link from "next/link"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; -// icons import { FileText, HelpCircle, MessagesSquare, MoveLeft } from "lucide-react"; +// hooks +import { useApplication } from "hooks/store"; +// icons import { DiscordIcon, GithubIcon } from "@plane/ui"; // assets import packageJson from "package.json"; @@ -39,7 +39,7 @@ export const InstanceHelpSection: FC = () => { // store const { theme: { sidebarCollapsed, toggleSidebar }, - } = useMobxStore(); + } = useApplication(); // refs const helpOptionsRef = useRef(null); diff --git a/web/components/instance/image-config-form.tsx b/web/components/instance/image-config-form.tsx index 6cb00314e..b767ba7df 100644 --- a/web/components/instance/image-config-form.tsx +++ b/web/components/instance/image-config-form.tsx @@ -1,15 +1,13 @@ import { FC, useState } from "react"; import { Controller, useForm } from "react-hook-form"; +import { Eye, EyeOff } from "lucide-react"; // ui import { Button, Input } from "@plane/ui"; // types import { IFormattedInstanceConfiguration } from "types/instance"; // hooks +import { useApplication } from "hooks/store"; import useToast from "hooks/use-toast"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; -// icons -import { Eye, EyeOff } from "lucide-react"; export interface IInstanceImageConfigForm { config: IFormattedInstanceConfiguration; @@ -23,8 +21,8 @@ export const InstanceImageConfigForm: FC = (props) => const { config } = props; // states const [showPassword, setShowPassword] = useState(false); - // store - const { instance: instanceStore } = useMobxStore(); + // store hooks + const { instance: instanceStore } = useApplication(); // toast const { setToastAlert } = useToast(); // form data diff --git a/web/components/instance/setup-done-view.tsx b/web/components/instance/setup-done-view.tsx index 6c1c53f7c..163b00ca5 100644 --- a/web/components/instance/setup-done-view.tsx +++ b/web/components/instance/setup-done-view.tsx @@ -1,6 +1,8 @@ import React, { useState } from "react"; import Image from "next/image"; import { useTheme } from "next-themes"; +// hooks +import { useApplication } from "hooks/store"; // ui import { Button } from "@plane/ui"; import { UserCog2 } from "lucide-react"; @@ -8,17 +10,16 @@ import { UserCog2 } from "lucide-react"; import instanceSetupDone from "public/instance-setup-done.webp"; import PlaneBlackLogo from "public/plane-logos/black-horizontal-with-blue-logo.svg"; import PlaneWhiteLogo from "public/plane-logos/white-horizontal-with-blue-logo.svg"; -import { useMobxStore } from "lib/mobx/store-provider"; export const InstanceSetupDone = () => { // states const [isRedirecting, setIsRedirecting] = useState(false); // next-themes const { resolvedTheme } = useTheme(); - // mobx store + // store hooks const { instance: { fetchInstanceInfo }, - } = useMobxStore(); + } = useApplication(); const planeLogo = resolvedTheme === "dark" ? PlaneWhiteLogo : PlaneBlackLogo; diff --git a/web/components/instance/setup-form/sign-in-form.tsx b/web/components/instance/setup-form/sign-in-form.tsx index c4fd0dbe0..2b5c94d19 100644 --- a/web/components/instance/setup-form/sign-in-form.tsx +++ b/web/components/instance/setup-form/sign-in-form.tsx @@ -1,11 +1,10 @@ import { FC } from "react"; import { useForm, Controller } from "react-hook-form"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +import { XCircle } from "lucide-react"; +// hooks +import { useUser } from "hooks/store"; // ui import { Input, Button } from "@plane/ui"; -// icons -import { XCircle } from "lucide-react"; // services import { AuthService } from "services/auth.service"; const authService = new AuthService(); @@ -25,9 +24,8 @@ export interface IInstanceSetupEmailForm { export const InstanceSetupSignInForm: FC = (props) => { const { handleNextStep } = props; - const { - user: { fetchCurrentUser }, - } = useMobxStore(); + // store hooks + const { fetchCurrentUser } = useUser(); // form info const { control, diff --git a/web/components/instance/setup-view.tsx b/web/components/instance/setup-view.tsx index 0b56b90bf..e71f61cbc 100644 --- a/web/components/instance/setup-view.tsx +++ b/web/components/instance/setup-view.tsx @@ -4,15 +4,13 @@ import Image from "next/image"; // components import { InstanceSetupFormRoot } from "components/instance"; // hooks -import { useMobxStore } from "lib/mobx/store-provider"; +import { useUser } from "hooks/store"; // images import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png"; export const InstanceSetupView = observer(() => { - // store - const { - user: { fetchCurrentUser }, - } = useMobxStore(); + // store hooks + const { fetchCurrentUser } = useUser(); const mutateUserInfo = useCallback(() => { fetchCurrentUser(); diff --git a/web/components/instance/sidebar-dropdown.tsx b/web/components/instance/sidebar-dropdown.tsx index 7420450c1..2bac426d6 100644 --- a/web/components/instance/sidebar-dropdown.tsx +++ b/web/components/instance/sidebar-dropdown.tsx @@ -8,8 +8,8 @@ import { mutate } from "swr"; import { Menu, Transition } from "@headlessui/react"; // icons import { LogIn, LogOut, Settings, UserCog2 } from "lucide-react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication, useUser } from "hooks/store"; // hooks import useToast from "hooks/use-toast"; // ui @@ -26,13 +26,14 @@ const PROFILE_LINKS = [ ]; export const InstanceSidebarDropdown = observer(() => { + // router const router = useRouter(); - // store + // store hooks const { theme: { sidebarCollapsed }, - workspace: { workspaceSlug }, - user: { signOut, currentUser, currentUserSettings }, - } = useMobxStore(); + router: { workspaceSlug }, + } = useApplication(); + const { signOut, currentUser, currentUserSettings } = useUser(); // hooks const { setToastAlert } = useToast(); const { setTheme } = useTheme(); diff --git a/web/components/instance/sidebar-menu.tsx b/web/components/instance/sidebar-menu.tsx index 657283eba..aa6779d38 100644 --- a/web/components/instance/sidebar-menu.tsx +++ b/web/components/instance/sidebar-menu.tsx @@ -1,9 +1,8 @@ import Link from "next/link"; import { useRouter } from "next/router"; -// icons import { Image, BrainCog, Cog, Lock, Mail } from "lucide-react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication } from "hooks/store"; // ui import { Tooltip } from "@plane/ui"; @@ -41,9 +40,10 @@ const INSTANCE_ADMIN_LINKS = [ ]; export const InstanceAdminSidebarMenu = () => { + // store hooks const { theme: { sidebarCollapsed }, - } = useMobxStore(); + } = useApplication(); // router const router = useRouter(); diff --git a/web/components/integration/github/auth.tsx b/web/components/integration/github/auth.tsx index 9d5816f3b..58f061828 100644 --- a/web/components/integration/github/auth.tsx +++ b/web/components/integration/github/auth.tsx @@ -1,11 +1,11 @@ +import { observer } from "mobx-react-lite"; // hooks +import { useApplication } from "hooks/store"; import useIntegrationPopup from "hooks/use-integration-popup"; // ui import { Button } from "@plane/ui"; // types import { IWorkspaceIntegration } from "types"; -import { observer } from "mobx-react-lite"; -import { useMobxStore } from "lib/mobx/store-provider"; type Props = { workspaceIntegration: false | IWorkspaceIntegration | undefined; @@ -13,9 +13,10 @@ type Props = { }; export const GithubAuth: React.FC = observer(({ workspaceIntegration, provider }) => { + // store hooks const { - appConfig: { envConfig }, - } = useMobxStore(); + config: { envConfig }, + } = useApplication(); // hooks const { startAuth, isConnecting } = useIntegrationPopup({ provider, diff --git a/web/components/integration/github/import-data.tsx b/web/components/integration/github/import-data.tsx index 53592420d..5f53a5874 100644 --- a/web/components/integration/github/import-data.tsx +++ b/web/components/integration/github/import-data.tsx @@ -1,9 +1,8 @@ import { FC } from "react"; -import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { Control, Controller, UseFormWatch } from "react-hook-form"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useProject } from "hooks/store"; // components import { SelectRepository, TFormValues, TIntegrationSteps } from "components/integration"; // ui @@ -22,21 +21,18 @@ type Props = { export const GithubImportData: FC = observer((props) => { const { handleStepChange, integration, control, watch } = props; + // store hooks + const { workspaceProjects, getProjectById } = useProject(); - const router = useRouter(); - const { workspaceSlug } = router.query; + const options = workspaceProjects?.map((projectId) => { + const projectDetails = getProjectById(projectId); - const { project: projectStore } = useMobxStore(); - - const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : undefined; - - const options = projects - ? projects.map((project) => ({ - value: project.id, - query: project.name, - content:

{truncateText(project.name, 25)}

, - })) - : undefined; + return { + value: `${projectDetails?.id}`, + query: `${projectDetails?.name}`, + content:

{truncateText(projectDetails?.name ?? "", 25)}

, + }; + }); return (
@@ -74,7 +70,7 @@ export const GithubImportData: FC = observer((props) => {

Select the project to import the issues to.

- {projects && ( + {workspaceProjects && ( = observer((props) => { p.id === value)?.name - ) : ( - Select Project - ) + value ? getProjectById(value)?.name : Select Project } onChange={onChange} options={options} diff --git a/web/components/integration/jira/give-details.tsx b/web/components/integration/jira/give-details.tsx index 2ada332fc..58ded5411 100644 --- a/web/components/integration/jira/give-details.tsx +++ b/web/components/integration/jira/give-details.tsx @@ -1,28 +1,23 @@ import React from "react"; -import { useRouter } from "next/router"; import Link from "next/link"; import { observer } from "mobx-react-lite"; import { useFormContext, Controller } from "react-hook-form"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; -// icons import { Plus } from "lucide-react"; +// hooks +import { useApplication, useProject } from "hooks/store"; // components import { CustomSelect, Input } from "@plane/ui"; // types import { IJiraImporterForm } from "types"; export const JiraGetImportDetail: React.FC = observer(() => { - const router = useRouter(); - const { workspaceSlug } = router.query; - + // store hooks const { - project: projectStore, commandPalette: commandPaletteStore, - trackEvent: { setTrackElement }, - } = useMobxStore(); - const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : undefined; - + eventTracker: { setTrackElement }, + } = useApplication(); + const { workspaceProjects, getProjectById } = useProject(); + // form info const { control, formState: { errors }, @@ -170,20 +165,26 @@ export const JiraGetImportDetail: React.FC = observer(() => { onChange={onChange} label={ - {value && value !== "" ? ( - projects?.find((p) => p.id === value)?.name + {value && value.trim() !== "" ? ( + getProjectById(value)?.name ) : ( Select a project )} } > - {projects && projects.length > 0 ? ( - projects.map((project) => ( - - {project.name} - - )) + {workspaceProjects && workspaceProjects.length > 0 ? ( + workspaceProjects.map((projectId) => { + const projectDetails = getProjectById(projectId); + + if (!projectDetails) return; + + return ( + + {projectDetails.name} + + ); + }) ) : (

You don{"'"}t have any project. Please create a project first.

diff --git a/web/components/integration/single-integration-card.tsx b/web/components/integration/single-integration-card.tsx index e07f580e7..3b97967c2 100644 --- a/web/components/integration/single-integration-card.tsx +++ b/web/components/integration/single-integration-card.tsx @@ -2,12 +2,12 @@ import { useState } from "react"; import Image from "next/image"; import { useRouter } from "next/router"; - +import { observer } from "mobx-react-lite"; import useSWR, { mutate } from "swr"; - // services import { IntegrationService } from "services/integrations"; // hooks +import { useApplication, useUser } from "hooks/store"; import useToast from "hooks/use-toast"; import useIntegrationPopup from "hooks/use-integration-popup"; // ui @@ -20,8 +20,6 @@ import { CheckCircle } from "lucide-react"; import { IAppIntegration, IWorkspaceIntegration } from "types"; // fetch-keys import { WORKSPACE_INTEGRATIONS } from "constants/fetch-keys"; -import { observer } from "mobx-react-lite"; -import { useMobxStore } from "lib/mobx/store-provider"; type Props = { integration: IAppIntegration; @@ -44,20 +42,23 @@ const integrationDetails: { [key: string]: any } = { const integrationService = new IntegrationService(); export const SingleIntegrationCard: React.FC = observer(({ integration }) => { - const { - appConfig: { envConfig }, - user: { currentWorkspaceRole }, - } = useMobxStore(); - - const isUserAdmin = currentWorkspaceRole === 20; - + // states const [deletingIntegration, setDeletingIntegration] = useState(false); - + // router const router = useRouter(); const { workspaceSlug } = router.query; - + // store hooks + const { + config: { envConfig }, + } = useApplication(); + const { + membership: { currentWorkspaceRole }, + } = useUser(); + // toast alert const { setToastAlert } = useToast(); + const isUserAdmin = currentWorkspaceRole === 20; + const { startAuth, isConnecting: isInstalling } = useIntegrationPopup({ provider: integration.provider, github_app_name: envConfig?.github_app_name || "", diff --git a/web/components/integration/slack/select-channel.tsx b/web/components/integration/slack/select-channel.tsx index a746569fe..b2a54e2f1 100644 --- a/web/components/integration/slack/select-channel.tsx +++ b/web/components/integration/slack/select-channel.tsx @@ -2,18 +2,17 @@ import { useState, useEffect } from "react"; import { useRouter } from "next/router"; import useSWR, { mutate } from "swr"; import { observer } from "mobx-react-lite"; +// hooks +import { useApplication } from "hooks/store"; +import useIntegrationPopup from "hooks/use-integration-popup"; // services import { AppInstallationService } from "services/app_installation.service"; // ui import { Loader } from "@plane/ui"; -// hooks -import useIntegrationPopup from "hooks/use-integration-popup"; // types import { IWorkspaceIntegration, ISlackIntegration } from "types"; // fetch-keys import { SLACK_CHANNEL_INFO } from "constants/fetch-keys"; -// lib -import { useMobxStore } from "lib/mobx/store-provider"; type Props = { integration: IWorkspaceIntegration; @@ -22,10 +21,10 @@ type Props = { const appInstallationService = new AppInstallationService(); export const SelectChannel: React.FC = observer(({ integration }) => { - // store + // store hooks const { - appConfig: { envConfig }, - } = useMobxStore(); + config: { envConfig }, + } = useApplication(); // states const [slackChannelAvailabilityToggle, setSlackChannelAvailabilityToggle] = useState(false); const [slackChannel, setSlackChannel] = useState(null); diff --git a/web/components/issues/attachment/attachment-upload.tsx b/web/components/issues/attachment/attachment-upload.tsx index c1b323e74..f0debffbd 100644 --- a/web/components/issues/attachment/attachment-upload.tsx +++ b/web/components/issues/attachment/attachment-upload.tsx @@ -3,12 +3,11 @@ import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { mutate } from "swr"; import { useDropzone } from "react-dropzone"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication } from "hooks/store"; +import useToast from "hooks/use-toast"; // services import { IssueAttachmentService } from "services/issue"; -// hooks -import useToast from "hooks/use-toast"; // types import { IIssueAttachment } from "types"; // fetch-keys @@ -29,12 +28,12 @@ export const IssueAttachmentUpload: React.FC = observer((props) => { // router const router = useRouter(); const { workspaceSlug, projectId, issueId } = router.query; - + // toast alert const { setToastAlert } = useToast(); - + // store hooks const { - appConfig: { envConfig }, - } = useMobxStore(); + config: { envConfig }, + } = useApplication(); const onDrop = useCallback((acceptedFiles: File[]) => { if (!acceptedFiles[0] || !workspaceSlug) return; diff --git a/web/components/issues/comment/comment-card.tsx b/web/components/issues/comment/comment-card.tsx index 09f29da73..63b46258b 100644 --- a/web/components/issues/comment/comment-card.tsx +++ b/web/components/issues/comment/comment-card.tsx @@ -1,12 +1,13 @@ import React, { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; - +import { observer } from "mobx-react-lite"; +// hooks +import { useUser } from "hooks/store"; +import useEditorSuggestions from "hooks/use-editor-suggestions"; // services import { FileService } from "services/file.service"; // icons import { Check, Globe2, Lock, MessageSquare, Pencil, Trash2, X } from "lucide-react"; -// hooks -import useUser from "hooks/use-user"; // ui import { CustomMenu } from "@plane/ui"; import { CommentReaction } from "components/issues"; @@ -15,7 +16,6 @@ import { LiteTextEditorWithRef, LiteReadOnlyEditorWithRef } from "@plane/lite-te import { timeAgo } from "helpers/date-time.helper"; // types import type { IIssueActivity } from "types"; -import useEditorSuggestions from "hooks/use-editor-suggestions"; // services const fileService = new FileService(); @@ -28,22 +28,18 @@ type Props = { workspaceSlug: string; }; -export const CommentCard: React.FC = ({ - comment, - handleCommentDeletion, - onSubmit, - showAccessSpecifier = false, - workspaceSlug, -}) => { - const { user } = useUser(); - +export const CommentCard: React.FC = observer((props) => { + const { comment, handleCommentDeletion, onSubmit, showAccessSpecifier = false, workspaceSlug } = props; + // states + const [isEditing, setIsEditing] = useState(false); + // refs const editorRef = React.useRef(null); const showEditorRef = React.useRef(null); const editorSuggestions = useEditorSuggestions(); - - const [isEditing, setIsEditing] = useState(false); - + // store hooks + const { currentUser } = useUser(); + // form info const { formState: { isSubmitting }, handleSubmit, @@ -152,7 +148,7 @@ export const CommentCard: React.FC = ({
- {user?.id === comment.actor && ( + {currentUser?.id === comment.actor && ( setIsEditing(true)} className="flex items-center gap-1"> @@ -192,4 +188,4 @@ export const CommentCard: React.FC = ({ )}
); -}; +}); diff --git a/web/components/issues/comment/comment-reaction.tsx b/web/components/issues/comment/comment-reaction.tsx index c920caeba..f9eb474af 100644 --- a/web/components/issues/comment/comment-reaction.tsx +++ b/web/components/issues/comment/comment-reaction.tsx @@ -1,12 +1,14 @@ import { FC } from "react"; import { useRouter } from "next/router"; +import { observer } from "mobx-react-lite"; // hooks -import useUser from "hooks/use-user"; +import { useUser } from "hooks/store"; import useCommentReaction from "hooks/use-comment-reaction"; // ui import { ReactionSelector } from "components/core"; // helper import { renderEmoji } from "helpers/emoji.helper"; +// types import { IssueCommentReaction } from "types"; type Props = { @@ -15,13 +17,13 @@ type Props = { readonly?: boolean; }; -export const CommentReaction: FC = (props) => { +export const CommentReaction: FC = observer((props) => { const { projectId, commentId, readonly = false } = props; - + // router const router = useRouter(); const { workspaceSlug } = router.query; - - const { user } = useUser(); + // store hooks + const { currentUser } = useUser(); const { commentReactions, groupedReactions, handleReactionCreate, handleReactionDelete } = useCommentReaction( workspaceSlug, @@ -33,7 +35,7 @@ export const CommentReaction: FC = (props) => { if (!workspaceSlug || !projectId || !commentId) return; const isSelected = commentReactions?.some( - (r: IssueCommentReaction) => r.actor === user?.id && r.reaction === reaction + (r: IssueCommentReaction) => r.actor === currentUser?.id && r.reaction === reaction ); if (isSelected) { @@ -51,7 +53,7 @@ export const CommentReaction: FC = (props) => { position="top" value={ commentReactions - ?.filter((reaction: IssueCommentReaction) => reaction.actor === user?.id) + ?.filter((reaction: IssueCommentReaction) => reaction.actor === currentUser?.id) .map((r: IssueCommentReaction) => r.reaction) || [] } onSelect={handleReactionClick} @@ -70,7 +72,9 @@ export const CommentReaction: FC = (props) => { }} key={reaction} className={`flex h-full items-center gap-1 rounded-md px-2 py-1 text-sm text-custom-text-100 ${ - commentReactions?.some((r: IssueCommentReaction) => r.actor === user?.id && r.reaction === reaction) + commentReactions?.some( + (r: IssueCommentReaction) => r.actor === currentUser?.id && r.reaction === reaction + ) ? "bg-custom-primary-100/10" : "bg-custom-background-80" }`} @@ -78,7 +82,9 @@ export const CommentReaction: FC = (props) => { {renderEmoji(reaction)} r.actor === user?.id && r.reaction === reaction) + commentReactions?.some( + (r: IssueCommentReaction) => r.actor === currentUser?.id && r.reaction === reaction + ) ? "text-custom-primary-100" : "" } @@ -90,4 +96,4 @@ export const CommentReaction: FC = (props) => { )}
); -}; +}); diff --git a/web/components/issues/delete-draft-issue-modal.tsx b/web/components/issues/delete-draft-issue-modal.tsx index 955d8ac78..c794d12e4 100644 --- a/web/components/issues/delete-draft-issue-modal.tsx +++ b/web/components/issues/delete-draft-issue-modal.tsx @@ -1,9 +1,6 @@ import React, { useEffect, 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"; // services import { IssueDraftService } from "services/issue"; // hooks @@ -24,17 +21,14 @@ type Props = { const issueDraftService = new IssueDraftService(); -export const DeleteDraftIssueModal: React.FC = observer((props) => { +export const DeleteDraftIssueModal: React.FC = (props) => { const { isOpen, handleClose, data, onSubmit } = props; - + // states const [isDeleteLoading, setIsDeleteLoading] = useState(false); - - const { user: userStore } = useMobxStore(); - const user = userStore.currentUser; - + // router const router = useRouter(); const { workspaceSlug } = router.query; - + // toast alert const { setToastAlert } = useToast(); useEffect(() => { @@ -47,12 +41,12 @@ export const DeleteDraftIssueModal: React.FC = observer((props) => { }; const handleDeletion = async () => { - if (!workspaceSlug || !data || !user) return; + if (!workspaceSlug || !data) return; setIsDeleteLoading(true); await issueDraftService - .deleteDraftIssue(workspaceSlug as string, data.project, data.id) + .deleteDraftIssue(workspaceSlug.toString(), data.project, data.id) .then(() => { setIsDeleteLoading(false); handleClose(); @@ -138,4 +132,4 @@ export const DeleteDraftIssueModal: React.FC = observer((props) => { ); -}); +}; diff --git a/web/components/issues/draft-issue-form.tsx b/web/components/issues/draft-issue-form.tsx index a556c9485..d93daa686 100644 --- a/web/components/issues/draft-issue-form.tsx +++ b/web/components/issues/draft-issue-form.tsx @@ -1,12 +1,16 @@ import React, { FC, useState, useEffect, useRef } from "react"; import { useRouter } from "next/router"; import { Controller, useForm } from "react-hook-form"; +import { observer } from "mobx-react-lite"; +import { Sparkle, X } from "lucide-react"; +// hooks +import { useApplication } from "hooks/store"; +import useToast from "hooks/use-toast"; +import useLocalStorage from "hooks/use-local-storage"; +import useEditorSuggestions from "hooks/use-editor-suggestions"; // services import { AIService } from "services/ai.service"; import { FileService } from "services/file.service"; -// hooks -import useToast from "hooks/use-toast"; -import useLocalStorage from "hooks/use-local-storage"; // components import { GptAssistantModal } from "components/core"; import { ParentIssuesListModal } from "components/issues"; @@ -21,18 +25,11 @@ import { } from "components/issues/select"; import { CreateStateModal } from "components/states"; import { CreateLabelModal } from "components/labels"; +import { RichTextEditorWithRef } from "@plane/rich-text-editor"; // ui -import {} from "components/ui"; import { Button, CustomMenu, Input, ToggleSwitch } from "@plane/ui"; -// icons -import { Sparkle, X } from "lucide-react"; // types import type { IUser, IIssue, ISearchIssueResponse } from "types"; -// components -import { RichTextEditorWithRef } from "@plane/rich-text-editor"; -import useEditorSuggestions from "hooks/use-editor-suggestions"; -import { observer } from "mobx-react-lite"; -import { useMobxStore } from "lib/mobx/store-provider"; const aiService = new AIService(); const fileService = new FileService(); @@ -123,8 +120,8 @@ export const DraftIssueForm: FC = observer((props) => { const { workspaceSlug } = router.query; // store const { - appConfig: { envConfig }, - } = useMobxStore(); + config: { envConfig }, + } = useApplication(); // form info const { formState: { errors, isSubmitting }, diff --git a/web/components/issues/form.tsx b/web/components/issues/form.tsx index bc325d95e..7af2688bf 100644 --- a/web/components/issues/form.tsx +++ b/web/components/issues/form.tsx @@ -2,8 +2,10 @@ import React, { FC, useState, useEffect, useRef } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { Controller, useForm } from "react-hook-form"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +import { LayoutPanelTop, Sparkle, X } from "lucide-react"; +// hooks +import { useApplication, useUser } from "hooks/store"; +import useEditorSuggestions from "hooks/use-editor-suggestions"; // services import { AIService } from "services/ai.service"; import { FileService } from "services/file.service"; @@ -25,15 +27,11 @@ import { } from "components/issues/select"; import { CreateStateModal } from "components/states"; import { CreateLabelModal } from "components/labels"; +import { RichTextEditorWithRef } from "@plane/rich-text-editor"; // ui import { Button, CustomMenu, Input, ToggleSwitch } from "@plane/ui"; -// icons -import { LayoutPanelTop, Sparkle, X } from "lucide-react"; // types import type { IIssue, ISearchIssueResponse } from "types"; -// components -import { RichTextEditorWithRef } from "@plane/rich-text-editor"; -import useEditorSuggestions from "hooks/use-editor-suggestions"; const defaultValues: Partial = { project: "", @@ -106,12 +104,11 @@ export const IssueForm: FC = observer((props) => { // router const router = useRouter(); const { workspaceSlug } = router.query; - // store + // store hooks const { - user: userStore, - appConfig: { envConfig }, - } = useMobxStore(); - const user = userStore.currentUser; + config: { envConfig }, + } = useApplication(); + const {} = useUser(); // hooks const editorSuggestion = useEditorSuggestions(); const { setToastAlert } = useToast(); @@ -183,12 +180,12 @@ export const IssueForm: FC = observer((props) => { }; const handleAutoGenerateDescription = async () => { - if (!workspaceSlug || !projectId || !user) return; + if (!workspaceSlug || !projectId) return; setIAmFeelingLucky(true); aiService - .createGptTask(workspaceSlug as string, projectId as string, { + .createGptTask(workspaceSlug.toString(), projectId.toString(), { prompt: issueName, task: "Generate a proper description for this issue.", }) diff --git a/web/components/issues/issue-layouts/calendar/quick-add-issue-form.tsx b/web/components/issues/issue-layouts/calendar/quick-add-issue-form.tsx index 70f79b4fa..b92d0e214 100644 --- a/web/components/issues/issue-layouts/calendar/quick-add-issue-form.tsx +++ b/web/components/issues/issue-layouts/calendar/quick-add-issue-form.tsx @@ -2,9 +2,8 @@ import { useEffect, useRef, useState } from "react"; import { useRouter } from "next/router"; import { useForm } from "react-hook-form"; import { observer } from "mobx-react-lite"; -// store -import { useMobxStore } from "lib/mobx/store-provider"; // hooks +import { useProject, useWorkspace } from "hooks/store"; import useToast from "hooks/use-toast"; import useKeypress from "hooks/use-keypress"; import useOutsideClickDetector from "hooks/use-outside-click-detector"; @@ -13,7 +12,7 @@ import { createIssuePayload } from "helpers/issue.helper"; // icons import { PlusIcon } from "lucide-react"; // types -import { IIssue, IProject } from "types"; +import { IIssue } from "types"; type Props = { formKey: keyof IIssue; @@ -58,25 +57,22 @@ const Inputs = (props: any) => { export const CalendarQuickAddIssueForm: React.FC = observer((props) => { const { formKey, groupId, prePopulatedData, quickAddCallback, viewId } = props; - // router const router = useRouter(); - const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string }; - - const { workspace: workspaceStore, project: projectStore } = useMobxStore(); - - // ref + const { workspaceSlug, projectId } = router.query; + // store hooks + const { getProjectById } = useProject(); + const { getWorkspaceBySlug } = useWorkspace(); + // refs const ref = useRef(null); - // states const [isOpen, setIsOpen] = useState(false); - + // toast alert const { setToastAlert } = useToast(); // derived values - const workspaceDetail = (workspaceSlug && workspaceStore.getWorkspaceBySlug(workspaceSlug)) || null; - const projectDetail: IProject | null = - (workspaceSlug && projectId && projectStore.getProjectById(workspaceSlug, projectId)) || null; + const workspaceDetail = (workspaceSlug && getWorkspaceBySlug(workspaceSlug.toString())) || null; + const projectDetail = projectId ? getProjectById(projectId.toString()) : null; const { reset, @@ -112,7 +108,7 @@ export const CalendarQuickAddIssueForm: React.FC = observer((props) => { }, [errors, setToastAlert]); const onSubmitHandler = async (formData: IIssue) => { - if (isSubmitting || !groupId || !workspaceDetail || !projectDetail) return; + if (isSubmitting || !groupId || !workspaceDetail || !projectDetail || !workspaceSlug || !projectId) return; reset({ ...defaultValues }); @@ -124,8 +120,8 @@ export const CalendarQuickAddIssueForm: React.FC = observer((props) => { try { quickAddCallback && (await quickAddCallback( - workspaceSlug, - projectId, + workspaceSlug.toString(), + projectId.toString(), { ...payload, }, diff --git a/web/components/issues/issue-layouts/empty-states/global-view.tsx b/web/components/issues/issue-layouts/empty-states/global-view.tsx index d4348c4bf..7f8ac7e86 100644 --- a/web/components/issues/issue-layouts/empty-states/global-view.tsx +++ b/web/components/issues/issue-layouts/empty-states/global-view.tsx @@ -1,31 +1,24 @@ -// next -import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +import { Plus, PlusIcon } from "lucide-react"; +// hooks +import { useApplication, useProject } from "hooks/store"; // components import { EmptyState } from "components/common"; // assets import emptyIssue from "public/empty-state/issue.svg"; import emptyProject from "public/empty-state/project.svg"; -// icons -import { Plus, PlusIcon } from "lucide-react"; export const GlobalViewEmptyState: React.FC = observer(() => { - const router = useRouter(); - const { workspaceSlug } = router.query; - + // store hooks const { - commandPalette: commandPaletteStore, - project: projectStore, - trackEvent: { setTrackElement }, - } = useMobxStore(); - - const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : null; + commandPalette: { toggleCreateIssueModal, toggleCreateProjectModal }, + eventTracker: { setTrackElement }, + } = useApplication(); + const { workspaceProjects } = useProject(); return (
- {!projects || projects?.length === 0 ? ( + {!workspaceProjects || workspaceProjects?.length === 0 ? ( { text: "New Project", onClick: () => { setTrackElement("ALL_ISSUES_EMPTY_STATE"); - commandPaletteStore.toggleCreateProjectModal(true); + toggleCreateProjectModal(true); }, }} /> @@ -49,7 +42,7 @@ export const GlobalViewEmptyState: React.FC = observer(() => { icon: , onClick: () => { setTrackElement("ALL_ISSUES_EMPTY_STATE"); - commandPaletteStore.toggleCreateIssueModal(true); + toggleCreateIssueModal(true); }, }} /> diff --git a/web/components/issues/issue-layouts/empty-states/project-view.tsx b/web/components/issues/issue-layouts/empty-states/project-view.tsx index 33919c832..d3a445b96 100644 --- a/web/components/issues/issue-layouts/empty-states/project-view.tsx +++ b/web/components/issues/issue-layouts/empty-states/project-view.tsx @@ -1,7 +1,7 @@ import { observer } from "mobx-react-lite"; import { PlusIcon } from "lucide-react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication } from "hooks/store"; // components import { EmptyState } from "components/common"; // assets @@ -9,10 +9,11 @@ import emptyIssue from "public/empty-state/issue.svg"; import { EProjectStore } from "store_legacy/command-palette.store"; export const ProjectViewEmptyState: React.FC = observer(() => { + // store hooks const { commandPalette: commandPaletteStore, - trackEvent: { setTrackElement }, - } = useMobxStore(); + eventTracker: { setTrackElement }, + } = useApplication(); return (
diff --git a/web/components/issues/issue-layouts/empty-states/project.tsx b/web/components/issues/issue-layouts/empty-states/project.tsx index d708cb69c..d6e863b1d 100644 --- a/web/components/issues/issue-layouts/empty-states/project.tsx +++ b/web/components/issues/issue-layouts/empty-states/project.tsx @@ -1,7 +1,7 @@ import { observer } from "mobx-react-lite"; import { PlusIcon } from "lucide-react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication } from "hooks/store"; // components import { NewEmptyState } from "components/common/new-empty-state"; // assets @@ -9,10 +9,11 @@ import emptyIssue from "public/empty-state/empty_issues.webp"; import { EProjectStore } from "store_legacy/command-palette.store"; export const ProjectEmptyState: React.FC = observer(() => { + // store hooks const { commandPalette: commandPaletteStore, - trackEvent: { setTrackElement }, - } = useMobxStore(); + eventTracker: { setTrackElement }, + } = useApplication(); return (
diff --git a/web/components/issues/issue-layouts/gantt/base-gantt-root.tsx b/web/components/issues/issue-layouts/gantt/base-gantt-root.tsx index d6a71a923..861826e7c 100644 --- a/web/components/issues/issue-layouts/gantt/base-gantt-root.tsx +++ b/web/components/issues/issue-layouts/gantt/base-gantt-root.tsx @@ -1,8 +1,8 @@ import React from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useUser } from "hooks/store"; // components import { IssueGanttBlock, IssuePeekOverview } from "components/issues"; import { @@ -39,13 +39,13 @@ interface IBaseGanttRoot { export const BaseGanttRoot: React.FC = observer((props: IBaseGanttRoot) => { const { issueFiltersStore, issueStore, viewId } = props; - + // router const router = useRouter(); const { workspaceSlug, peekIssueId, peekProjectId } = router.query; - + // store hooks const { - user: { currentProjectRole }, - } = useMobxStore(); + membership: { currentProjectRole }, + } = useUser(); const appliedDisplayFilters = issueFiltersStore.issueFilters?.displayFilters; diff --git a/web/components/issues/issue-layouts/gantt/quick-add-issue-form.tsx b/web/components/issues/issue-layouts/gantt/quick-add-issue-form.tsx index e4a88e944..9e7232307 100644 --- a/web/components/issues/issue-layouts/gantt/quick-add-issue-form.tsx +++ b/web/components/issues/issue-layouts/gantt/quick-add-issue-form.tsx @@ -3,12 +3,10 @@ import { useRouter } from "next/router"; import { useForm } from "react-hook-form"; import { observer } from "mobx-react-lite"; import { PlusIcon } from "lucide-react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; // hooks +import { useProject, useWorkspace } from "hooks/store"; import useToast from "hooks/use-toast"; import useKeypress from "hooks/use-keypress"; -import useProjectDetails from "hooks/use-project-details"; import useOutsideClickDetector from "hooks/use-outside-click-detector"; // helpers import { renderDateFormat } from "helpers/date-time.helper"; @@ -55,16 +53,13 @@ const Inputs = (props: any) => { export const GanttInlineCreateIssueForm: React.FC = observer((props) => { const { prePopulatedData, quickAddCallback, viewId } = props; - // router const router = useRouter(); - const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string }; - - // store - const { workspace: workspaceStore } = useMobxStore(); - - const { projectDetails } = useProjectDetails(); - + const { workspaceSlug, projectId } = router.query; + // store hooks + const { getWorkspaceBySlug } = useWorkspace(); + const { currentProjectDetails } = useProject(); + // form info const { reset, handleSubmit, @@ -87,7 +82,7 @@ export const GanttInlineCreateIssueForm: React.FC = observer((props) => { const { setToastAlert } = useToast(); // derived values - const workspaceDetail = workspaceStore.getWorkspaceBySlug(workspaceSlug?.toString()!); + const workspaceDetail = getWorkspaceBySlug(workspaceSlug?.toString()!); useEffect(() => { if (!isOpen) reset({ ...defaultValues }); @@ -113,7 +108,7 @@ export const GanttInlineCreateIssueForm: React.FC = observer((props) => { // resetting the form so that user can add another issue quickly reset({ ...defaultValues, ...(prePopulatedData ?? {}) }); - const payload = createIssuePayload(workspaceDetail!, projectDetails!, { + const payload = createIssuePayload(workspaceDetail!, currentProjectDetails!, { ...(prePopulatedData ?? {}), ...formData, start_date: renderDateFormat(new Date()), @@ -122,7 +117,7 @@ export const GanttInlineCreateIssueForm: React.FC = observer((props) => { try { if (quickAddCallback) { - await quickAddCallback(workspaceSlug, projectId, payload, viewId); + await quickAddCallback(workspaceSlug.toString(), projectId.toString(), payload, viewId); } setToastAlert({ type: "success", @@ -152,7 +147,7 @@ export const GanttInlineCreateIssueForm: React.FC = observer((props) => { onSubmit={handleSubmit(onSubmitHandler)} >
-

{projectDetails?.identifier ?? "..."}

+

{currentProjectDetails?.identifier ?? "..."}

)} diff --git a/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx b/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx index 43b815615..cfcc83e12 100644 --- a/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx +++ b/web/components/issues/issue-layouts/kanban/base-kanban-root.tsx @@ -2,8 +2,9 @@ import { FC, useCallback, useState } from "react"; import { DragDropContext, DragStart, DraggableLocation, DropResult, Droppable } from "@hello-pangea/dnd"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useUser } from "hooks/store"; +import useToast from "hooks/use-toast"; // ui import { Spinner } from "@plane/ui"; // types @@ -24,8 +25,6 @@ import { } from "store_legacy/issues"; import { IQuickActionProps } from "../list/list-view-types"; import { IIssueKanBanViewStore } from "store_legacy/issue"; -// hooks -import useToast from "hooks/use-toast"; //components import { KanBan } from "./default"; import { KanBanSwimLanes } from "./swimlanes"; @@ -92,14 +91,13 @@ export const BaseKanBanRoot: React.FC = observer((props: IBas // router const router = useRouter(); const { workspaceSlug, peekIssueId, peekProjectId } = router.query; - // mobx store - const { user: userStore } = useMobxStore(); - - // hooks + // store hooks + const { + membership: { currentProjectRole }, + } = useUser(); + // toast alert const { setToastAlert } = useToast(); - const { currentProjectRole } = userStore; - const issues = issueStore?.getIssues || {}; const issueIds = issueStore?.getIssuesIds || []; diff --git a/web/components/issues/issue-layouts/kanban/quick-add-issue-form.tsx b/web/components/issues/issue-layouts/kanban/quick-add-issue-form.tsx index 0120e02f6..4a9a03aed 100644 --- a/web/components/issues/issue-layouts/kanban/quick-add-issue-form.tsx +++ b/web/components/issues/issue-layouts/kanban/quick-add-issue-form.tsx @@ -1,18 +1,17 @@ import { useEffect, useState, useRef } from "react"; import { useRouter } from "next/router"; import { useForm } from "react-hook-form"; -import { PlusIcon } from "lucide-react"; import { observer } from "mobx-react-lite"; -// store -import { useMobxStore } from "lib/mobx/store-provider"; +import { PlusIcon } from "lucide-react"; // hooks +import { useProject, useWorkspace } from "hooks/store"; import useToast from "hooks/use-toast"; import useKeypress from "hooks/use-keypress"; import useOutsideClickDetector from "hooks/use-outside-click-detector"; // helpers import { createIssuePayload } from "helpers/issue.helper"; // types -import { IIssue, IProject } from "types"; +import { IIssue } from "types"; const Inputs = (props: any) => { const { register, setFocus, projectDetail } = props; @@ -56,16 +55,15 @@ const defaultValues: Partial = { export const KanBanQuickAddIssueForm: React.FC = observer((props) => { const { formKey, groupId, prePopulatedData, quickAddCallback, viewId } = props; - // router const router = useRouter(); - const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string }; + const { workspaceSlug, projectId } = router.query; + // store hooks + const { getWorkspaceBySlug } = useWorkspace(); + const { getProjectById } = useProject(); - const { workspace: workspaceStore, project: projectStore } = useMobxStore(); - - const workspaceDetail = (workspaceSlug && workspaceStore.getWorkspaceBySlug(workspaceSlug)) || null; - const projectDetail: IProject | null = - (workspaceSlug && projectId && projectStore.getProjectById(workspaceSlug, projectId)) || null; + const workspaceDetail = workspaceSlug ? getWorkspaceBySlug(workspaceSlug.toString()) : null; + const projectDetail = projectId ? getProjectById(projectId.toString()) : null; const ref = useRef(null); @@ -89,7 +87,7 @@ export const KanBanQuickAddIssueForm: React.FC = obser }, [isOpen, reset]); const onSubmitHandler = async (formData: IIssue) => { - if (isSubmitting || !groupId || !workspaceDetail || !projectDetail) return; + if (isSubmitting || !groupId || !workspaceDetail || !projectDetail || !workspaceSlug || !projectId) return; reset({ ...defaultValues }); @@ -101,8 +99,8 @@ export const KanBanQuickAddIssueForm: React.FC = obser try { quickAddCallback && (await quickAddCallback( - workspaceSlug, - projectId, + workspaceSlug.toString(), + projectId.toString(), { ...payload, }, diff --git a/web/components/issues/issue-layouts/list/quick-add-issue-form.tsx b/web/components/issues/issue-layouts/list/quick-add-issue-form.tsx index 9237d8a1f..f5ffb5376 100644 --- a/web/components/issues/issue-layouts/list/quick-add-issue-form.tsx +++ b/web/components/issues/issue-layouts/list/quick-add-issue-form.tsx @@ -4,11 +4,10 @@ import { useForm } from "react-hook-form"; import { PlusIcon } from "lucide-react"; import { observer } from "mobx-react-lite"; // hooks +import { useProject, useWorkspace } from "hooks/store"; import useToast from "hooks/use-toast"; import useKeypress from "hooks/use-keypress"; import useOutsideClickDetector from "hooks/use-outside-click-detector"; -// store -import { useMobxStore } from "lib/mobx/store-provider"; // constants import { IIssue, IProject } from "types"; // types @@ -60,15 +59,12 @@ const defaultValues: Partial = { export const ListQuickAddIssueForm: FC = observer((props) => { const { prePopulatedData, quickAddCallback, viewId } = props; - + // router const router = useRouter(); - const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string }; - - const { workspace: workspaceStore, project: projectStore } = useMobxStore(); - - const workspaceDetail = (workspaceSlug && workspaceStore.getWorkspaceBySlug(workspaceSlug)) || null; - const projectDetail: IProject | null = - (workspaceSlug && projectId && projectStore.getProjectById(workspaceSlug, projectId)) || null; + const { workspaceSlug, projectId } = router.query; + // store hooks + const { currentWorkspace } = useWorkspace(); + const { currentProjectDetails } = useProject(); const ref = useRef(null); @@ -92,17 +88,18 @@ export const ListQuickAddIssueForm: FC = observer((props }, [isOpen, reset]); const onSubmitHandler = async (formData: IIssue) => { - if (isSubmitting || !workspaceDetail || !projectDetail) return; + if (isSubmitting || !currentWorkspace || !currentProjectDetails || !workspaceSlug || !projectId) return; reset({ ...defaultValues }); - const payload = createIssuePayload(workspaceDetail, projectDetail, { + const payload = createIssuePayload(currentWorkspace, currentProjectDetails, { ...(prePopulatedData ?? {}), ...formData, }); try { - quickAddCallback && (await quickAddCallback(workspaceSlug, projectId, { ...payload }, viewId)); + quickAddCallback && + (await quickAddCallback(workspaceSlug.toString(), projectId.toString(), { ...payload }, viewId)); setToastAlert({ type: "success", title: "Success!", @@ -130,7 +127,12 @@ export const ListQuickAddIssueForm: FC = observer((props onSubmit={handleSubmit(onSubmitHandler)} className="flex w-full items-center gap-x-3 border-[0.5px] border-t-0 border-custom-border-100 bg-custom-background-100 px-3" > - +
{`Press 'Enter' to add another issue`}
diff --git a/web/components/issues/issue-layouts/properties/state.tsx b/web/components/issues/issue-layouts/properties/state.tsx index ab8b226cd..6cd1f3348 100644 --- a/web/components/issues/issue-layouts/properties/state.tsx +++ b/web/components/issues/issue-layouts/properties/state.tsx @@ -1,18 +1,15 @@ import { Fragment, useState } from "react"; - import { observer } from "mobx-react-lite"; -import { useMobxStore } from "lib/mobx/store-provider"; - -// hooks import { usePopper } from "react-popper"; -// ui +import { Placement } from "@popperjs/core"; import { Combobox } from "@headlessui/react"; -import { StateGroupIcon, Tooltip } from "@plane/ui"; import { Check, ChevronDown, Search } from "lucide-react"; +// hooks +import { useApplication, useProjectState } from "hooks/store"; +// ui +import { StateGroupIcon, Tooltip } from "@plane/ui"; // types import { IState } from "types"; -import { Placement } from "@popperjs/core"; -import { RootStore } from "store_legacy/root"; export interface IIssuePropertyState { projectId: string | null; @@ -40,25 +37,24 @@ export const IssuePropertyState: React.FC = observer((props optionsClassName = "", placement, } = props; - - const { workspace: workspaceStore, projectState: projectStateStore }: RootStore = useMobxStore(); - const workspaceSlug = workspaceStore?.workspaceSlug; - + // states const [query, setQuery] = useState(""); const [referenceElement, setReferenceElement] = useState(null); const [popperElement, setPopperElement] = useState(null); const [isLoading, setIsLoading] = useState(false); + // store hooks + const { + router: { workspaceSlug }, + } = useApplication(); + const { projectStates: storeStates, fetchProjectStates } = useProjectState(); let projectStates: IState[] = defaultOptions; - const storeStates = projectId ? projectStateStore.states[projectId] : []; if (storeStates && storeStates.length > 0) projectStates = storeStates; - const fetchProjectStates = () => { + const handleFetchProjectStates = () => { setIsLoading(true); if (workspaceSlug && projectId) - workspaceSlug && - projectId && - projectStateStore.fetchProjectStates(workspaceSlug, projectId).then(() => setIsLoading(false)); + workspaceSlug && projectId && fetchProjectStates(workspaceSlug, projectId).then(() => setIsLoading(false)); }; const selectedOption: IState | undefined = @@ -121,7 +117,7 @@ export const IssuePropertyState: React.FC = observer((props className={`flex h-5 w-full items-center justify-between gap-1 rounded border-[0.5px] border-custom-border-300 px-2.5 py-1 text-xs ${ disabled ? "cursor-not-allowed text-custom-text-200" : "cursor-pointer hover:bg-custom-background-80" } ${buttonClassName}`} - onClick={() => !storeStates && fetchProjectStates()} + onClick={() => !storeStates && handleFetchProjectStates()} > {label} {!hideDropdownArrow && !disabled &&
)} diff --git a/web/components/issues/issue-reaction.tsx b/web/components/issues/issue-reaction.tsx index 695870e49..37d0599e4 100644 --- a/web/components/issues/issue-reaction.tsx +++ b/web/components/issues/issue-reaction.tsx @@ -1,11 +1,11 @@ +import { observer } from "mobx-react-lite"; // hooks +import { useUser } from "hooks/store"; import useIssueReaction from "hooks/use-issue-reaction"; // components import { ReactionSelector } from "components/core"; // string helpers import { renderEmoji } from "helpers/emoji.helper"; -import { observer } from "mobx-react-lite"; -import { useMobxStore } from "lib/mobx/store-provider"; // types type Props = { @@ -17,9 +17,7 @@ type Props = { export const IssueReaction: React.FC = observer((props) => { const { workspaceSlug, projectId, issueId } = props; - const { - user: { currentUser }, - } = useMobxStore(); + const { currentUser } = useUser(); const { reactions, groupedReactions, handleReactionCreate, handleReactionDelete } = useIssueReaction( workspaceSlug, diff --git a/web/components/issues/peek-overview/issue-detail.tsx b/web/components/issues/peek-overview/issue-detail.tsx index 610d0348d..03a3a197b 100644 --- a/web/components/issues/peek-overview/issue-detail.tsx +++ b/web/components/issues/peek-overview/issue-detail.tsx @@ -3,9 +3,8 @@ import { Controller, useForm } from "react-hook-form"; import debounce from "lodash/debounce"; // packages import { RichTextEditor } from "@plane/rich-text-editor"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; // hooks +import { useUser } from "hooks/store"; import useReloadConfirmations from "hooks/use-reload-confirmation"; import useEditorSuggestions from "hooks/use-editor-suggestions"; // components @@ -46,8 +45,9 @@ export const PeekOverviewIssueDetails: FC = (props) = setIsSubmitting, } = props; // store - const { user: userStore } = useMobxStore(); - const { currentProjectRole } = userStore; + const { + membership: { currentProjectRole }, + } = useUser(); const isAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER; // states const [characterLimit, setCharacterLimit] = useState(false); diff --git a/web/components/issues/select/module.tsx b/web/components/issues/select/module.tsx index 5874b50c1..4f47b0d2e 100644 --- a/web/components/issues/select/module.tsx +++ b/web/components/issues/select/module.tsx @@ -1,14 +1,12 @@ import React, { useState } from "react"; import { observer } from "mobx-react-lite"; import { usePopper } from "react-popper"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; -// ui import { Combobox } from "@headlessui/react"; +import { Check, Search } from "lucide-react"; +// hooks +import { useModule } from "hooks/store"; // icons import { DiceIcon } from "@plane/ui"; -// icons -import { Check, Search } from "lucide-react"; export interface IssueModuleSelectProps { workspaceSlug: string; @@ -19,37 +17,39 @@ export interface IssueModuleSelectProps { export const IssueModuleSelect: React.FC = observer((props) => { const { workspaceSlug, projectId, value, onChange } = props; + // states const [query, setQuery] = useState(""); - const [referenceElement, setReferenceElement] = useState(null); const [popperElement, setPopperElement] = useState(null); - + // store hooks + const { projectModules, getModuleById, fetchModules } = useModule(); + // popper-js const { styles, attributes } = usePopper(referenceElement, popperElement, { placement: "bottom-start", }); - const { module: moduleStore } = useMobxStore(); - - const fetchModules = () => { - if (workspaceSlug && projectId) moduleStore.fetchModules(workspaceSlug, projectId); + const handleFetchModules = () => { + if (workspaceSlug && projectId) fetchModules(workspaceSlug, projectId); }; - const modules = projectId ? moduleStore.modules[projectId] : undefined; + const selectedModule = value ? getModuleById(value) : null; - const selectedModule = modules ? modules?.find((i) => i.id === value) : undefined; + const options = projectModules?.map((moduleId) => { + const moduleDetails = getModuleById(moduleId); - const options = modules?.map((module) => ({ - value: module.id, - query: module.name, - content: ( -
- - - - {module.name} -
- ), - })); + return { + value: `${moduleDetails?.id}`, + query: `${moduleDetails?.name}`, + content: ( +
+ + + + {moduleDetails?.name} +
+ ), + }; + }); const filteredOptions = query === "" ? options : options?.filter((option) => option.query.toLowerCase().includes(query.toLowerCase())); @@ -73,7 +73,7 @@ export const IssueModuleSelect: React.FC = observer((pro ref={setReferenceElement} type="button" className="flex w-full cursor-pointer items-center justify-between gap-1 rounded border-[0.5px] border-custom-border-300 px-2 py-1 text-xs text-custom-text-200 hover:bg-custom-background-80" - onClick={fetchModules} + onClick={handleFetchModules} > {label} diff --git a/web/components/issues/select/project.tsx b/web/components/issues/select/project.tsx index 6aed0aac5..4964cf0f3 100644 --- a/web/components/issues/select/project.tsx +++ b/web/components/issues/select/project.tsx @@ -1,16 +1,13 @@ import React, { useState } from "react"; import { observer } from "mobx-react-lite"; import type { FieldError } from "react-hook-form"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; -// popper js import { usePopper } from "react-popper"; -// ui import { Combobox } from "@headlessui/react"; +import { Check, Clipboard, Search } from "lucide-react"; +// hooks +import { useProject } from "hooks/store"; // helpers import { renderEmoji } from "helpers/emoji.helper"; -// icons -import { Check, Clipboard, Search } from "lucide-react"; export interface IssueProjectSelectProps { value: string; @@ -28,25 +25,31 @@ export const IssueProjectSelect: React.FC = observer((p const { styles, attributes } = usePopper(referenceElement, popperElement, { placement: "bottom-start", }); + // store hooks + const { joinedProjects, getProjectById } = useProject(); - const { - project: { joinedProjects }, - } = useMobxStore(); + const selectedProject = getProjectById(value); - const selectedProject = joinedProjects?.find((i) => i.id === value); + const options = joinedProjects?.map((projectId) => { + const projectDetails = getProjectById(projectId); - const options = joinedProjects?.map((project) => ({ - value: project.id, - query: project.name, - content: ( -
- - {project.emoji ? renderEmoji(project.emoji) : project.icon_prop ? renderEmoji(project.icon_prop) : null} - - {project.name} -
- ), - })); + return { + value: `${projectDetails?.id}`, + query: `${projectDetails?.name}`, + content: ( +
+ + {projectDetails?.emoji + ? renderEmoji(projectDetails?.emoji) + : projectDetails?.icon_prop + ? renderEmoji(projectDetails?.icon_prop) + : null} + + {projectDetails?.name} +
+ ), + }; + }); const filteredOptions = query === "" ? options : options?.filter((option) => option.query.toLowerCase().includes(query.toLowerCase())); diff --git a/web/components/issues/select/state.tsx b/web/components/issues/select/state.tsx index acefa0c74..665ff784a 100644 --- a/web/components/issues/select/state.tsx +++ b/web/components/issues/select/state.tsx @@ -2,12 +2,11 @@ import React from "react"; import { useRouter } from "next/router"; import useSWR from "swr"; import { observer } from "mobx-react-lite"; -// store -import { useMobxStore } from "lib/mobx/store-provider"; +import { Plus } from "lucide-react"; +// hooks +import { useProjectState } from "hooks/store"; // ui import { CustomSearchSelect, DoubleCircleIcon, StateGroupIcon } from "@plane/ui"; -// icons -import { Plus } from "lucide-react"; type Props = { setIsOpen: React.Dispatch>; @@ -18,23 +17,18 @@ type Props = { export const IssueStateSelect: React.FC = observer((props) => { const { setIsOpen, value, onChange, projectId } = props; - - // states + // router const router = useRouter(); const { workspaceSlug } = router.query; - - const { - projectState: { states: projectStates, fetchProjectStates }, - } = useMobxStore(); + // store hooks + const { projectStates, fetchProjectStates } = useProjectState(); useSWR( workspaceSlug && projectId ? `STATES_LIST_${projectId.toUpperCase()}` : null, workspaceSlug && projectId ? () => fetchProjectStates(workspaceSlug.toString(), projectId) : null ); - const states = projectStates?.[projectId] || []; - - const options = states?.map((state) => ({ + const options = projectStates?.map((state) => ({ value: state.id, query: state.name, content: ( @@ -45,8 +39,8 @@ export const IssueStateSelect: React.FC = observer((props) => { ), })); - const selectedOption = states?.find((s) => s.id === value); - const currentDefaultState = states?.find((s) => s.default); + const selectedOption = projectStates?.find((s) => s.id === value); + const currentDefaultState = projectStates?.find((s) => s.default); return ( = ({ issueId, submitChanges, watch, disabled = false }) => { +export const SidebarBlockedSelect: React.FC = observer((props) => { + const { issueId, submitChanges, watch, disabled = false } = props; + // states const [isBlockedModalOpen, setIsBlockedModalOpen] = useState(false); - - const { user } = useUser(); - const { setToastAlert } = useToast(); - + // router const router = useRouter(); const { workspaceSlug, projectId } = router.query; + // toast alert + const { setToastAlert } = useToast(); + // store hooks + const { currentUser } = useUser(); const handleClose = () => { setIsBlockedModalOpen(false); @@ -65,7 +67,7 @@ export const SidebarBlockedSelect: React.FC = ({ issueId, submitChanges, }, })); - if (!user) return; + if (!currentUser) return; issueService .createIssueRelation(workspaceSlug as string, projectId as string, issueId as string, { @@ -128,7 +130,7 @@ export const SidebarBlockedSelect: React.FC = ({ issueId, submitChanges, related_issues: updatedRelations, }); - if (!user) return; + if (!currentUser) return; issueService.deleteIssueRelation( workspaceSlug as string, @@ -158,4 +160,4 @@ export const SidebarBlockedSelect: React.FC = ({ issueId, submitChanges,
); -}; +}); diff --git a/web/components/issues/sidebar-select/blocker.tsx b/web/components/issues/sidebar-select/blocker.tsx index b61119dab..1eede27ec 100644 --- a/web/components/issues/sidebar-select/blocker.tsx +++ b/web/components/issues/sidebar-select/blocker.tsx @@ -1,18 +1,16 @@ import React, { useState } from "react"; - import { useRouter } from "next/router"; - -// react-hook-form import { UseFormWatch } from "react-hook-form"; +import { observer } from "mobx-react-lite"; +import { X } from "lucide-react"; // hooks +import { useUser } from "hooks/store"; import useToast from "hooks/use-toast"; -import useUser from "hooks/use-user"; // components import { ExistingIssuesListModal } from "components/core"; // services import { IssueService } from "services/issue"; // icons -import { X } from "lucide-react"; import { BlockerIcon } from "@plane/ui"; // types import { BlockeIssueDetail, IIssue, ISearchIssueResponse } from "types"; @@ -27,14 +25,17 @@ type Props = { // services const issueService = new IssueService(); -export const SidebarBlockerSelect: React.FC = ({ issueId, submitChanges, watch, disabled = false }) => { +export const SidebarBlockerSelect: React.FC = observer((props) => { + const { issueId, submitChanges, watch, disabled = false } = props; + // states const [isBlockerModalOpen, setIsBlockerModalOpen] = useState(false); - - const { user } = useUser(); - const { setToastAlert } = useToast(); - + // router const router = useRouter(); const { workspaceSlug, projectId } = router.query; + // toast alert + const { setToastAlert } = useToast(); + // store hooks + const { currentUser } = useUser(); const handleClose = () => { setIsBlockerModalOpen(false); @@ -66,7 +67,7 @@ export const SidebarBlockerSelect: React.FC = ({ issueId, submitChanges, }, })); - if (!user) return; + if (!currentUser) return; issueService .createIssueRelation(workspaceSlug as string, projectId as string, issueId as string, { @@ -138,7 +139,7 @@ export const SidebarBlockerSelect: React.FC = ({ issueId, submitChanges, issue_relations: updatedBlockers, }); - if (!user) return; + if (!currentUser) return; issueService.deleteIssueRelation( workspaceSlug as string, @@ -168,4 +169,4 @@ export const SidebarBlockerSelect: React.FC = ({ issueId, submitChanges,
); -}; +}); diff --git a/web/components/issues/sidebar-select/duplicate.tsx b/web/components/issues/sidebar-select/duplicate.tsx index 722d76b04..2ac404224 100644 --- a/web/components/issues/sidebar-select/duplicate.tsx +++ b/web/components/issues/sidebar-select/duplicate.tsx @@ -1,13 +1,11 @@ import React, { useState } from "react"; - import { useRouter } from "next/router"; -// react-hook-form import { UseFormWatch } from "react-hook-form"; -// hooks -import useToast from "hooks/use-toast"; -import useUser from "hooks/use-user"; -// icons +import { observer } from "mobx-react-lite"; import { X, CopyPlus } from "lucide-react"; +// hooks +import { useUser } from "hooks/store"; +import useToast from "hooks/use-toast"; // components import { ExistingIssuesListModal } from "components/core"; // services @@ -25,16 +23,17 @@ type Props = { // services const issueService = new IssueService(); -export const SidebarDuplicateSelect: React.FC = (props) => { +export const SidebarDuplicateSelect: React.FC = observer((props) => { const { issueId, submitChanges, watch, disabled = false } = props; - + // states const [isDuplicateModalOpen, setIsDuplicateModalOpen] = useState(false); - - const { user } = useUser(); - const { setToastAlert } = useToast(); - + // router const router = useRouter(); const { workspaceSlug, projectId } = router.query; + // toast alert + const { setToastAlert } = useToast(); + // store hooks + const { currentUser } = useUser(); const handleClose = () => { setIsDuplicateModalOpen(false); @@ -64,7 +63,7 @@ export const SidebarDuplicateSelect: React.FC = (props) => { }, })); - if (!user) return; + if (!currentUser) return; issueService .createIssueRelation(workspaceSlug as string, projectId as string, issueId as string, { @@ -130,7 +129,7 @@ export const SidebarDuplicateSelect: React.FC = (props) => { type="button" className="opacity-0 duration-300 group-hover:opacity-100" onClick={() => { - if (!user) return; + if (!currentUser) return; issueService .deleteIssueRelation( @@ -164,4 +163,4 @@ export const SidebarDuplicateSelect: React.FC = (props) => {
); -}; +}); diff --git a/web/components/issues/sidebar-select/relates-to.tsx b/web/components/issues/sidebar-select/relates-to.tsx index 00ae798df..b1f26b3fa 100644 --- a/web/components/issues/sidebar-select/relates-to.tsx +++ b/web/components/issues/sidebar-select/relates-to.tsx @@ -1,13 +1,12 @@ import React, { useState } from "react"; - import { useRouter } from "next/router"; -// react-hook-form import { UseFormWatch } from "react-hook-form"; -// hooks -import useToast from "hooks/use-toast"; -import useUser from "hooks/use-user"; -// icons +import { observer } from "mobx-react-lite"; import { X } from "lucide-react"; +// hooks +import { useUser } from "hooks/store"; +import useToast from "hooks/use-toast"; +// icons import { RelatedIcon } from "@plane/ui"; // components import { ExistingIssuesListModal } from "components/core"; @@ -26,16 +25,17 @@ type Props = { // services const issueService = new IssueService(); -export const SidebarRelatesSelect: React.FC = (props) => { +export const SidebarRelatesSelect: React.FC = observer((props) => { const { issueId, submitChanges, watch, disabled = false } = props; - + // states const [isRelatesToModalOpen, setIsRelatesToModalOpen] = useState(false); - - const { user } = useUser(); - const { setToastAlert } = useToast(); - + // router const router = useRouter(); const { workspaceSlug, projectId } = router.query; + // toast alert + const { setToastAlert } = useToast(); + // store hooks + const { currentUser } = useUser(); const handleClose = () => { setIsRelatesToModalOpen(false); @@ -65,7 +65,7 @@ export const SidebarRelatesSelect: React.FC = (props) => { }, })); - if (!user) return; + if (!currentUser) return; issueService .createIssueRelation(workspaceSlug as string, projectId as string, issueId as string, { @@ -131,7 +131,7 @@ export const SidebarRelatesSelect: React.FC = (props) => { type="button" className="opacity-0 duration-300 group-hover:opacity-100" onClick={() => { - if (!user) return; + if (!currentUser) return; issueService .deleteIssueRelation( @@ -165,4 +165,4 @@ export const SidebarRelatesSelect: React.FC = (props) => {
); -}; +}); diff --git a/web/components/modules/delete-module-modal.tsx b/web/components/modules/delete-module-modal.tsx index f5fee3154..91b8b8fe6 100644 --- a/web/components/modules/delete-module-modal.tsx +++ b/web/components/modules/delete-module-modal.tsx @@ -1,7 +1,9 @@ import React, { useState } from "react"; import { useRouter } from "next/router"; import { Dialog, Transition } from "@headlessui/react"; +import { observer } from "mobx-react-lite"; // hooks +import { useApplication, useModule } from "hooks/store"; import useToast from "hooks/use-toast"; // ui import { Button } from "@plane/ui"; @@ -9,8 +11,6 @@ import { Button } from "@plane/ui"; import { AlertTriangle } from "lucide-react"; // types import type { IModule } from "types"; -import { observer } from "mobx-react-lite"; -import { useMobxStore } from "lib/mobx/store-provider"; type Props = { data: IModule; @@ -20,17 +20,17 @@ type Props = { export const DeleteModuleModal: React.FC = observer((props) => { const { data, isOpen, onClose } = props; - + // states const [isDeleteLoading, setIsDeleteLoading] = useState(false); - + // router const router = useRouter(); const { workspaceSlug, projectId, moduleId, peekModule } = router.query; - + // store hooks const { - module: moduleStore, - trackEvent: { postHogEventTracker }, - } = useMobxStore(); - + eventTracker: { postHogEventTracker }, + } = useApplication(); + const { deleteModule } = useModule(); + // toast alert const { setToastAlert } = useToast(); const handleClose = () => { @@ -43,8 +43,7 @@ export const DeleteModuleModal: React.FC = observer((props) => { setIsDeleteLoading(true); - await moduleStore - .deleteModule(workspaceSlug.toString(), projectId.toString(), data.id) + await deleteModule(workspaceSlug.toString(), projectId.toString(), data.id) .then(() => { if (moduleId || peekModule) router.push(`/${workspaceSlug}/projects/${data.project}/modules`); handleClose(); diff --git a/web/components/modules/modal.tsx b/web/components/modules/modal.tsx index da4103833..0f8614323 100644 --- a/web/components/modules/modal.tsx +++ b/web/components/modules/modal.tsx @@ -2,12 +2,11 @@ import React, { useEffect, useState } from "react"; import { observer } from "mobx-react-lite"; import { useForm } from "react-hook-form"; import { Dialog, Transition } from "@headlessui/react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication, useModule, useProject } from "hooks/store"; +import useToast from "hooks/use-toast"; // components import { ModuleForm } from "components/modules"; -// hooks -import useToast from "hooks/use-toast"; // types import type { IModule } from "types"; @@ -29,17 +28,15 @@ const defaultValues: Partial = { export const CreateUpdateModuleModal: React.FC = observer((props) => { const { isOpen, onClose, data, workspaceSlug, projectId } = props; - + // states const [activeProject, setActiveProject] = useState(null); - + // store hooks const { - project: projectStore, - module: moduleStore, - trackEvent: { postHogEventTracker }, - } = useMobxStore(); - - const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : undefined; - + eventTracker: { postHogEventTracker }, + } = useApplication(); + const { workspaceProjects } = useProject(); + const { createModule, updateModuleDetails } = useModule(); + // toast alert const { setToastAlert } = useToast(); const handleClose = () => { @@ -51,11 +48,11 @@ export const CreateUpdateModuleModal: React.FC = observer((props) => { defaultValues, }); - const createModule = async (payload: Partial) => { + const handleCreateModule = async (payload: Partial) => { if (!workspaceSlug || !projectId) return; + const selectedProjectId = payload.project ?? projectId.toString(); - await moduleStore - .createModule(workspaceSlug.toString(), selectedProjectId, payload) + await createModule(workspaceSlug.toString(), selectedProjectId, payload) .then((res) => { handleClose(); @@ -81,11 +78,11 @@ export const CreateUpdateModuleModal: React.FC = observer((props) => { }); }; - const updateModule = async (payload: Partial) => { + const handleUpdateModule = async (payload: Partial) => { if (!workspaceSlug || !projectId || !data) return; + const selectedProjectId = payload.project ?? projectId.toString(); - await moduleStore - .updateModuleDetails(workspaceSlug.toString(), selectedProjectId, data.id, payload) + await updateModuleDetails(workspaceSlug.toString(), selectedProjectId, data.id, payload) .then((res) => { handleClose(); @@ -117,8 +114,8 @@ export const CreateUpdateModuleModal: React.FC = observer((props) => { const payload: Partial = { ...formData, }; - if (!data) await createModule(payload); - else await updateModule(payload); + if (!data) await handleCreateModule(payload); + else await handleUpdateModule(payload); }; useEffect(() => { @@ -138,9 +135,9 @@ export const CreateUpdateModuleModal: React.FC = observer((props) => { // if data is not present, set active project to the project // in the url. This has the least priority. - if (projects && projects.length > 0 && !activeProject) - setActiveProject(projects?.find((p) => p.id === projectId)?.id ?? projects?.[0].id ?? null); - }, [activeProject, data, projectId, projects, isOpen]); + if (workspaceProjects && workspaceProjects.length > 0 && !activeProject) + setActiveProject(projectId ?? workspaceProjects?.[0] ?? null); + }, [activeProject, data, projectId, workspaceProjects, isOpen]); return ( diff --git a/web/components/modules/module-card-item.tsx b/web/components/modules/module-card-item.tsx index eb65084c1..7eac23401 100644 --- a/web/components/modules/module-card-item.tsx +++ b/web/components/modules/module-card-item.tsx @@ -2,78 +2,50 @@ import React, { useState } from "react"; import Link from "next/link"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +import { Info, LinkIcon, Pencil, Star, Trash2 } from "lucide-react"; // hooks +import { useModule, useUser } from "hooks/store"; import useToast from "hooks/use-toast"; // components import { CreateUpdateModuleModal, DeleteModuleModal } from "components/modules"; // ui import { Avatar, AvatarGroup, CustomMenu, LayersIcon, Tooltip } from "@plane/ui"; -// icons -import { Info, LinkIcon, Pencil, Star, Trash2 } from "lucide-react"; // helpers import { copyUrlToClipboard } from "helpers/string.helper"; import { renderShortDate, renderShortMonthDate } from "helpers/date-time.helper"; -// types -import { IModule } from "types"; // constants import { MODULE_STATUS } from "constants/module"; import { EUserWorkspaceRoles } from "constants/workspace"; type Props = { - module: IModule; + moduleId: string; }; export const ModuleCardItem: React.FC = observer((props) => { - const { module } = props; - + const { moduleId } = props; + // states const [editModal, setEditModal] = useState(false); const [deleteModal, setDeleteModal] = useState(false); - + // router const router = useRouter(); const { workspaceSlug, projectId } = router.query; - + // toast alert const { setToastAlert } = useToast(); - - const { module: moduleStore, user: userStore } = useMobxStore(); - - const { currentProjectRole } = userStore; - + // store hooks + const { + membership: { currentProjectRole }, + } = useUser(); + const { getModuleById, addModuleToFavorites, removeModuleFromFavorites } = useModule(); + // derived values + const moduleDetails = getModuleById(moduleId); const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER; - const moduleTotalIssues = - module.backlog_issues + - module.unstarted_issues + - module.started_issues + - module.completed_issues + - module.cancelled_issues; - - const completionPercentage = (module.completed_issues / moduleTotalIssues) * 100; - - const endDate = new Date(module.target_date ?? ""); - const startDate = new Date(module.start_date ?? ""); - - const isDateValid = module.target_date || module.start_date; - - const areYearsEqual = startDate.getFullYear() === endDate.getFullYear(); - - const moduleStatus = MODULE_STATUS.find((status) => status.value === module.status); - - const issueCount = module - ? !moduleTotalIssues || moduleTotalIssues === 0 - ? "0 Issue" - : moduleTotalIssues === module.completed_issues - ? `${moduleTotalIssues} Issue${moduleTotalIssues > 1 ? "s" : ""}` - : `${module.completed_issues}/${moduleTotalIssues} Issues` - : "0 Issue"; - const handleAddToFavorites = (e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); if (!workspaceSlug || !projectId) return; - moduleStore.addModuleToFavorites(workspaceSlug.toString(), projectId.toString(), module.id).catch(() => { + addModuleToFavorites(workspaceSlug.toString(), projectId.toString(), moduleId).catch(() => { setToastAlert({ type: "error", title: "Error!", @@ -87,7 +59,7 @@ export const ModuleCardItem: React.FC = observer((props) => { e.preventDefault(); if (!workspaceSlug || !projectId) return; - moduleStore.removeModuleFromFavorites(workspaceSlug.toString(), projectId.toString(), module.id).catch(() => { + removeModuleFromFavorites(workspaceSlug.toString(), projectId.toString(), moduleId).catch(() => { setToastAlert({ type: "error", title: "Error!", @@ -99,7 +71,7 @@ export const ModuleCardItem: React.FC = observer((props) => { const handleCopyText = (e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); - copyUrlToClipboard(`${workspaceSlug}/projects/${projectId}/modules/${module.id}`).then(() => { + copyUrlToClipboard(`${workspaceSlug}/projects/${projectId}/modules/${moduleId}`).then(() => { setToastAlert({ type: "success", title: "Link Copied!", @@ -127,28 +99,56 @@ export const ModuleCardItem: React.FC = observer((props) => { router.push({ pathname: router.pathname, - query: { ...query, peekModule: module.id }, + query: { ...query, peekModule: moduleId }, }); }; + if (!moduleDetails) return null; + + const moduleTotalIssues = + moduleDetails.backlog_issues + + moduleDetails.unstarted_issues + + moduleDetails.started_issues + + moduleDetails.completed_issues + + moduleDetails.cancelled_issues; + + const completionPercentage = (moduleDetails.completed_issues / moduleTotalIssues) * 100; + + const endDate = new Date(moduleDetails.target_date ?? ""); + const startDate = new Date(moduleDetails.start_date ?? ""); + + const isDateValid = moduleDetails.target_date || moduleDetails.start_date; + + const areYearsEqual = startDate.getFullYear() === endDate.getFullYear(); + + const moduleStatus = MODULE_STATUS.find((status) => status.value === moduleDetails.status); + + const issueCount = module + ? !moduleTotalIssues || moduleTotalIssues === 0 + ? "0 Issue" + : moduleTotalIssues === moduleDetails.completed_issues + ? `${moduleTotalIssues} Issue${moduleTotalIssues > 1 ? "s" : ""}` + : `${moduleDetails.completed_issues}/${moduleTotalIssues} Issues` + : "0 Issue"; + return ( <> {workspaceSlug && projectId && ( setEditModal(false)} - data={module} + data={moduleDetails} projectId={projectId.toString()} workspaceSlug={workspaceSlug.toString()} /> )} - setDeleteModal(false)} /> - + setDeleteModal(false)} /> +
- - {module.name} + + {moduleDetails.name}
{moduleStatus && ( @@ -175,11 +175,11 @@ export const ModuleCardItem: React.FC = observer((props) => { {issueCount ?? "0 Issue"}
- {module.members_detail.length > 0 && ( - + {moduleDetails.members_detail.length > 0 && ( +
- {module.members_detail.map((member) => ( + {moduleDetails.members_detail.map((member) => ( ))} @@ -223,7 +223,7 @@ export const ModuleCardItem: React.FC = observer((props) => {
{isEditingAllowed && - (module.is_favorite ? ( + (moduleDetails.is_favorite ? ( diff --git a/web/components/modules/module-list-item.tsx b/web/components/modules/module-list-item.tsx index 005d8beba..0c0bcdcab 100644 --- a/web/components/modules/module-list-item.tsx +++ b/web/components/modules/module-list-item.tsx @@ -2,67 +2,50 @@ import React, { useState } from "react"; import Link from "next/link"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +import { Check, Info, LinkIcon, Pencil, Star, Trash2, User2 } from "lucide-react"; // hooks +import { useModule, useUser } from "hooks/store"; import useToast from "hooks/use-toast"; // components import { CreateUpdateModuleModal, DeleteModuleModal } from "components/modules"; // ui import { Avatar, AvatarGroup, CircularProgressIndicator, CustomMenu, Tooltip } from "@plane/ui"; -// icons -import { Check, Info, LinkIcon, Pencil, Star, Trash2, User2 } from "lucide-react"; // helpers import { copyUrlToClipboard } from "helpers/string.helper"; import { renderShortDate, renderShortMonthDate } from "helpers/date-time.helper"; -// types -import { IModule } from "types"; // constants import { MODULE_STATUS } from "constants/module"; import { EUserWorkspaceRoles } from "constants/workspace"; type Props = { - module: IModule; + moduleId: string; }; export const ModuleListItem: React.FC = observer((props) => { - const { module } = props; - + const { moduleId } = props; + // states const [editModal, setEditModal] = useState(false); const [deleteModal, setDeleteModal] = useState(false); - + // router const router = useRouter(); const { workspaceSlug, projectId } = router.query; - + // toast alert const { setToastAlert } = useToast(); - - const { module: moduleStore, user: userStore } = useMobxStore(); - - const { currentProjectRole } = userStore; - + // store hooks + const { + membership: { currentProjectRole }, + } = useUser(); + const { getModuleById, addModuleToFavorites, removeModuleFromFavorites } = useModule(); + // derived values + const moduleDetails = getModuleById(moduleId); const isEditingAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER; - const completionPercentage = ((module.completed_issues + module.cancelled_issues) / module.total_issues) * 100; - - const endDate = new Date(module.target_date ?? ""); - const startDate = new Date(module.start_date ?? ""); - - const renderDate = module.start_date || module.target_date; - - const areYearsEqual = startDate.getFullYear() === endDate.getFullYear(); - - const moduleStatus = MODULE_STATUS.find((status) => status.value === module.status); - - const progress = isNaN(completionPercentage) ? 0 : Math.floor(completionPercentage); - - const completedModuleCheck = module.status === "completed"; - const handleAddToFavorites = (e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); if (!workspaceSlug || !projectId) return; - moduleStore.addModuleToFavorites(workspaceSlug.toString(), projectId.toString(), module.id).catch(() => { + addModuleToFavorites(workspaceSlug.toString(), projectId.toString(), moduleId).catch(() => { setToastAlert({ type: "error", title: "Error!", @@ -76,7 +59,7 @@ export const ModuleListItem: React.FC = observer((props) => { e.preventDefault(); if (!workspaceSlug || !projectId) return; - moduleStore.removeModuleFromFavorites(workspaceSlug.toString(), projectId.toString(), module.id).catch(() => { + removeModuleFromFavorites(workspaceSlug.toString(), projectId.toString(), moduleId).catch(() => { setToastAlert({ type: "error", title: "Error!", @@ -88,7 +71,7 @@ export const ModuleListItem: React.FC = observer((props) => { const handleCopyText = (e: React.MouseEvent) => { e.stopPropagation(); e.preventDefault(); - copyUrlToClipboard(`${workspaceSlug}/projects/${projectId}/modules/${module.id}`).then(() => { + copyUrlToClipboard(`${workspaceSlug}/projects/${projectId}/modules/${moduleId}`).then(() => { setToastAlert({ type: "success", title: "Link Copied!", @@ -116,23 +99,41 @@ export const ModuleListItem: React.FC = observer((props) => { router.push({ pathname: router.pathname, - query: { ...query, peekModule: module.id }, + query: { ...query, peekModule: moduleId }, }); }; + if (!moduleDetails) return null; + + const completionPercentage = + ((moduleDetails.completed_issues + moduleDetails.cancelled_issues) / moduleDetails.total_issues) * 100; + + const endDate = new Date(moduleDetails.target_date ?? ""); + const startDate = new Date(moduleDetails.start_date ?? ""); + + const renderDate = moduleDetails.start_date || moduleDetails.target_date; + + const areYearsEqual = startDate.getFullYear() === endDate.getFullYear(); + + const moduleStatus = MODULE_STATUS.find((status) => status.value === moduleDetails.status); + + const progress = isNaN(completionPercentage) ? 0 : Math.floor(completionPercentage); + + const completedModuleCheck = moduleDetails.status === "completed"; + return ( <> {workspaceSlug && projectId && ( setEditModal(false)} - data={module} + data={moduleDetails} projectId={projectId.toString()} workspaceSlug={workspaceSlug.toString()} /> )} - setDeleteModal(false)} /> - + setDeleteModal(false)} /> +
@@ -151,8 +152,8 @@ export const ModuleListItem: React.FC = observer((props) => { )} - - {module.name} + + {moduleDetails.name}
diff --git a/web/components/modules/module-peek-overview.tsx b/web/components/modules/module-peek-overview.tsx index a276b3501..f82d436bc 100644 --- a/web/components/modules/module-peek-overview.tsx +++ b/web/components/modules/module-peek-overview.tsx @@ -1,10 +1,8 @@ import React, { useEffect } from "react"; - import { useRouter } from "next/router"; - -// mobx import { observer } from "mobx-react-lite"; -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useModule } from "hooks/store"; // components import { ModuleDetailsSidebar } from "./sidebar"; @@ -14,13 +12,13 @@ type Props = { }; export const ModulePeekOverview: React.FC = observer(({ projectId, workspaceSlug }) => { + // router const router = useRouter(); const { peekModule } = router.query; - + // refs const ref = React.useRef(null); - - const { module: moduleStore } = useMobxStore(); - const { fetchModuleDetails } = moduleStore; + // store hooks + const { fetchModuleDetails } = useModule(); const handleClose = () => { delete router.query.peekModule; diff --git a/web/components/modules/modules-list-view.tsx b/web/components/modules/modules-list-view.tsx index 3e4f42ccd..1fdeb3ce1 100644 --- a/web/components/modules/modules-list-view.tsx +++ b/web/components/modules/modules-list-view.tsx @@ -1,9 +1,8 @@ import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { Plus } from "lucide-react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; // hooks +import { useApplication, useModule } from "hooks/store"; import useLocalStorage from "hooks/use-local-storage"; // components import { ModuleCardItem, ModuleListItem, ModulePeekOverview, ModulesListGanttChartView } from "components/modules"; @@ -14,16 +13,16 @@ import emptyModule from "public/empty-state/empty_modules.webp"; import { NewEmptyState } from "components/common/new-empty-state"; export const ModulesListView: React.FC = observer(() => { + // router const router = useRouter(); const { workspaceSlug, projectId, peekModule } = router.query; - - const { module: moduleStore, commandPalette: commandPaletteStore } = useMobxStore(); + // store hooks + const { commandPalette: commandPaletteStore } = useApplication(); + const { projectModules } = useModule(); const { storedValue: modulesView } = useLocalStorage("modules_view", "grid"); - const modulesList = moduleStore.projectModules; - - if (!modulesList) + if (!projectModules) return ( @@ -37,14 +36,14 @@ export const ModulesListView: React.FC = observer(() => { return ( <> - {modulesList.length > 0 ? ( + {projectModules.length > 0 ? ( <> {modulesView === "list" && (
- {modulesList.map((module) => ( - + {projectModules.map((moduleId) => ( + ))}
{ : "lg:grid-cols-2 xl:grid-cols-3 3xl:grid-cols-4" } auto-rows-max transition-all `} > - {modulesList.map((module) => ( - + {projectModules.map((moduleId) => ( + ))}
= observer((props) => { const { moduleId, handleClose } = props; - + // states const [moduleDeleteModal, setModuleDeleteModal] = useState(false); const [moduleLinkModal, setModuleLinkModal] = useState(false); const [selectedLinkToUpdate, setSelectedLinkToUpdate] = useState(null); - + // router const router = useRouter(); const { workspaceSlug, projectId, peekModule } = router.query; - + // store hooks const { - module: { - moduleDetails: _moduleDetails, - updateModuleDetails, - createModuleLink, - updateModuleLink, - deleteModuleLink, - }, - user: userStore, - } = useMobxStore(); + membership: { currentProjectRole }, + } = useUser(); + const { getModuleById, updateModuleDetails, createModuleLink, updateModuleLink, deleteModuleLink } = useModule(); - const userRole = userStore.currentProjectRole; - const moduleDetails = _moduleDetails[moduleId] ?? undefined; + const moduleDetails = getModuleById(moduleId); const { setToastAlert } = useToast(); @@ -551,7 +544,7 @@ export const ModuleDetailsSidebar: React.FC = observer((props) => {
- {userRole && moduleDetails.link_module && moduleDetails.link_module.length > 0 ? ( + {currentProjectRole && moduleDetails.link_module && moduleDetails.link_module.length > 0 ? ( <>
= ({ stepChange, setTryDiffAccount
); -}; +}); diff --git a/web/components/onboarding/onboarding-sidebar.tsx b/web/components/onboarding/onboarding-sidebar.tsx index 3e4edc75b..fc7bfe7e5 100644 --- a/web/components/onboarding/onboarding-sidebar.tsx +++ b/web/components/onboarding/onboarding-sidebar.tsx @@ -17,9 +17,8 @@ import { Bell, } from "lucide-react"; import { Avatar, DiceIcon, PhotoFilterIcon } from "@plane/ui"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; - +// hooks +import { useUser, useWorkspace } from "hooks/store"; // types import { IWorkspace } from "types"; // assets @@ -92,11 +91,10 @@ var lastWorkspaceName: string = ""; export const OnboardingSidebar: React.FC = (props) => { const { workspaceName, showProject, control, setValue, watch, userFullName } = props; - const { - workspace: workspaceStore, - user: { currentUser }, - } = useMobxStore(); - const workspace = workspaceStore.workspaces ? workspaceStore.workspaces[0] : null; + // store hooks + const { currentUser } = useUser(); + const { workspaces } = useWorkspace(); + const workspaceDetails = Object.values(workspaces ?? {})?.[0]; const { resolvedTheme } = useTheme(); @@ -170,7 +168,7 @@ export const OnboardingSidebar: React.FC = (props) => {
0 ? value : workspace ? workspace.name : "New Workspace"} + name={value.length > 0 ? value : workspaceDetails ? workspaceDetails.name : "New Workspace"} src={""} size={24} shape="square" @@ -200,7 +198,7 @@ export const OnboardingSidebar: React.FC = (props) => {
= (props) => { const { isOpen, onClose } = props; - // states const [switchingAccount, setSwitchingAccount] = useState(false); const [isDeactivating, setIsDeactivating] = useState(false); - - const { - user: { deactivateAccount, signOut }, - } = useMobxStore(); - + // router const router = useRouter(); + // store hooks + const { deactivateAccount, signOut } = useUser(); const { resolvedTheme, setTheme } = useTheme(); diff --git a/web/components/onboarding/tour/root.tsx b/web/components/onboarding/tour/root.tsx index a02d6849a..362c03ed5 100644 --- a/web/components/onboarding/tour/root.tsx +++ b/web/components/onboarding/tour/root.tsx @@ -1,14 +1,13 @@ import { useState } from "react"; import Image from "next/image"; import { observer } from "mobx-react-lite"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +import { X } from "lucide-react"; +// hooks +import { useApplication, useUser } from "hooks/store"; // components import { TourSidebar } from "components/onboarding"; // ui import { Button } from "@plane/ui"; -// icons -import { X } from "lucide-react"; // assets import PlaneWhiteLogo from "public/plane-logos/white-horizontal.svg"; import IssuesTour from "public/onboarding/issues.webp"; @@ -78,13 +77,12 @@ export const TourRoot: React.FC = observer((props) => { const { onComplete } = props; // states const [step, setStep] = useState("welcome"); - + // store hooks const { - user: userStore, commandPalette: commandPaletteStore, - trackEvent: { setTrackElement }, - } = useMobxStore(); - const user = userStore.currentUser; + eventTracker: { setTrackElement }, + } = useApplication(); + const { currentUser } = useUser(); const currentStepIndex = TOUR_STEPS.findIndex((tourStep) => tourStep.key === step); const currentStep = TOUR_STEPS[currentStepIndex]; @@ -99,7 +97,7 @@ export const TourRoot: React.FC = observer((props) => {

- Welcome to Plane, {user?.first_name} {user?.last_name} + Welcome to Plane, {currentUser?.first_name} {currentUser?.last_name}

We{"'"}re glad that you decided to try out Plane. You can now manage your projects with ease. Get diff --git a/web/components/onboarding/user-details.tsx b/web/components/onboarding/user-details.tsx index e6c5707e6..07ba55dc4 100644 --- a/web/components/onboarding/user-details.tsx +++ b/web/components/onboarding/user-details.tsx @@ -3,8 +3,8 @@ import Image from "next/image"; import { Controller, useForm } from "react-hook-form"; import { observer } from "mobx-react-lite"; import { Camera, User2 } from "lucide-react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useUser, useWorkspace } from "hooks/store"; // components import { Button, Input } from "@plane/ui"; import { OnboardingSidebar, OnboardingStepIndicator } from "components/onboarding"; @@ -42,13 +42,15 @@ const fileService = new FileService(); export const UserDetails: React.FC = observer((props) => { const { user, setUserName } = props; + // states const [isRemoving, setIsRemoving] = useState(false); const [isImageUploadModalOpen, setIsImageUploadModalOpen] = useState(false); - const { - user: userStore, - workspace: { workspaces }, - } = useMobxStore(); - const workspaceName = workspaces ? workspaces[0]?.name : "New Workspace"; + // store hooks + const { updateCurrentUser } = useUser(); + const { workspaces } = useWorkspace(); + // derived values + const workspaceName = workspaces ? Object.values(workspaces)?.[0]?.name : "New Workspace"; + // form info const { getValues, handleSubmit, @@ -74,7 +76,7 @@ export const UserDetails: React.FC = observer((props) => { }, }; - await userStore.updateCurrentUser(payload); + await updateCurrentUser(payload); }; const handleDelete = (url: string | null | undefined) => { if (!url) return; diff --git a/web/components/onboarding/workspace.tsx b/web/components/onboarding/workspace.tsx index 5ea7b882f..baa5877e7 100644 --- a/web/components/onboarding/workspace.tsx +++ b/web/components/onboarding/workspace.tsx @@ -5,11 +5,10 @@ import { Button, Input } from "@plane/ui"; // types import { IUser, IWorkspace, TOnboardingSteps } from "types"; // hooks +import { useUser, useWorkspace } from "hooks/store"; import useToast from "hooks/use-toast"; // services import { WorkspaceService } from "services/workspace.service"; -// mobx -import { useMobxStore } from "lib/mobx/store-provider"; // constants import { RESTRICTED_URLS } from "constants/workspace"; @@ -28,14 +27,13 @@ const workspaceService = new WorkspaceService(); export const Workspace: React.FC = (props) => { const { stepChange, user, control, handleSubmit, setValue, errors, isSubmitting } = props; + // states const [slugError, setSlugError] = useState(false); const [invalidSlug, setInvalidSlug] = useState(false); - - const { - workspace: workspaceStore, - user: { updateCurrentUser }, - } = useMobxStore(); - + // store hooks + const { updateCurrentUser } = useUser(); + const { createWorkspace, fetchWorkspaces, workspaces } = useWorkspace(); + // toast alert const { setToastAlert } = useToast(); const handleCreateWorkspace = async (formData: IWorkspace) => { @@ -47,15 +45,14 @@ export const Workspace: React.FC = (props) => { if (res.status === true && !RESTRICTED_URLS.includes(formData.slug)) { setSlugError(false); - await workspaceStore - .createWorkspace(formData) + await createWorkspace(formData) .then(async () => { setToastAlert({ type: "success", title: "Success!", message: "Workspace created successfully.", }); - await workspaceStore.fetchWorkspaces(); + await fetchWorkspaces(); await completeStep(); }) .catch(() => @@ -77,7 +74,9 @@ export const Workspace: React.FC = (props) => { }; const completeStep = async () => { - if (!user || !workspaceStore.workspaces) return; + if (!user || !workspaces) return; + + const firstWorkspace = Object.values(workspaces ?? {})?.[0]; const payload: Partial = { workspace_create: true, @@ -86,7 +85,7 @@ export const Workspace: React.FC = (props) => { await stepChange(payload); await updateCurrentUser({ - last_workspace_id: workspaceStore.workspaces[0]?.id, + last_workspace_id: firstWorkspace?.id, }); }; diff --git a/web/components/page-views/signin.tsx b/web/components/page-views/signin.tsx index 815a626a5..e4810ec2d 100644 --- a/web/components/page-views/signin.tsx +++ b/web/components/page-views/signin.tsx @@ -1,9 +1,8 @@ import { useEffect } from "react"; import { observer } from "mobx-react-lite"; import Image from "next/image"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; // hooks +import { useApplication, useUser } from "hooks/store"; import useSignInRedirection from "hooks/use-sign-in-redirection"; // components import { SignInRoot } from "components/account"; @@ -15,11 +14,11 @@ import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png"; export type AuthType = "sign-in" | "sign-up"; export const SignInView = observer(() => { - // store + // store hooks const { - user: { currentUser }, - appConfig: { envConfig }, - } = useMobxStore(); + config: { envConfig }, + } = useApplication(); + const { currentUser } = useUser(); // sign in redirection hook const { isRedirecting, handleRedirection } = useSignInRedirection(); diff --git a/web/components/page-views/workspace-dashboard.tsx b/web/components/page-views/workspace-dashboard.tsx index c276e6278..a290f48c7 100644 --- a/web/components/page-views/workspace-dashboard.tsx +++ b/web/components/page-views/workspace-dashboard.tsx @@ -3,7 +3,7 @@ import { useRouter } from "next/router"; import useSWR from "swr"; import { observer } from "mobx-react-lite"; // hooks -import { useMobxStore } from "lib/mobx/store-provider"; +import { useApplication, useProject, useUser } from "hooks/store"; // components import { TourRoot } from "components/onboarding"; import { UserGreetingsView } from "components/user"; @@ -13,36 +13,30 @@ import { NewEmptyState } from "components/common/new-empty-state"; import emptyProject from "public/empty-state/dashboard_empty_project.webp"; export const WorkspaceDashboardView = observer(() => { + // states + const [month, setMonth] = useState(new Date().getMonth() + 1); // router const router = useRouter(); const { workspaceSlug } = router.query; - // store - + // store hooks const { - user: userStore, - project: projectStore, commandPalette: commandPaletteStore, - trackEvent: { setTrackElement, postHogEventTracker }, - } = useMobxStore(); - - const user = userStore.currentUser; - const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : null; - const workspaceDashboardInfo = userStore.dashboardInfo; - // states - const [month, setMonth] = useState(new Date().getMonth() + 1); + eventTracker: { setTrackElement, postHogEventTracker }, + } = useApplication(); + const { currentUser, dashboardInfo: workspaceDashboardInfo, fetchUserDashboardInfo, updateTourCompleted } = useUser(); + const { workspaceProjects } = useProject(); // fetch user dashboard info useSWR( workspaceSlug ? `USER_WORKSPACE_DASHBOARD_${workspaceSlug}_${month}` : null, - workspaceSlug ? () => userStore.fetchUserDashboardInfo(workspaceSlug.toString(), month) : null + workspaceSlug ? () => fetchUserDashboardInfo(workspaceSlug.toString(), month) : null ); const handleTourCompleted = () => { - userStore - .updateTourCompleted() + updateTourCompleted() .then(() => { postHogEventTracker("USER_TOUR_COMPLETE", { - user_id: user?.id, - email: user?.email, + user_id: currentUser?.id, + email: currentUser?.email, state: "SUCCESS", }); }) @@ -56,16 +50,16 @@ export const WorkspaceDashboardView = observer(() => { {/* {isProductUpdatesModalOpen && ( )} */} - {user && !user.is_tour_completed && ( + {currentUser && !currentUser.is_tour_completed && (

)}
- {user && } + {currentUser && } - {projects ? ( - projects.length > 0 ? ( + {workspaceProjects ? ( + workspaceProjects.length > 0 ? (
diff --git a/web/components/pages/create-update-block-inline.tsx b/web/components/pages/create-update-block-inline.tsx index 31b58d0df..ccd81adfe 100644 --- a/web/components/pages/create-update-block-inline.tsx +++ b/web/components/pages/create-update-block-inline.tsx @@ -9,7 +9,9 @@ import { IssueService } from "services/issue/issue.service"; import { AIService } from "services/ai.service"; import { FileService } from "services/file.service"; // hooks +import { useApplication } from "hooks/store"; import useToast from "hooks/use-toast"; +import useEditorSuggestions from "hooks/use-editor-suggestions"; // components import { GptAssistantModal } from "components/core"; import { Button, TextArea } from "@plane/ui"; @@ -18,8 +20,6 @@ import { RichTextEditorWithRef } from "@plane/rich-text-editor"; import { IUser, IPageBlock } from "types"; // fetch-keys import { PAGE_BLOCKS_LIST } from "constants/fetch-keys"; -import useEditorSuggestions from "hooks/use-editor-suggestions"; -import { useMobxStore } from "lib/mobx/store-provider"; type Props = { handleClose: () => void; @@ -48,8 +48,8 @@ export const CreateUpdateBlockInline: FC = (props) => { const [gptAssistantModal, setGptAssistantModal] = useState(false); // store const { - appConfig: { envConfig }, - } = useMobxStore(); + config: { envConfig }, + } = useApplication(); // refs const editorRef = useRef(null); // router diff --git a/web/components/pages/create-update-page-modal.tsx b/web/components/pages/create-update-page-modal.tsx index e754bd771..d90781351 100644 --- a/web/components/pages/create-update-page-modal.tsx +++ b/web/components/pages/create-update-page-modal.tsx @@ -2,13 +2,12 @@ import React, { FC } from "react"; import { useRouter } from "next/router"; import { Dialog, Transition } from "@headlessui/react"; // hooks +import { useApplication, usePage, useWorkspace } from "hooks/store"; import useToast from "hooks/use-toast"; // components import { PageForm } from "./page-form"; // types import { IPage } from "types"; -// store -import { useMobxStore } from "lib/mobx/store-provider"; type Props = { data?: IPage | null; @@ -22,13 +21,13 @@ export const CreateUpdatePageModal: FC = (props) => { // router const router = useRouter(); const { workspaceSlug } = router.query; - // store + // store hooks const { - page: { createPage, updatePage }, - trackEvent: { postHogEventTracker }, - workspace: { currentWorkspace }, - } = useMobxStore(); - + eventTracker: { postHogEventTracker }, + } = useApplication(); + const { currentWorkspace } = useWorkspace(); + const { createPage, updatePage } = usePage(); + // toast alert const { setToastAlert } = useToast(); const onClose = () => { @@ -56,7 +55,7 @@ export const CreateUpdatePageModal: FC = (props) => { { isGrouping: true, groupType: "Workspace_metrics", - gorupId: currentWorkspace?.id!, + groupId: currentWorkspace?.id!, } ); }) @@ -74,7 +73,7 @@ export const CreateUpdatePageModal: FC = (props) => { { isGrouping: true, groupType: "Workspace_metrics", - gorupId: currentWorkspace?.id!, + groupId: currentWorkspace?.id!, } ); }); @@ -100,7 +99,7 @@ export const CreateUpdatePageModal: FC = (props) => { { isGrouping: true, groupType: "Workspace_metrics", - gorupId: currentWorkspace?.id!, + groupId: currentWorkspace?.id!, } ); }) @@ -118,7 +117,7 @@ export const CreateUpdatePageModal: FC = (props) => { { isGrouping: true, groupType: "Workspace_metrics", - gorupId: currentWorkspace?.id!, + groupId: currentWorkspace?.id!, } ); }); diff --git a/web/components/pages/delete-page-modal.tsx b/web/components/pages/delete-page-modal.tsx index 3ece7db0d..c11e820dc 100644 --- a/web/components/pages/delete-page-modal.tsx +++ b/web/components/pages/delete-page-modal.tsx @@ -2,14 +2,12 @@ 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"; +import { AlertTriangle } from "lucide-react"; // hooks +import { usePage } from "hooks/store"; import useToast from "hooks/use-toast"; // ui import { Button } from "@plane/ui"; -// icons -import { AlertTriangle } from "lucide-react"; // types import type { IPage } from "types"; @@ -21,16 +19,14 @@ type TConfirmPageDeletionProps = { export const DeletePageModal: React.FC = observer((props) => { const { data, isOpen, onClose } = props; - + // states const [isDeleting, setIsDeleting] = useState(false); - + // router const router = useRouter(); const { workspaceSlug, projectId } = router.query; - - const { - page: { deletePage }, - } = useMobxStore(); - + // store hooks + const { deletePage } = usePage(); + // toast alert const { setToastAlert } = useToast(); const handleClose = () => { diff --git a/web/components/pages/pages-list/all-pages-list.tsx b/web/components/pages/pages-list/all-pages-list.tsx index 33e9b3198..0d757ea91 100644 --- a/web/components/pages/pages-list/all-pages-list.tsx +++ b/web/components/pages/pages-list/all-pages-list.tsx @@ -1,17 +1,15 @@ import { FC } from "react"; import { observer } from "mobx-react-lite"; +// hooks +import { usePage } from "hooks/store"; // components import { PagesListView } from "components/pages/pages-list"; -// fetch-keys -import { useMobxStore } from "lib/mobx/store-provider"; // ui import { Loader } from "@plane/ui"; export const AllPagesList: FC = observer(() => { // store - const { - page: { projectPages }, - } = useMobxStore(); + const { projectPages } = usePage(); if (!projectPages) return ( diff --git a/web/components/pages/pages-list/archived-pages-list.tsx b/web/components/pages/pages-list/archived-pages-list.tsx index 26baa0f5c..32d9bbddf 100644 --- a/web/components/pages/pages-list/archived-pages-list.tsx +++ b/web/components/pages/pages-list/archived-pages-list.tsx @@ -3,14 +3,12 @@ import { observer } from "mobx-react-lite"; // components import { PagesListView } from "components/pages/pages-list"; // hooks -import { useMobxStore } from "lib/mobx/store-provider"; +import { usePage } from "hooks/store"; // ui import { Loader } from "@plane/ui"; export const ArchivedPagesList: FC = observer(() => { - const { - page: { archivedProjectPages }, - } = useMobxStore(); + const { archivedProjectPages } = usePage(); if (!archivedProjectPages) return ( diff --git a/web/components/pages/pages-list/favorite-pages-list.tsx b/web/components/pages/pages-list/favorite-pages-list.tsx index b20ce73fc..013767b06 100644 --- a/web/components/pages/pages-list/favorite-pages-list.tsx +++ b/web/components/pages/pages-list/favorite-pages-list.tsx @@ -3,14 +3,12 @@ import { observer } from "mobx-react-lite"; // components import { PagesListView } from "components/pages/pages-list"; // hooks -import { useMobxStore } from "lib/mobx/store-provider"; +import { usePage } from "hooks/store"; // ui import { Loader } from "@plane/ui"; export const FavoritePagesList: FC = observer(() => { - const { - page: { favoriteProjectPages }, - } = useMobxStore(); + const { favoriteProjectPages } = usePage(); if (!favoriteProjectPages) return ( diff --git a/web/components/pages/pages-list/list-item.tsx b/web/components/pages/pages-list/list-item.tsx index 6f02f7656..b85b11258 100644 --- a/web/components/pages/pages-list/list-item.tsx +++ b/web/components/pages/pages-list/list-item.tsx @@ -16,6 +16,7 @@ import { // mobx store import { useMobxStore } from "lib/mobx/store-provider"; // hooks +import { usePage, useUser } from "hooks/store"; import useToast from "hooks/use-toast"; // helpers import { copyUrlToClipboard } from "helpers/string.helper"; @@ -24,35 +25,48 @@ import { render24HourFormatTime, renderFormattedDate } from "helpers/date-time.h import { CustomMenu, Tooltip } from "@plane/ui"; // components import { CreateUpdatePageModal, DeletePageModal } from "components/pages"; -// types -import { IPage } from "types"; +// constants import { EUserWorkspaceRoles } from "constants/workspace"; export interface IPagesListItem { workspaceSlug: string; projectId: string; - page: IPage; + pageId: string; } export const PagesListItem: FC = observer((props) => { - const { workspaceSlug, projectId, page } = props; + const { workspaceSlug, projectId, pageId } = props; // states const [createUpdatePageModal, setCreateUpdatePageModal] = useState(false); const [deletePageModal, setDeletePageModal] = useState(false); // mobx store const { - page: { archivePage, removeFromFavorites, addToFavorites, makePublic, makePrivate, restorePage }, - user: { currentUser, currentProjectRole }, projectMember: { projectMembers }, } = useMobxStore(); - // hooks + const { + currentUser, + membership: { currentProjectRole }, + } = useUser(); + const { + getArchivedPageById, + getUnArchivedPageById, + archivePage, + removeFromFavorites, + addToFavorites, + makePrivate, + makePublic, + restorePage, + } = usePage(); + // toast alert const { setToastAlert } = useToast(); + // derived values + const pageDetails = getUnArchivedPageById(pageId) ?? getArchivedPageById(pageId); const handleCopyUrl = (e: any) => { e.preventDefault(); e.stopPropagation(); - copyUrlToClipboard(`${workspaceSlug}/projects/${projectId}/pages/${page.id}`).then(() => { + copyUrlToClipboard(`${workspaceSlug}/projects/${projectId}/pages/${pageId}`).then(() => { setToastAlert({ type: "success", title: "Link Copied!", @@ -65,7 +79,7 @@ export const PagesListItem: FC = observer((props) => { e.preventDefault(); e.stopPropagation(); - addToFavorites(workspaceSlug, projectId, page.id) + addToFavorites(workspaceSlug, projectId, pageId) .then(() => { setToastAlert({ type: "success", @@ -86,7 +100,7 @@ export const PagesListItem: FC = observer((props) => { e.preventDefault(); e.stopPropagation(); - removeFromFavorites(workspaceSlug, projectId, page.id) + removeFromFavorites(workspaceSlug, projectId, pageId) .then(() => { setToastAlert({ type: "success", @@ -107,28 +121,28 @@ export const PagesListItem: FC = observer((props) => { e.preventDefault(); e.stopPropagation(); - makePublic(workspaceSlug, projectId, page.id); + makePublic(workspaceSlug, projectId, pageId); }; const handleMakePrivate = (e: any) => { e.preventDefault(); e.stopPropagation(); - makePrivate(workspaceSlug, projectId, page.id); + makePrivate(workspaceSlug, projectId, pageId); }; const handleArchivePage = (e: any) => { e.preventDefault(); e.stopPropagation(); - archivePage(workspaceSlug, projectId, page.id); + archivePage(workspaceSlug, projectId, pageId); }; const handleRestorePage = (e: any) => { e.preventDefault(); e.stopPropagation(); - restorePage(workspaceSlug, projectId, page.id); + restorePage(workspaceSlug, projectId, pageId); }; const handleDeletePage = (e: any) => { @@ -145,8 +159,12 @@ export const PagesListItem: FC = observer((props) => { setCreateUpdatePageModal(true); }; - const ownerDetails = projectMembers?.find((projectMember) => projectMember.member.id === page.owned_by)?.member; - const isCurrentUserOwner = page.owned_by === currentUser?.id; + if (!pageDetails) return null; + + const ownerDetails = projectMembers?.find( + (projectMember) => projectMember.member.id === pageDetails.owned_by + )?.member; + const isCurrentUserOwner = pageDetails.owned_by === currentUser?.id; const userCanEdit = isCurrentUserOwner || @@ -161,19 +179,19 @@ export const PagesListItem: FC = observer((props) => { setCreateUpdatePageModal(false)} - data={page} + data={pageDetails} projectId={projectId} /> - setDeletePageModal(false)} data={page} /> + setDeletePageModal(false)} data={pageDetails} />
  • - +
    -

    {page.name}

    - {page.label_details.length > 0 && - page.label_details.map((label) => ( +

    {pageDetails.name}

    + {pageDetails.label_details.length > 0 && + pageDetails.label_details.map((label) => (
    = observer((props) => { ))}
    - {page.archived_at ? ( + {pageDetails.archived_at ? ( -

    {render24HourFormatTime(page.archived_at)}

    +

    {render24HourFormatTime(pageDetails.archived_at)}

    ) : ( -

    {render24HourFormatTime(page.updated_at)}

    +

    {render24HourFormatTime(pageDetails.updated_at)}

    )} {isEditingAllowed && ( - - {page.is_favorite ? ( + + {pageDetails.is_favorite ? ( @@ -225,12 +243,12 @@ export const PagesListItem: FC = observer((props) => { {userCanChangeAccess && ( - {page.access ? ( + {pageDetails.access ? ( @@ -243,12 +261,14 @@ export const PagesListItem: FC = observer((props) => { )} - {page.archived_at ? ( + {pageDetails.archived_at ? ( <> {userCanArchive && ( diff --git a/web/components/pages/pages-list/list-view.tsx b/web/components/pages/pages-list/list-view.tsx index 769bed1bd..b4bc5bdd2 100644 --- a/web/components/pages/pages-list/list-view.tsx +++ b/web/components/pages/pages-list/list-view.tsx @@ -2,8 +2,8 @@ import { FC } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { Plus } from "lucide-react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication, useUser } from "hooks/store"; // components import { PagesListItem } from "./list-item"; import { NewEmptyState } from "components/common/new-empty-state"; @@ -11,21 +11,22 @@ import { NewEmptyState } from "components/common/new-empty-state"; import { Loader } from "@plane/ui"; // images import emptyPage from "public/empty-state/empty_page.png"; -// types -import { IPage } from "types"; // constants import { EUserWorkspaceRoles } from "constants/workspace"; type IPagesListView = { - pages: IPage[]; + pages: string[]; }; -export const PagesListView: FC = observer(({ pages }) => { - // store +export const PagesListView: FC = observer((props) => { + const { pages } = props; + // store hooks const { - user: { currentProjectRole }, commandPalette: { toggleCreatePageModal }, - } = useMobxStore(); + } = useApplication(); + const { + membership: { currentProjectRole }, + } = useUser(); // router const router = useRouter(); const { workspaceSlug, projectId } = router.query; @@ -49,12 +50,12 @@ export const PagesListView: FC = observer(({ pages }) => {
    {pages.length > 0 ? (
      - {pages.map((page) => ( + {pages.map((pageId) => ( ))}
    diff --git a/web/components/pages/pages-list/private-page-list.tsx b/web/components/pages/pages-list/private-page-list.tsx index 28a45d149..93403fc35 100644 --- a/web/components/pages/pages-list/private-page-list.tsx +++ b/web/components/pages/pages-list/private-page-list.tsx @@ -1,16 +1,14 @@ import { FC } from "react"; import { observer } from "mobx-react-lite"; +// hooks +import { usePage } from "hooks/store"; // components import { PagesListView } from "components/pages/pages-list"; -// hooks -import { useMobxStore } from "lib/mobx/store-provider"; // ui import { Loader } from "@plane/ui"; export const PrivatePagesList: FC = observer(() => { - const { - page: { privateProjectPages }, - } = useMobxStore(); + const { privateProjectPages } = usePage(); if (!privateProjectPages) return ( diff --git a/web/components/pages/pages-list/recent-pages-list.tsx b/web/components/pages/pages-list/recent-pages-list.tsx index 68d3056ff..8f5f8f2fc 100644 --- a/web/components/pages/pages-list/recent-pages-list.tsx +++ b/web/components/pages/pages-list/recent-pages-list.tsx @@ -1,8 +1,8 @@ import React, { FC } from "react"; import { observer } from "mobx-react-lite"; import { Plus } from "lucide-react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication, usePage } from "hooks/store"; // components import { PagesListView } from "components/pages/pages-list"; import { NewEmptyState } from "components/common/new-empty-state"; @@ -14,11 +14,9 @@ import emptyPage from "public/empty-state/empty_page.png"; import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper"; export const RecentPagesList: FC = observer(() => { - // store - const { - commandPalette: commandPaletteStore, - page: { recentProjectPages }, - } = useMobxStore(); + // store hooks + const { commandPalette: commandPaletteStore } = useApplication(); + const { recentProjectPages } = usePage(); const isEmpty = recentProjectPages && Object.values(recentProjectPages).every((value) => value.length === 0); diff --git a/web/components/pages/pages-list/shared-pages-list.tsx b/web/components/pages/pages-list/shared-pages-list.tsx index 8c400695c..b36625806 100644 --- a/web/components/pages/pages-list/shared-pages-list.tsx +++ b/web/components/pages/pages-list/shared-pages-list.tsx @@ -3,16 +3,14 @@ import { observer } from "mobx-react-lite"; // components import { PagesListView } from "components/pages/pages-list"; // hooks -import { useMobxStore } from "lib/mobx/store-provider"; +import { usePage } from "hooks/store"; // ui import { Loader } from "@plane/ui"; export const SharedPagesList: FC = observer(() => { - const { - page: { sharedProjectPages }, - } = useMobxStore(); + const { publicProjectPages } = usePage(); - if (!sharedProjectPages) + if (!publicProjectPages) return ( @@ -21,5 +19,5 @@ export const SharedPagesList: FC = observer(() => { ); - return ; + return ; }); diff --git a/web/components/profile/sidebar.tsx b/web/components/profile/sidebar.tsx index 6c01db9f7..4307614da 100644 --- a/web/components/profile/sidebar.tsx +++ b/web/components/profile/sidebar.tsx @@ -1,14 +1,12 @@ import { useRouter } from "next/router"; import Link from "next/link"; - import useSWR from "swr"; - -// headless ui import { Disclosure, Transition } from "@headlessui/react"; +import { observer } from "mobx-react-lite"; +// hooks +import { useUser } from "hooks/store"; // services import { UserService } from "services/user.service"; -// hooks -import useUser from "hooks/use-user"; // ui import { Loader, Tooltip } from "@plane/ui"; // icons @@ -22,11 +20,12 @@ import { USER_PROFILE_PROJECT_SEGREGATION } from "constants/fetch-keys"; // services const userService = new UserService(); -export const ProfileSidebar = () => { +export const ProfileSidebar = observer(() => { + // router const router = useRouter(); const { workspaceSlug, userId } = router.query; - - const { user } = useUser(); + // store hooks + const { currentUser } = useUser(); const { data: userProjectsData } = useSWR( workspaceSlug && userId ? USER_PROFILE_PROJECT_SEGREGATION(workspaceSlug.toString(), userId.toString()) : null, @@ -65,7 +64,7 @@ export const ProfileSidebar = () => { {userProjectsData ? ( <>
    - {user?.id === userId && ( + {currentUser?.id === userId && (
    @@ -149,8 +148,8 @@ export const ProfileSidebar = () => { completedIssuePercentage <= 35 ? "bg-red-500/10 text-red-500" : completedIssuePercentage <= 70 - ? "bg-yellow-500/10 text-yellow-500" - : "bg-green-500/10 text-green-500" + ? "bg-yellow-500/10 text-yellow-500" + : "bg-green-500/10 text-green-500" }`} > {completedIssuePercentage}% @@ -256,4 +255,4 @@ export const ProfileSidebar = () => { )}
    ); -}; +}); diff --git a/web/components/project/card-list.tsx b/web/components/project/card-list.tsx index 6b3e1ebab..f2eb6e4f7 100644 --- a/web/components/project/card-list.tsx +++ b/web/components/project/card-list.tsx @@ -1,7 +1,6 @@ -import { FC } from "react"; import { observer } from "mobx-react-lite"; -// lib -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication, useProject } from "hooks/store"; // components import { ProjectCard } from "components/project"; import { Loader } from "@plane/ui"; @@ -10,22 +9,15 @@ import emptyProject from "public/empty-state/empty_project.webp"; // icons import { NewEmptyState } from "components/common/new-empty-state"; -export interface IProjectCardList { - workspaceSlug: string; -} - -export const ProjectCardList: FC = observer((props) => { - const { workspaceSlug } = props; - // store +export const ProjectCardList = observer(() => { + // store hooks const { - project: projectStore, commandPalette: commandPaletteStore, - trackEvent: { setTrackElement }, - } = useMobxStore(); + eventTracker: { setTrackElement }, + } = useApplication(); + const { workspaceProjects, searchedProjects, getProjectById } = useProject(); - const projects = workspaceSlug ? projectStore.projects[workspaceSlug.toString()] : null; - - if (!projects) { + if (!workspaceProjects) { return ( @@ -40,15 +32,19 @@ export const ProjectCardList: FC = observer((props) => { return ( <> - {projects.length > 0 ? ( + {workspaceProjects.length > 0 ? (
    - {projectStore.searchedProjects.length == 0 ? ( + {searchedProjects.length == 0 ? (
    No matching projects
    ) : (
    - {projectStore.searchedProjects.map((project) => ( - - ))} + {searchedProjects.map((projectId) => { + const projectDetails = getProjectById(projectId); + + if (!projectDetails) return; + + return ; + })}
    )}
    diff --git a/web/components/project/card.tsx b/web/components/project/card.tsx index 503939fab..b6182899c 100644 --- a/web/components/project/card.tsx +++ b/web/components/project/card.tsx @@ -1,11 +1,9 @@ import React, { useState } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; -import { useMobxStore } from "lib/mobx/store-provider"; -import { RootStore } from "store_legacy/root"; -// icons import { LinkIcon, Lock, Pencil, Star } from "lucide-react"; // hooks +import { useProject } from "hooks/store"; import useToast from "hooks/use-toast"; // components import { DeleteProjectModal, JoinProjectModal } from "components/project"; @@ -16,6 +14,8 @@ import { copyTextToClipboard } from "helpers/string.helper"; import { renderEmoji } from "helpers/emoji.helper"; // types import type { IProject } from "types"; +// constants +import { EUserWorkspaceRoles } from "constants/workspace"; export type ProjectCardProps = { project: IProject; @@ -26,21 +26,21 @@ export const ProjectCard: React.FC = observer((props) => { // router const router = useRouter(); const { workspaceSlug } = router.query; - // toast + // toast alert const { setToastAlert } = useToast(); // states const [deleteProjectModalOpen, setDeleteProjectModal] = useState(false); const [joinProjectModalOpen, setJoinProjectModal] = useState(false); + // store hooks + const { addProjectToFavorites, removeProjectFromFavorites } = useProject(); - const { project: projectStore }: RootStore = useMobxStore(); - - const isOwner = project.member_role === 20; - const isMember = project.member_role === 15; + const isOwner = project.member_role === EUserWorkspaceRoles.ADMIN; + const isMember = project.member_role === EUserWorkspaceRoles.MEMBER; const handleAddToFavorites = () => { if (!workspaceSlug) return; - projectStore.addProjectToFavorites(workspaceSlug.toString(), project.id).catch(() => { + addProjectToFavorites(workspaceSlug.toString(), project.id).catch(() => { setToastAlert({ type: "error", title: "Error!", @@ -52,7 +52,7 @@ export const ProjectCard: React.FC = observer((props) => { const handleRemoveFromFavorites = () => { if (!workspaceSlug || !project) return; - projectStore.removeProjectFromFavorites(workspaceSlug.toString(), project.id).catch(() => { + removeProjectFromFavorites(workspaceSlug.toString(), project.id).catch(() => { setToastAlert({ type: "error", title: "Error!", @@ -120,8 +120,8 @@ export const ProjectCard: React.FC = observer((props) => { {project.emoji ? renderEmoji(project.emoji) : project.icon_prop - ? renderEmoji(project.icon_prop) - : null} + ? renderEmoji(project.icon_prop) + : null}
    diff --git a/web/components/project/confirm-project-member-remove.tsx b/web/components/project/confirm-project-member-remove.tsx index 092cbd4aa..aff89e8fb 100644 --- a/web/components/project/confirm-project-member-remove.tsx +++ b/web/components/project/confirm-project-member-remove.tsx @@ -1,10 +1,9 @@ import React, { useState } from "react"; import { Dialog, Transition } from "@headlessui/react"; import { observer } from "mobx-react-lite"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; -// icons import { AlertTriangle } from "lucide-react"; +// hooks +import { useUser } from "hooks/store"; // ui import { Button } from "@plane/ui"; // types @@ -19,13 +18,10 @@ type Props = { export const ConfirmProjectMemberRemove: React.FC = observer((props) => { const { data, onSubmit, isOpen, onClose } = props; - // states const [isDeleteLoading, setIsDeleteLoading] = useState(false); - - const { - user: { currentUser }, - } = useMobxStore(); + // store hooks + const { currentUser } = useUser(); const handleClose = () => { onClose(); @@ -98,8 +94,8 @@ export const ConfirmProjectMemberRemove: React.FC = observer((props) => { ? "Leaving..." : "Leave" : isDeleteLoading - ? "Removing..." - : "Remove"} + ? "Removing..." + : "Remove"}
    diff --git a/web/components/project/create-project-modal.tsx b/web/components/project/create-project-modal.tsx index e8a85600f..7834b508f 100644 --- a/web/components/project/create-project-modal.tsx +++ b/web/components/project/create-project-modal.tsx @@ -2,12 +2,10 @@ import { useState, useEffect, Fragment, FC, ChangeEvent } from "react"; import { useForm, Controller } from "react-hook-form"; import { Dialog, Transition } from "@headlessui/react"; import { observer } from "mobx-react-lite"; -// icons import { X } from "lucide-react"; // hooks -import { useMobxStore } from "lib/mobx/store-provider"; +import { useApplication, useProject, useUser, useWorkspace } from "hooks/store"; import useToast from "hooks/use-toast"; -import { useWorkspaceMyMembership } from "contexts/workspace-member.context"; // ui import { Button, CustomSelect, Input, TextArea } from "@plane/ui"; // components @@ -20,6 +18,8 @@ import { getRandomEmoji, renderEmoji } from "helpers/emoji.helper"; import { IWorkspaceMember } from "types"; // constants import { NETWORK_CHOICES, PROJECT_UNSPLASH_COVERS } from "constants/project"; +// constants +import { EUserWorkspaceRoles } from "constants/workspace"; type Props = { isOpen: boolean; @@ -64,11 +64,13 @@ export const CreateProjectModal: FC = observer((props) => { const { isOpen, onClose, setToFavorite = false, workspaceSlug } = props; // store const { - project: projectStore, - workspaceMember: { workspaceMembers }, - trackEvent: { postHogEventTracker }, - workspace: { currentWorkspace }, - } = useMobxStore(); + eventTracker: { postHogEventTracker }, + } = useApplication(); + const { + membership: { currentWorkspaceRole }, + } = useUser(); + const { currentWorkspace } = useWorkspace(); + const { addProjectToFavorites, createProject } = useProject(); // states const [isChangeInIdentifierRequired, setIsChangeInIdentifierRequired] = useState(true); // toast @@ -95,11 +97,10 @@ export const CreateProjectModal: FC = observer((props) => { reValidateMode: "onChange", }); - const { memberDetails } = useWorkspaceMyMembership(); - const currentNetwork = NETWORK_CHOICES.find((n) => n.key === watch("network")); - if (memberDetails && isOpen) if (memberDetails.role <= 10) return ; + if (currentWorkspaceRole && isOpen) + if (currentWorkspaceRole <= EUserWorkspaceRoles.MEMBER) return ; const handleClose = () => { onClose(); @@ -110,7 +111,7 @@ export const CreateProjectModal: FC = observer((props) => { const handleAddToFavorites = (projectId: string) => { if (!workspaceSlug) return; - projectStore.addProjectToFavorites(workspaceSlug.toString(), projectId).catch(() => { + addProjectToFavorites(workspaceSlug.toString(), projectId).catch(() => { setToastAlert({ type: "error", title: "Error!", @@ -128,8 +129,7 @@ export const CreateProjectModal: FC = observer((props) => { payload.project_lead = formData.project_lead_member; - return projectStore - .createProject(workspaceSlug.toString(), payload) + return createProject(workspaceSlug.toString(), payload) .then((res) => { const newPayload = { ...res, @@ -138,7 +138,7 @@ export const CreateProjectModal: FC = observer((props) => { postHogEventTracker("PROJECT_CREATED", newPayload, { isGrouping: true, groupType: "Workspace_metrics", - gorupId: res.workspace, + groupId: res.workspace, }); setToastAlert({ type: "success", @@ -165,7 +165,7 @@ export const CreateProjectModal: FC = observer((props) => { { isGrouping: true, groupType: "Workspace_metrics", - gorupId: currentWorkspace?.id!, + groupId: currentWorkspace?.id!, } ); }); diff --git a/web/components/project/delete-project-modal.tsx b/web/components/project/delete-project-modal.tsx index 7670c0999..ba32a3744 100644 --- a/web/components/project/delete-project-modal.tsx +++ b/web/components/project/delete-project-modal.tsx @@ -2,16 +2,14 @@ import React from "react"; import { useRouter } from "next/router"; import { Controller, useForm } from "react-hook-form"; import { Dialog, Transition } from "@headlessui/react"; -// hooks -import useToast from "hooks/use-toast"; -// icons import { AlertTriangle } from "lucide-react"; +// hooks +import { useApplication, useProject, useWorkspace } from "hooks/store"; +import useToast from "hooks/use-toast"; // ui import { Button, Input } from "@plane/ui"; // types import type { IProject } from "types"; -// fetch-keys -import { useMobxStore } from "lib/mobx/store-provider"; type DeleteProjectModal = { isOpen: boolean; @@ -26,16 +24,16 @@ const defaultValues = { export const DeleteProjectModal: React.FC = (props) => { const { isOpen, project, onClose } = props; - // store + // store hooks const { - project: projectStore, - workspace: { currentWorkspace }, - trackEvent: { postHogEventTracker }, - } = useMobxStore(); + eventTracker: { postHogEventTracker }, + } = useApplication(); + const { currentWorkspace } = useWorkspace(); + const { deleteProject } = useProject(); // router const router = useRouter(); const { workspaceSlug, projectId } = router.query; - // toast + // toast alert const { setToastAlert } = useToast(); // form info const { @@ -60,8 +58,7 @@ export const DeleteProjectModal: React.FC = (props) => { const onSubmit = async () => { if (!workspaceSlug || !canDelete) return; - await projectStore - .deleteProject(workspaceSlug.toString(), project.id) + await deleteProject(workspaceSlug.toString(), project.id) .then(() => { if (projectId && projectId.toString() === project.id) router.push(`/${workspaceSlug}/projects`); @@ -74,7 +71,7 @@ export const DeleteProjectModal: React.FC = (props) => { { isGrouping: true, groupType: "Workspace_metrics", - gorupId: currentWorkspace?.id!, + groupId: currentWorkspace?.id!, } ); setToastAlert({ @@ -92,7 +89,7 @@ export const DeleteProjectModal: React.FC = (props) => { { isGrouping: true, groupType: "Workspace_metrics", - gorupId: currentWorkspace?.id!, + groupId: currentWorkspace?.id!, } ); setToastAlert({ diff --git a/web/components/project/form.tsx b/web/components/project/form.tsx index f4d753885..dd6937aed 100644 --- a/web/components/project/form.tsx +++ b/web/components/project/form.tsx @@ -1,5 +1,8 @@ import { FC } from "react"; import { Controller, useForm } from "react-hook-form"; +// hooks +import { useApplication, useProject, useWorkspace } from "hooks/store"; +import useToast from "hooks/use-toast"; // components import EmojiIconPicker from "components/emoji-icon-picker"; import { ImagePickerPopover } from "components/core"; @@ -13,9 +16,6 @@ import { renderShortDateWithYearFormat } from "helpers/date-time.helper"; import { NETWORK_CHOICES } from "constants/project"; // services import { ProjectService } from "services/project"; -// hooks -import useToast from "hooks/use-toast"; -import { useMobxStore } from "lib/mobx/store-provider"; export interface IProjectDetailsForm { project: IProject; @@ -27,15 +27,15 @@ const projectService = new ProjectService(); export const ProjectDetailsForm: FC = (props) => { const { project, workspaceSlug, isAdmin } = props; - // store + // store hooks const { - project: projectStore, - trackEvent: { postHogEventTracker }, - workspace: { currentWorkspace }, - } = useMobxStore(); - // toast + eventTracker: { postHogEventTracker }, + } = useApplication(); + const { currentWorkspace } = useWorkspace(); + const { updateProject } = useProject(); + // toast alert const { setToastAlert } = useToast(); - // form data + // form info const { handleSubmit, watch, @@ -60,11 +60,10 @@ export const ProjectDetailsForm: FC = (props) => { setValue("identifier", formattedValue); }; - const updateProject = async (payload: Partial) => { + const handleUpdateChange = async (payload: Partial) => { if (!workspaceSlug || !project) return; - return projectStore - .updateProject(workspaceSlug.toString(), project.id, payload) + return updateProject(workspaceSlug.toString(), project.id, payload) .then((res) => { postHogEventTracker( "PROJECT_UPDATED", @@ -72,7 +71,7 @@ export const ProjectDetailsForm: FC = (props) => { { isGrouping: true, groupType: "Workspace_metrics", - gorupId: res.workspace, + groupId: res.workspace, } ); setToastAlert({ @@ -90,7 +89,7 @@ export const ProjectDetailsForm: FC = (props) => { { isGrouping: true, groupType: "Workspace_metrics", - gorupId: currentWorkspace?.id!, + groupId: currentWorkspace?.id!, } ); setToastAlert({ @@ -125,9 +124,9 @@ export const ProjectDetailsForm: FC = (props) => { .checkProjectIdentifierAvailability(workspaceSlug as string, payload.identifier ?? "") .then(async (res) => { if (res.exists) setError("identifier", { message: "Identifier already exists" }); - else await updateProject(payload); + else await handleUpdateChange(payload); }); - else await updateProject(payload); + else await handleUpdateChange(payload); }; const currentNetwork = NETWORK_CHOICES.find((n) => n.key === project?.network); diff --git a/web/components/project/join-project-modal.tsx b/web/components/project/join-project-modal.tsx index 3f971e756..593ee57ed 100644 --- a/web/components/project/join-project-modal.tsx +++ b/web/components/project/join-project-modal.tsx @@ -1,13 +1,12 @@ import { useState, Fragment } from "react"; import { useRouter } from "next/router"; import { Transition, Dialog } from "@headlessui/react"; +// hooks +import { useProject, useUser } from "hooks/store"; // ui import { Button } from "@plane/ui"; // types import type { IProject } from "types"; -// lib -import { useMobxStore } from "lib/mobx/store-provider"; -import { RootStore } from "store_legacy/root"; // type type TJoinProjectModalProps = { @@ -21,11 +20,11 @@ export const JoinProjectModal: React.FC = (props) => { const { handleClose, isOpen, project, workspaceSlug } = props; // states const [isJoiningLoading, setIsJoiningLoading] = useState(false); - // store + // store hooks const { - project: projectStore, - user: { joinProject }, - }: RootStore = useMobxStore(); + membership: { joinProject }, + } = useUser(); + const { fetchProjects } = useProject(); // router const router = useRouter(); @@ -35,7 +34,7 @@ export const JoinProjectModal: React.FC = (props) => { joinProject(workspaceSlug, [project.id]) .then(() => { router.push(`/${workspaceSlug}/projects/${project.id}/issues`); - projectStore.fetchProjects(workspaceSlug); + fetchProjects(workspaceSlug); handleClose(); }) .finally(() => { diff --git a/web/components/project/leave-project-modal.tsx b/web/components/project/leave-project-modal.tsx index 03a4aeefe..047ac4f45 100644 --- a/web/components/project/leave-project-modal.tsx +++ b/web/components/project/leave-project-modal.tsx @@ -4,12 +4,11 @@ import { Controller, useForm } from "react-hook-form"; import { Dialog, Transition } from "@headlessui/react"; import { AlertTriangleIcon } from "lucide-react"; import { observer } from "mobx-react-lite"; +// hooks +import { useApplication, useUser } from "hooks/store"; +import useToast from "hooks/use-toast"; // ui import { Button, Input } from "@plane/ui"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; -// hooks -import useToast from "hooks/use-toast"; // types import { IProject } from "types"; @@ -34,11 +33,13 @@ export const LeaveProjectModal: FC = observer((props) => { // router const router = useRouter(); const { workspaceSlug } = router.query; - // store + // store hooks const { - user: { leaveProject }, - trackEvent: { postHogEventTracker }, - } = useMobxStore(); + eventTracker: { postHogEventTracker }, + } = useApplication(); + const { + membership: { leaveProject }, + } = useUser(); // toast const { setToastAlert } = useToast(); diff --git a/web/components/project/project-settings-member-defaults.tsx b/web/components/project/project-settings-member-defaults.tsx index 4390a32f8..b6a635be8 100644 --- a/web/components/project/project-settings-member-defaults.tsx +++ b/web/components/project/project-settings-member-defaults.tsx @@ -1,10 +1,9 @@ import { useEffect } from "react"; import { useRouter } from "next/router"; import useSWR from "swr"; -// store import { observer } from "mobx-react-lite"; -import { useMobxStore } from "lib/mobx/store-provider"; // hooks +import { useProject, useUser } from "hooks/store"; import useToast from "hooks/use-toast"; import { Controller, useForm } from "react-hook-form"; @@ -15,6 +14,7 @@ import { Loader } from "@plane/ui"; import { IProject, IUserLite, IWorkspace } from "types"; // fetch-keys import { PROJECT_MEMBERS } from "constants/fetch-keys"; +// constants import { EUserWorkspaceRoles } from "constants/workspace"; const defaultValues: Partial = { @@ -26,10 +26,12 @@ export const ProjectSettingsMemberDefaults: React.FC = observer(() => { // router const router = useRouter(); const { workspaceSlug, projectId } = router.query; - // store - const { user: userStore, project: projectStore } = useMobxStore(); - const { currentProjectDetails } = projectStore; - const { currentProjectRole } = userStore; + // store hooks + const { + membership: { currentProjectRole }, + } = useUser(); + const { currentProjectDetails, fetchProjectDetails, updateProject } = useProject(); + const isAdmin = currentProjectRole === EUserWorkspaceRoles.ADMIN; // hooks const { setToastAlert } = useToast(); @@ -38,9 +40,7 @@ export const ProjectSettingsMemberDefaults: React.FC = observer(() => { // fetching user members useSWR( workspaceSlug && projectId ? PROJECT_MEMBERS(projectId.toString()) : null, - workspaceSlug && projectId - ? () => projectStore.fetchProjectDetails(workspaceSlug.toString(), projectId.toString()) - : null + workspaceSlug && projectId ? () => fetchProjectDetails(workspaceSlug.toString(), projectId.toString()) : null ); useEffect(() => { @@ -64,13 +64,12 @@ export const ProjectSettingsMemberDefaults: React.FC = observer(() => { ...formData, }); - await projectStore - .updateProject(workspaceSlug.toString(), projectId.toString(), { - default_assignee: formData.default_assignee === "none" ? null : formData.default_assignee, - project_lead: formData.project_lead === "none" ? null : formData.project_lead, - }) + await updateProject(workspaceSlug.toString(), projectId.toString(), { + default_assignee: formData.default_assignee === "none" ? null : formData.default_assignee, + project_lead: formData.project_lead === "none" ? null : formData.project_lead, + }) .then(() => { - projectStore.fetchProjectDetails(workspaceSlug.toString(), projectId.toString()); + fetchProjectDetails(workspaceSlug.toString(), projectId.toString()); setToastAlert({ title: "Success", type: "success", diff --git a/web/components/project/publish-project/modal.tsx b/web/components/project/publish-project/modal.tsx index c68d3abac..521a52a1c 100644 --- a/web/components/project/publish-project/modal.tsx +++ b/web/components/project/publish-project/modal.tsx @@ -3,15 +3,14 @@ import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { Controller, useForm } from "react-hook-form"; import { Dialog, Transition } from "@headlessui/react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; -// ui components -import { Button, Loader, ToggleSwitch } from "@plane/ui"; import { Check, CircleDot, Globe2 } from "lucide-react"; +// hooks +import { useProjectPublish } from "hooks/store"; +import useToast from "hooks/use-toast"; +// ui +import { Button, Loader, ToggleSwitch } from "@plane/ui"; import { CustomPopover } from "./popover"; import { IProjectPublishSettings, TProjectPublishViews } from "store_legacy/project"; -// hooks -import useToast from "hooks/use-toast"; // types import { IProject } from "types"; @@ -52,22 +51,29 @@ const viewOptions: { export const PublishProjectModal: React.FC = observer((props) => { const { isOpen, project, onClose } = props; - - const [isUnpublishing, setIsUnpublishing] = useState(false); + // states + const [isUnPublishing, setIsUnPublishing] = useState(false); const [isUpdateRequired, setIsUpdateRequired] = useState(false); let plane_deploy_url = process.env.NEXT_PUBLIC_DEPLOY_URL; if (typeof window !== "undefined" && !plane_deploy_url) plane_deploy_url = window.location.protocol + "//" + window.location.host + "/spaces"; - + // router const router = useRouter(); const { workspaceSlug } = router.query; - - const { projectPublish: projectPublishStore } = useMobxStore(); - + // store hooks + const { + projectPublishSettings, + getProjectSettingsAsync, + publishProject, + updateProjectSettingsAsync, + unPublishProject, + fetchSettingsLoader, + } = useProjectPublish(); + // toast alert const { setToastAlert } = useToast(); - + // form info const { control, formState: { isSubmitting }, @@ -88,14 +94,11 @@ export const PublishProjectModal: React.FC = observer((props) => { // prefill form with the saved settings if the project is already published useEffect(() => { - if ( - projectPublishStore.projectPublishSettings && - projectPublishStore.projectPublishSettings !== "not-initialized" - ) { + if (projectPublishSettings && projectPublishSettings !== "not-initialized") { let userBoards: TProjectPublishViews[] = []; - if (projectPublishStore.projectPublishSettings?.views) { - const savedViews = projectPublishStore.projectPublishSettings?.views; + if (projectPublishSettings?.views) { + const savedViews = projectPublishSettings?.views; if (!savedViews) return; @@ -109,32 +112,31 @@ export const PublishProjectModal: React.FC = observer((props) => { } const updatedData = { - id: projectPublishStore.projectPublishSettings?.id || null, - comments: projectPublishStore.projectPublishSettings?.comments || false, - reactions: projectPublishStore.projectPublishSettings?.reactions || false, - votes: projectPublishStore.projectPublishSettings?.votes || false, - inbox: projectPublishStore.projectPublishSettings?.inbox || null, + id: projectPublishSettings?.id || null, + comments: projectPublishSettings?.comments || false, + reactions: projectPublishSettings?.reactions || false, + votes: projectPublishSettings?.votes || false, + inbox: projectPublishSettings?.inbox || null, views: userBoards, }; reset({ ...updatedData }); } - }, [reset, projectPublishStore.projectPublishSettings, isOpen]); + }, [reset, projectPublishSettings, isOpen]); // fetch publish settings useEffect(() => { if (!workspaceSlug || !isOpen) return; - if (projectPublishStore.projectPublishSettings === "not-initialized") { - projectPublishStore.getProjectSettingsAsync(workspaceSlug.toString(), project.id); + if (projectPublishSettings === "not-initialized") { + getProjectSettingsAsync(workspaceSlug.toString(), project.id); } - }, [isOpen, workspaceSlug, project, projectPublishStore]); + }, [isOpen, workspaceSlug, project, projectPublishSettings, getProjectSettingsAsync]); const handlePublishProject = async (payload: IProjectPublishSettings) => { if (!workspaceSlug) return; - return projectPublishStore - .publishProject(workspaceSlug.toString(), project.id, payload) + return publishProject(workspaceSlug.toString(), project.id, payload) .then((res) => { handleClose(); // window.open(`${plane_deploy_url}/${workspaceSlug}/${project.id}`, "_blank"); @@ -146,8 +148,7 @@ export const PublishProjectModal: React.FC = observer((props) => { const handleUpdatePublishSettings = async (payload: IProjectPublishSettings) => { if (!workspaceSlug) return; - await projectPublishStore - .updateProjectSettingsAsync(workspaceSlug.toString(), project.id, payload.id ?? "", payload) + await updateProjectSettingsAsync(workspaceSlug.toString(), project.id, payload.id ?? "", payload) .then((res) => { setToastAlert({ type: "success", @@ -164,13 +165,12 @@ export const PublishProjectModal: React.FC = observer((props) => { }); }; - const handleUnpublishProject = async (publishId: string) => { + const handleUnPublishProject = async (publishId: string) => { if (!workspaceSlug || !publishId) return; - setIsUnpublishing(true); + setIsUnPublishing(true); - await projectPublishStore - .unPublishProject(workspaceSlug.toString(), project.id, publishId) + await unPublishProject(workspaceSlug.toString(), project.id, publishId) .then((res) => { handleClose(); return res; @@ -179,10 +179,10 @@ export const PublishProjectModal: React.FC = observer((props) => { setToastAlert({ type: "error", title: "Error!", - message: "Something went wrong while unpublishing the project.", + message: "Something went wrong while un-publishing the project.", }) ) - .finally(() => setIsUnpublishing(false)); + .finally(() => setIsUnPublishing(false)); }; const CopyLinkToClipboard = ({ copy_link }: { copy_link: string }) => { @@ -236,10 +236,9 @@ export const PublishProjectModal: React.FC = observer((props) => { // check if an update is required or not const checkIfUpdateIsRequired = () => { - if (!projectPublishStore.projectPublishSettings || projectPublishStore.projectPublishSettings === "not-initialized") - return; + if (!projectPublishSettings || projectPublishSettings === "not-initialized") return; - const currentSettings = projectPublishStore.projectPublishSettings as IProjectPublishSettings; + const currentSettings = projectPublishSettings; const newSettings = getValues(); if ( @@ -298,16 +297,16 @@ export const PublishProjectModal: React.FC = observer((props) => { {project.is_deployed && ( )}
    {/* content */} - {projectPublishStore.fetchSettingsLoader ? ( + {fetchSettingsLoader ? ( @@ -462,7 +461,7 @@ export const PublishProjectModal: React.FC = observer((props) => {
    Anyone with the link can access
    - {!projectPublishStore.fetchSettingsLoader && ( + {!fetchSettingsLoader && (
    diff --git a/web/components/workspace/create-workspace-form.tsx b/web/components/workspace/create-workspace-form.tsx index f322685ef..dc201ab99 100644 --- a/web/components/workspace/create-workspace-form.tsx +++ b/web/components/workspace/create-workspace-form.tsx @@ -2,11 +2,10 @@ import { Dispatch, SetStateAction, useEffect, useState, FC } from "react"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { Controller, useForm } from "react-hook-form"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; // services import { WorkspaceService } from "services/workspace.service"; // hooks +import { useApplication, useWorkspace } from "hooks/store"; import useToast from "hooks/use-toast"; // ui import { Button, CustomSelect, Input } from "@plane/ui"; @@ -43,19 +42,19 @@ export const CreateWorkspaceForm: FC = observer((props) => { default: "Create Workspace", }, } = props; - + // states const [slugError, setSlugError] = useState(false); const [invalidSlug, setInvalidSlug] = useState(false); - + // router const router = useRouter(); - + // store hooks const { - workspace: workspaceStore, - trackEvent: { postHogEventTracker }, - } = useMobxStore(); - + eventTracker: { postHogEventTracker }, + } = useApplication(); + const { createWorkspace } = useWorkspace(); + // toast alert const { setToastAlert } = useToast(); - + // form info const { handleSubmit, control, @@ -71,8 +70,7 @@ export const CreateWorkspaceForm: FC = observer((props) => { if (res.status === true && !RESTRICTED_URLS.includes(formData.slug)) { setSlugError(false); - await workspaceStore - .createWorkspace(formData) + await createWorkspace(formData) .then(async (res) => { postHogEventTracker("WORKSPACE_CREATED", { ...res, diff --git a/web/components/workspace/delete-workspace-modal.tsx b/web/components/workspace/delete-workspace-modal.tsx index b92aeb4bc..d63f8410b 100644 --- a/web/components/workspace/delete-workspace-modal.tsx +++ b/web/components/workspace/delete-workspace-modal.tsx @@ -4,9 +4,8 @@ import { observer } from "mobx-react-lite"; import { Controller, useForm } from "react-hook-form"; import { Dialog, Transition } from "@headlessui/react"; import { AlertTriangle } from "lucide-react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; // hooks +import { useApplication, useWorkspace } from "hooks/store"; import useToast from "hooks/use-toast"; // ui import { Button, Input } from "@plane/ui"; @@ -26,16 +25,16 @@ const defaultValues = { export const DeleteWorkspaceModal: React.FC = observer((props) => { const { isOpen, data, onClose } = props; - + // router const router = useRouter(); - + // store hooks const { - workspace: workspaceStore, - trackEvent: { postHogEventTracker }, - } = useMobxStore(); - + eventTracker: { postHogEventTracker }, + } = useApplication(); + const { deleteWorkspace } = useWorkspace(); + // toast alert const { setToastAlert } = useToast(); - + // form info const { control, formState: { errors, isSubmitting }, @@ -58,8 +57,7 @@ export const DeleteWorkspaceModal: React.FC = observer((props) => { const onSubmit = async () => { if (!data || !canDelete) return; - await workspaceStore - .deleteWorkspace(data.slug) + await deleteWorkspace(data.slug) .then((res) => { handleClose(); router.push("/"); diff --git a/web/components/workspace/help-section.tsx b/web/components/workspace/help-section.tsx index 23663afcf..aa58fcd65 100644 --- a/web/components/workspace/help-section.tsx +++ b/web/components/workspace/help-section.tsx @@ -2,9 +2,8 @@ import React, { useRef, useState } from "react"; import Link from "next/link"; import { Transition } from "@headlessui/react"; import { observer } from "mobx-react-lite"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; // hooks +import { useApplication } from "hooks/store"; import useOutsideClickDetector from "hooks/use-outside-click-detector"; // icons import { FileText, HelpCircle, MessagesSquare, MoveLeft, Zap } from "lucide-react"; @@ -41,11 +40,11 @@ export interface WorkspaceHelpSectionProps { } export const WorkspaceHelpSection: React.FC = observer(() => { - // store + // store hooks const { theme: { sidebarCollapsed, toggleSidebar }, commandPalette: { toggleShortcutModal }, - } = useMobxStore(); + } = useApplication(); // states const [isNeedHelpOpen, setIsNeedHelpOpen] = useState(false); // refs diff --git a/web/components/workspace/send-workspace-invitation-modal.tsx b/web/components/workspace/send-workspace-invitation-modal.tsx index a720e31f5..a0d5740d1 100644 --- a/web/components/workspace/send-workspace-invitation-modal.tsx +++ b/web/components/workspace/send-workspace-invitation-modal.tsx @@ -3,8 +3,8 @@ import { observer } from "mobx-react-lite"; import { Controller, useFieldArray, useForm } from "react-hook-form"; import { Dialog, Transition } from "@headlessui/react"; import { Plus, X } from "lucide-react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useUser } from "hooks/store"; // ui import { Button, CustomSelect, Input } from "@plane/ui"; // types @@ -40,8 +40,8 @@ export const SendWorkspaceInvitationModal: React.FC = observer((props) => const { isOpen, onClose, onSubmit } = props; // mobx store const { - user: { currentWorkspaceRole }, - } = useMobxStore(); + membership: { currentWorkspaceRole }, + } = useUser(); // form info const { control, diff --git a/web/components/workspace/settings/workspace-details.tsx b/web/components/workspace/settings/workspace-details.tsx index e94614961..bafd10f7d 100644 --- a/web/components/workspace/settings/workspace-details.tsx +++ b/web/components/workspace/settings/workspace-details.tsx @@ -3,11 +3,10 @@ import { observer } from "mobx-react-lite"; import { Controller, useForm } from "react-hook-form"; import { Disclosure, Transition } from "@headlessui/react"; import { ChevronDown, ChevronUp, Pencil } from "lucide-react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; // services import { FileService } from "services/file.service"; // hooks +import { useApplication, useUser, useWorkspace } from "hooks/store"; import useToast from "hooks/use-toast"; // components import { DeleteWorkspaceModal } from "components/workspace"; @@ -36,14 +35,15 @@ export const WorkspaceDetails: FC = observer(() => { const [deleteWorkspaceModal, setDeleteWorkspaceModal] = useState(false); const [isImageRemoving, setIsImageRemoving] = useState(false); const [isImageUploadModalOpen, setIsImageUploadModalOpen] = useState(false); - // store + // store hooks const { - workspace: { currentWorkspace, updateWorkspace }, - user: { currentWorkspaceRole }, - trackEvent: { postHogEventTracker }, - } = useMobxStore(); - - // hooks + eventTracker: { postHogEventTracker }, + } = useApplication(); + const { + membership: { currentWorkspaceRole }, + } = useUser(); + const { currentWorkspace, updateWorkspace } = useWorkspace(); + // toast alert const { setToastAlert } = useToast(); // form info const { diff --git a/web/components/workspace/sidebar-dropdown.tsx b/web/components/workspace/sidebar-dropdown.tsx index 8d1cfa27d..3f093a0cb 100644 --- a/web/components/workspace/sidebar-dropdown.tsx +++ b/web/components/workspace/sidebar-dropdown.tsx @@ -6,8 +6,8 @@ import { useTheme } from "next-themes"; import { Menu, Transition } from "@headlessui/react"; import { mutate } from "swr"; import { Check, ChevronDown, LogOut, Plus, Settings, UserCircle2 } from "lucide-react"; -// mobx store -import { useMobxStore } from "lib/mobx/store-provider"; +// hooks +import { useApplication, useUser, useWorkspace } from "hooks/store"; // hooks import useToast from "hooks/use-toast"; // ui @@ -45,15 +45,16 @@ const profileLinks = (workspaceSlug: string, userId: string) => [ ]; export const WorkspaceSidebarDropdown = observer(() => { + // router const router = useRouter(); const { workspaceSlug } = router.query; - // store + // store hooks const { theme: { sidebarCollapsed }, - workspace: { workspaces, currentWorkspace: activeWorkspace }, - user: { currentUser, updateCurrentUser, isUserInstanceAdmin, signOut }, - trackEvent: { setTrackElement }, - } = useMobxStore(); + eventTracker: { setTrackElement }, + } = useApplication(); + const { currentUser, updateCurrentUser, isUserInstanceAdmin, signOut } = useUser(); + const { currentWorkspace: activeWorkspace, workspaces } = useWorkspace(); // hooks const { setToastAlert } = useToast(); const { setTheme } = useTheme(); @@ -90,6 +91,8 @@ export const WorkspaceSidebarDropdown = observer(() => { ); }; + const workspacesList = Object.values(workspaces ?? {}); + return (
    @@ -149,10 +152,10 @@ export const WorkspaceSidebarDropdown = observer(() => { Workspace - {workspaces ? ( + {workspacesList ? (
    - {workspaces.length > 0 ? ( - workspaces.map((workspace: IWorkspace) => ( + {workspacesList.length > 0 ? ( + workspacesList.map((workspace) => ( {() => (