feat(tasker): open project settings in modal

This commit is contained in:
DCCONSTRUCTIONS
2026-05-10 19:58:30 +03:00
parent bf75ce84eb
commit e49840341b
16 changed files with 725 additions and 40 deletions
@@ -25,13 +25,13 @@ import { CycleAppliedFiltersList } from "@/components/cycles/applied-filters";
import { CyclesView } from "@/components/cycles/cycles-view";
import { CycleCreateUpdateModal } from "@/components/cycles/modal";
import { DetailedEmptyState } from "@/components/empty-state/detailed-empty-state-root";
import { openProjectSettingsModal } from "@/components/project/settings/project-settings-modal.utils";
import { CycleModuleListLayoutLoader } from "@/components/ui/loader/cycle-module-list-loader";
// hooks
import { useCycle } from "@/hooks/store/use-cycle";
import { useCycleFilter } from "@/hooks/store/use-cycle-filter";
import { useProject } from "@/hooks/store/use-project";
import { useUserPermissions } from "@/hooks/store/user";
import { useAppRouter } from "@/hooks/use-app-router";
import type { Route } from "./+types/page";
function ProjectCyclesPage({ params }: Route.ComponentProps) {
@@ -40,8 +40,6 @@ function ProjectCyclesPage({ params }: Route.ComponentProps) {
// store hooks
const { currentProjectCycleIds, loader } = useCycle();
const { getProjectById, currentProjectDetails } = useProject();
// router
const router = useAppRouter();
const { workspaceSlug, projectId } = params;
// theme hook
const { resolvedTheme } = useTheme();
@@ -81,7 +79,7 @@ function ProjectCyclesPage({ params }: Route.ComponentProps) {
primaryButton={{
text: t("disabled_project.empty_state.cycle.primary_button.text"),
onClick: () => {
router.push(`/${workspaceSlug}/settings/projects/${projectId}/features`);
openProjectSettingsModal(projectId, "features_cycles");
},
disabled: !hasAdminLevelPermission,
}}
@@ -18,15 +18,13 @@ import lightIntakeAsset from "@/app/assets/empty-state/disabled-feature/intake-l
import { PageHead } from "@/components/core/page-title";
import { DetailedEmptyState } from "@/components/empty-state/detailed-empty-state-root";
import { InboxIssueRoot } from "@/components/inbox";
import { openProjectSettingsModal } from "@/components/project/settings/project-settings-modal.utils";
// hooks
import { useProject } from "@/hooks/store/use-project";
import { useUserPermissions } from "@/hooks/store/user";
import { useAppRouter } from "@/hooks/use-app-router";
import type { Route } from "./+types/page";
function ProjectInboxPage({ params }: Route.ComponentProps) {
/// router
const router = useAppRouter();
const { workspaceSlug, projectId } = params;
const searchParams = useSearchParams();
const navigationTab = searchParams.get("currentTab");
@@ -53,7 +51,7 @@ function ProjectInboxPage({ params }: Route.ComponentProps) {
primaryButton={{
text: t("disabled_project.empty_state.inbox.primary_button.text"),
onClick: () => {
router.push(`/${workspaceSlug}/settings/projects/${projectId}/features`);
openProjectSettingsModal(projectId, "features_intake");
},
disabled: !canPerformEmptyStateActions,
}}
@@ -20,17 +20,15 @@ import lightModulesAsset from "@/app/assets/empty-state/disabled-feature/modules
import { PageHead } from "@/components/core/page-title";
import { DetailedEmptyState } from "@/components/empty-state/detailed-empty-state-root";
import { ModuleAppliedFiltersList, ModulesListView } from "@/components/modules";
import { openProjectSettingsModal } from "@/components/project/settings/project-settings-modal.utils";
// hooks
import { useModuleFilter } from "@/hooks/store/use-module-filter";
import { useProject } from "@/hooks/store/use-project";
import { useUserPermissions } from "@/hooks/store/user";
import { useAppRouter } from "@/hooks/use-app-router";
import type { Route } from "./+types/page";
function ProjectModulesPage({ params }: Route.ComponentProps) {
// router
const router = useAppRouter();
const { workspaceSlug, projectId } = params;
const { projectId } = params;
// theme hook
const { resolvedTheme } = useTheme();
// plane hooks
@@ -74,7 +72,7 @@ function ProjectModulesPage({ params }: Route.ComponentProps) {
primaryButton={{
text: t("disabled_project.empty_state.module.primary_button.text"),
onClick: () => {
router.push(`/${workspaceSlug}/settings/projects/${projectId}/features`);
openProjectSettingsModal(projectId, "features_modules");
},
disabled: !canPerformEmptyStateActions,
}}
@@ -20,10 +20,10 @@ import { PageHead } from "@/components/core/page-title";
import { DetailedEmptyState } from "@/components/empty-state/detailed-empty-state-root";
import { PagesListRoot } from "@/components/pages/list/root";
import { PagesListView } from "@/components/pages/pages-list-view";
import { openProjectSettingsModal } from "@/components/project/settings/project-settings-modal.utils";
// hooks
import { useProject } from "@/hooks/store/use-project";
import { useUserPermissions } from "@/hooks/store/user";
import { useAppRouter } from "@/hooks/use-app-router";
// plane web hooks
import { EPageStoreType } from "@/plane-web/hooks/store";
import type { Route } from "./+types/page";
@@ -36,7 +36,6 @@ const getPageType = (pageType?: string | null): TPageNavigationTabs => {
function ProjectPagesPage({ params }: Route.ComponentProps) {
// router
const router = useAppRouter();
const searchParams = useSearchParams();
const type = searchParams.get("type");
const { workspaceSlug, projectId } = params;
@@ -65,7 +64,7 @@ function ProjectPagesPage({ params }: Route.ComponentProps) {
primaryButton={{
text: t("disabled_project.empty_state.page.primary_button.text"),
onClick: () => {
router.push(`/${workspaceSlug}/settings/projects/${projectId}/features`);
openProjectSettingsModal(projectId, "features_pages");
},
disabled: !canPerformEmptyStateActions,
}}
@@ -20,19 +20,17 @@ import lightViewsAsset from "@/app/assets/empty-state/disabled-feature/views-lig
// components
import { PageHead } from "@/components/core/page-title";
import { DetailedEmptyState } from "@/components/empty-state/detailed-empty-state-root";
import { openProjectSettingsModal } from "@/components/project/settings/project-settings-modal.utils";
import { ViewAppliedFiltersList } from "@/components/views/applied-filters";
import { ProjectViewsList } from "@/components/views/views-list";
// hooks
import { useProject } from "@/hooks/store/use-project";
import { useProjectView } from "@/hooks/store/use-project-view";
import { useUserPermissions } from "@/hooks/store/user";
import { useAppRouter } from "@/hooks/use-app-router";
import type { Route } from "./+types/page";
function ProjectViewsPage({ params }: Route.ComponentProps) {
// router
const router = useAppRouter();
const { workspaceSlug, projectId } = params;
const { projectId } = params;
// theme hook
const { resolvedTheme } = useTheme();
// plane hooks
@@ -77,7 +75,7 @@ function ProjectViewsPage({ params }: Route.ComponentProps) {
primaryButton={{
text: t("disabled_project.empty_state.view.primary_button.text"),
onClick: () => {
router.push(`/${workspaceSlug}/settings/projects/${projectId}/features`);
openProjectSettingsModal(projectId, "features_views");
},
disabled: !canPerformEmptyStateActions,
}}
@@ -6,8 +6,12 @@
import { observer } from "mobx-react";
import { usePathname } from "next/navigation";
import { Outlet } from "react-router";
import { Outlet, redirect } from "react-router";
// components
import {
buildProjectSettingsModalUrl,
getProjectSettingsModalTabFromPath,
} from "@/components/project/settings/project-settings-modal.utils";
import { getProjectActivePath } from "@/components/settings/helper";
import { SettingsMobileNav } from "@/components/settings/mobile/nav";
// layouts
@@ -16,6 +20,13 @@ import { ProjectAuthWrapper } from "@/layouts/auth-layout/project-wrapper";
import type { Route } from "./+types/layout";
import { ProjectSettingsSidebarRoot } from "@/components/settings/project/sidebar";
export const clientLoader = ({ params, request }: Route.ClientLoaderArgs) => {
const { workspaceSlug, projectId } = params;
const tab = getProjectSettingsModalTabFromPath(new URL(request.url).pathname);
throw redirect(buildProjectSettingsModalUrl(workspaceSlug, projectId, tab));
};
function ProjectDetailSettingsLayout({ params }: Route.ComponentProps) {
const { workspaceSlug, projectId } = params;
// router
@@ -7,6 +7,7 @@
import { useEffect } from "react";
import { observer } from "mobx-react";
import { Outlet } from "react-router";
import { buildProjectSettingsModalUrl } from "@/components/project/settings/project-settings-modal.utils";
// hooks
import { useProject } from "@/hooks/store/use-project";
import { useAppRouter } from "@/hooks/use-app-router";
@@ -23,7 +24,7 @@ function ProjectSettingsLayout({ params }: Route.ComponentProps) {
useEffect(() => {
if (projectId) return;
if (joinedProjectIds.length > 0) {
router.push(`/${workspaceSlug}/settings/projects/${joinedProjectIds[0]}`);
router.push(buildProjectSettingsModalUrl(workspaceSlug, joinedProjectIds[0]));
}
}, [joinedProjectIds, router, workspaceSlug, projectId]);
@@ -10,6 +10,7 @@ import { WorkspaceContentWrapper } from "@/plane-web/components/workspace/conten
import { AppRailVisibilityProvider } from "@/plane-web/hooks/app-rail";
import { GlobalModals } from "@/plane-web/components/common/modal/global";
import { WorkspaceAuthWrapper } from "@/layouts/auth-layout/workspace-wrapper";
import { ProjectSettingsModal } from "@/components/project/settings/project-settings-modal";
import { WorkspaceSettingsModal } from "@/components/workspace/settings/workspace-settings-modal";
import { WorkspaceNotificationsModal } from "@/components/workspace-notifications/notifications-modal";
import type { Route } from "./+types/layout";
@@ -24,6 +25,7 @@ export default function WorkspaceLayout(props: Route.ComponentProps) {
<WorkspaceContentWrapper workspaceSlug={workspaceSlug}>
<GlobalModals workspaceSlug={workspaceSlug} />
<WorkspaceSettingsModal />
<ProjectSettingsModal />
<WorkspaceNotificationsModal />
<Outlet />
</WorkspaceContentWrapper>
@@ -5,13 +5,18 @@
*/
import { redirect } from "react-router";
import {
buildProjectSettingsModalUrl,
getProjectSettingsModalTabFromPath,
} from "@/components/project/settings/project-settings-modal.utils";
import type { Route } from "./+types/project-settings";
export const clientLoader = ({ params }: Route.ClientLoaderArgs) => {
const { workspaceSlug, projectId } = params;
const splat = params["*"] || "";
const destination = `/${workspaceSlug}/settings/projects/${projectId}${splat ? `/${splat}` : ""}/`;
throw redirect(destination);
const tab = getProjectSettingsModalTabFromPath(splat);
throw redirect(buildProjectSettingsModalUrl(workspaceSlug, projectId, tab));
};
export default function ProjectSettings() {