diff --git a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/external-contours/layout.tsx b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/external-contours/layout.tsx
new file mode 100644
index 0000000..c4b8934
--- /dev/null
+++ b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/external-contours/layout.tsx
@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) 2023-present Plane Software, Inc. and contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ * See the LICENSE file for details.
+ */
+
+import { Outlet } from "react-router";
+import { AppHeader } from "@/components/core/app-header";
+import { ContentWrapper } from "@/components/core/content-wrapper";
+import { ProjectExternalContoursHeader } from "@/plane-web/components/projects/external-contours/header";
+
+export default function ProjectExternalContoursLayout() {
+ return (
+ <>
+ } />
+
+
+
+ >
+ );
+}
diff --git a/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/external-contours/page.tsx b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/external-contours/page.tsx
new file mode 100644
index 0000000..dfaf35d
--- /dev/null
+++ b/plane-src/apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/external-contours/page.tsx
@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2023-present Plane Software, Inc. and contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ * See the LICENSE file for details.
+ */
+
+import { observer } from "mobx-react";
+import { useTranslation } from "@plane/i18n";
+import { TransferIcon } from "@plane/propel/icons";
+import { PageHead } from "@/components/core/page-title";
+import { useProject } from "@/hooks/store/use-project";
+import type { Route } from "./+types/page";
+
+function ProjectExternalContoursPage(_props: Route.ComponentProps) {
+ const { t } = useTranslation();
+ const { currentProjectDetails } = useProject();
+
+ const pageTitle = currentProjectDetails?.name
+ ? t("external_contours_page.page_label", { workspace: currentProjectDetails.name })
+ : t("external_contours_page.page_label", { workspace: "NODE.DC" });
+
+ return (
+
+
+
+
+
+
+
+
+
+
{t("external_contours_page.empty_state.title")}
+
+ {t("external_contours_page.empty_state.description")}
+
+
+
+
+
+
+
+
+ {t("external_contours_page.empty_state.request_title")}
+
+
{t("external_contours_page.empty_state.request_description")}
+
+
+
+
+ {t("external_contours_page.empty_state.list_title")}
+
+
{t("external_contours_page.empty_state.list_description")}
+
+
+
+
+ );
+}
+
+export default observer(ProjectExternalContoursPage);
diff --git a/plane-src/apps/web/app/routes/core.ts b/plane-src/apps/web/app/routes/core.ts
index c9c82bd..04dae23 100644
--- a/plane-src/apps/web/app/routes/core.ts
+++ b/plane-src/apps/web/app/routes/core.ts
@@ -216,6 +216,13 @@ export const coreRoutes: RouteConfigEntry[] = [
"./(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/intake/page.tsx"
),
]),
+ // External contours list
+ layout("./(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/external-contours/layout.tsx", [
+ route(
+ ":workspaceSlug/projects/:projectId/external-contours",
+ "./(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/external-contours/page.tsx"
+ ),
+ ]),
]),
// Project Archives - Issues, Cycles, Modules
diff --git a/plane-src/apps/web/ce/components/navigations/use-navigation-items.ts b/plane-src/apps/web/ce/components/navigations/use-navigation-items.ts
index 7e085a1..f03a342 100644
--- a/plane-src/apps/web/ce/components/navigations/use-navigation-items.ts
+++ b/plane-src/apps/web/ce/components/navigations/use-navigation-items.ts
@@ -7,7 +7,7 @@
import { useMemo, useCallback } from "react";
// plane imports
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
-import { CycleIcon, IntakeIcon, ModuleIcon, PageIcon, ViewsIcon, WorkItemsIcon } from "@plane/propel/icons";
+import { CycleIcon, IntakeIcon, ModuleIcon, PageIcon, TransferIcon, ViewsIcon, WorkItemsIcon } from "@plane/propel/icons";
import type { EUserProjectRoles, IPartialProject } from "@plane/types";
import type { TNavigationItem } from "@/components/navigation/tab-navigation-root";
@@ -42,6 +42,16 @@ export const useNavigationItems = ({
shouldRender: true,
sortOrder: 1,
},
+ {
+ i18n_key: "sidebar.external_contours",
+ key: "external_contours",
+ name: "External contours",
+ href: `/${workspaceSlug}/projects/${projectId}/external-contours`,
+ icon: TransferIcon,
+ access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
+ shouldRender: true,
+ sortOrder: 2,
+ },
{
i18n_key: "sidebar.cycles",
key: "cycles",
@@ -50,7 +60,7 @@ export const useNavigationItems = ({
icon: CycleIcon,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
shouldRender: !!project?.cycle_view,
- sortOrder: 2,
+ sortOrder: 3,
},
{
i18n_key: "sidebar.modules",
@@ -60,7 +70,7 @@ export const useNavigationItems = ({
icon: ModuleIcon,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
shouldRender: !!project?.module_view,
- sortOrder: 3,
+ sortOrder: 4,
},
{
i18n_key: "sidebar.views",
@@ -70,7 +80,7 @@ export const useNavigationItems = ({
icon: ViewsIcon,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
shouldRender: !!project?.issue_views_view,
- sortOrder: 4,
+ sortOrder: 5,
},
{
i18n_key: "sidebar.pages",
@@ -80,7 +90,7 @@ export const useNavigationItems = ({
icon: PageIcon,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
shouldRender: !!project?.page_view,
- sortOrder: 5,
+ sortOrder: 6,
},
{
i18n_key: "sidebar.intake",
@@ -90,7 +100,7 @@ export const useNavigationItems = ({
icon: IntakeIcon,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
shouldRender: !!project?.inbox_view,
- sortOrder: 6,
+ sortOrder: 7,
},
],
[project]
diff --git a/plane-src/apps/web/ce/components/projects/external-contours/header.tsx b/plane-src/apps/web/ce/components/projects/external-contours/header.tsx
new file mode 100644
index 0000000..cc600c7
--- /dev/null
+++ b/plane-src/apps/web/ce/components/projects/external-contours/header.tsx
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2023-present Plane Software, Inc. and contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ * See the LICENSE file for details.
+ */
+
+import { observer } from "mobx-react";
+import { useParams } from "next/navigation";
+import { useTranslation } from "@plane/i18n";
+import { TransferIcon } from "@plane/propel/icons";
+import { Breadcrumbs, Header } from "@plane/ui";
+import { BreadcrumbLink } from "@/components/common/breadcrumb-link";
+import { CommonProjectBreadcrumbs } from "@/plane-web/components/breadcrumbs/common";
+
+export const ProjectExternalContoursHeader = observer(function ProjectExternalContoursHeader() {
+ const { workspaceSlug, projectId } = useParams();
+ const { t } = useTranslation();
+
+ return (
+
+
+
+
+ }
+ isLast
+ />
+ }
+ isLast
+ />
+
+
+
+ );
+});
diff --git a/plane-src/apps/web/ce/components/projects/navigation/helper.tsx b/plane-src/apps/web/ce/components/projects/navigation/helper.tsx
index 26baffb..f27c762 100644
--- a/plane-src/apps/web/ce/components/projects/navigation/helper.tsx
+++ b/plane-src/apps/web/ce/components/projects/navigation/helper.tsx
@@ -6,7 +6,7 @@
// plane imports
import { EUserPermissions, EProjectFeatureKey } from "@plane/constants";
-import { CycleIcon, IntakeIcon, ModuleIcon, PageIcon, ViewsIcon, WorkItemsIcon } from "@plane/propel/icons";
+import { CycleIcon, IntakeIcon, ModuleIcon, PageIcon, TransferIcon, ViewsIcon, WorkItemsIcon } from "@plane/propel/icons";
// components
import type { TNavigationItem } from "@/components/workspace/sidebar/project-navigation";
@@ -31,6 +31,16 @@ export const getProjectFeatureNavigation = (
shouldRender: true,
sortOrder: 1,
},
+ {
+ i18n_key: "sidebar.external_contours",
+ key: "external_contours",
+ name: "External contours",
+ href: `/${workspaceSlug}/projects/${projectId}/external-contours`,
+ icon: TransferIcon,
+ access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
+ shouldRender: true,
+ sortOrder: 2,
+ },
{
i18n_key: "sidebar.cycles",
key: EProjectFeatureKey.CYCLES,
@@ -39,7 +49,7 @@ export const getProjectFeatureNavigation = (
icon: CycleIcon,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
shouldRender: project.cycle_view,
- sortOrder: 2,
+ sortOrder: 3,
},
{
i18n_key: "sidebar.modules",
@@ -49,7 +59,7 @@ export const getProjectFeatureNavigation = (
icon: ModuleIcon,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
shouldRender: project.module_view,
- sortOrder: 3,
+ sortOrder: 4,
},
{
i18n_key: "sidebar.views",
@@ -59,7 +69,7 @@ export const getProjectFeatureNavigation = (
icon: ViewsIcon,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
shouldRender: project.issue_views_view,
- sortOrder: 4,
+ sortOrder: 5,
},
{
i18n_key: "sidebar.pages",
@@ -69,7 +79,7 @@ export const getProjectFeatureNavigation = (
icon: PageIcon,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
shouldRender: project.page_view,
- sortOrder: 5,
+ sortOrder: 6,
},
{
i18n_key: "sidebar.intake",
@@ -79,6 +89,6 @@ export const getProjectFeatureNavigation = (
icon: IntakeIcon,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
shouldRender: project.inbox_view,
- sortOrder: 6,
+ sortOrder: 7,
},
];
diff --git a/plane-src/apps/web/core/components/workspace/sidebar/project-navigation.tsx b/plane-src/apps/web/core/components/workspace/sidebar/project-navigation.tsx
index 3582215..91fcd86 100644
--- a/plane-src/apps/web/core/components/workspace/sidebar/project-navigation.tsx
+++ b/plane-src/apps/web/core/components/workspace/sidebar/project-navigation.tsx
@@ -10,7 +10,7 @@ import Link from "next/link";
import { useParams, usePathname } from "next/navigation";
import { EUserPermissionsLevel, EUserPermissions } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
-import { CycleIcon, IntakeIcon, ModuleIcon, PageIcon, ViewsIcon, WorkItemsIcon } from "@plane/propel/icons";
+import { CycleIcon, IntakeIcon, ModuleIcon, PageIcon, TransferIcon, ViewsIcon, WorkItemsIcon } from "@plane/propel/icons";
import type { EUserProjectRoles } from "@plane/types";
// plane ui
// components
@@ -80,6 +80,16 @@ export const ProjectNavigation = observer(function ProjectNavigation(props: TPro
shouldRender: true,
sortOrder: 1,
},
+ {
+ i18n_key: "sidebar.external_contours",
+ key: "external_contours",
+ name: "External contours",
+ href: `/${workspaceSlug}/projects/${projectId}/external-contours`,
+ icon: TransferIcon,
+ access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
+ shouldRender: true,
+ sortOrder: 2,
+ },
{
i18n_key: "sidebar.cycles",
key: "cycles",
@@ -88,7 +98,7 @@ export const ProjectNavigation = observer(function ProjectNavigation(props: TPro
icon: CycleIcon,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
shouldRender: project?.cycle_view ?? false,
- sortOrder: 2,
+ sortOrder: 3,
},
{
i18n_key: "sidebar.modules",
@@ -98,7 +108,7 @@ export const ProjectNavigation = observer(function ProjectNavigation(props: TPro
icon: ModuleIcon,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER],
shouldRender: project?.module_view ?? false,
- sortOrder: 3,
+ sortOrder: 4,
},
{
i18n_key: "sidebar.views",
@@ -108,7 +118,7 @@ export const ProjectNavigation = observer(function ProjectNavigation(props: TPro
icon: ViewsIcon,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
shouldRender: project?.issue_views_view ?? false,
- sortOrder: 4,
+ sortOrder: 5,
},
{
i18n_key: "sidebar.pages",
@@ -118,7 +128,7 @@ export const ProjectNavigation = observer(function ProjectNavigation(props: TPro
icon: PageIcon,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
shouldRender: project?.page_view ?? false,
- sortOrder: 5,
+ sortOrder: 6,
},
{
i18n_key: "sidebar.intake",
@@ -128,7 +138,7 @@ export const ProjectNavigation = observer(function ProjectNavigation(props: TPro
icon: IntakeIcon,
access: [EUserPermissions.ADMIN, EUserPermissions.MEMBER, EUserPermissions.GUEST],
shouldRender: project?.inbox_view ?? false,
- sortOrder: 6,
+ sortOrder: 7,
},
],
[project]
diff --git a/plane-src/packages/i18n/src/locales/en/translations.ts b/plane-src/packages/i18n/src/locales/en/translations.ts
index c2171e2..16b067f 100644
--- a/plane-src/packages/i18n/src/locales/en/translations.ts
+++ b/plane-src/packages/i18n/src/locales/en/translations.ts
@@ -5,6 +5,27 @@
*/
export default {
+ sidebar: {
+ projects: "Projects",
+ pages: "Pages",
+ new_work_item: "New work item",
+ home: "Home",
+ your_work: "Your work",
+ inbox: "Inbox",
+ workspace: "Workspace",
+ views: "Views",
+ analytics: "Analytics",
+ work_items: "Work items",
+ external_contours: "External contours",
+ cycles: "Cycles",
+ modules: "Modules",
+ intake: "Intake",
+ drafts: "Drafts",
+ favorites: "Favorites",
+ pro: "Pro",
+ upgrade: "Upgrade",
+ stickies: "Stickies",
+ },
submit: "Submit",
cancel: "Cancel",
loading: "Loading",
@@ -217,6 +238,7 @@ export default {
modules: "Modules",
pages: "Pages",
intake: "Intake",
+ external_contours: "External contours",
time_tracking: "Time Tracking",
work_management: "Work management",
projects_and_issues: "Projects and work items",
@@ -258,6 +280,21 @@ export default {
you_can_see_here_if_someone_invites_you_to_a_workspace: "You can see here if someone invites you to a workspace",
back_to_home: "Back to home",
workspace_name: "workspace-name",
+ external_contours_page: {
+ page_label: "{workspace} - External contours",
+ title: "External contours",
+ empty_state: {
+ title: "External contours module is ready for the next stage",
+ description:
+ "This screen will host the cross-project request form, the sent requests list, and status pills for each routed task.",
+ request_title: "Send to an external contour",
+ request_description:
+ "The next stage will add the form for selecting the target project, assignee, priority, due date, and description.",
+ list_title: "Sent requests",
+ list_description:
+ "This area will show sent cross-project requests with their current status, a link to the target task, and later synchronization.",
+ },
+ },
deactivate_your_account: "Deactivate your account",
deactivate_your_account_description:
"Once deactivated, you can't be assigned work items and be billed for your workspace. To reactivate your account, you will need an invite to a workspace at this email address.",
diff --git a/plane-src/packages/i18n/src/locales/ru/translations.ts b/plane-src/packages/i18n/src/locales/ru/translations.ts
index 0b7e763..d75d011 100644
--- a/plane-src/packages/i18n/src/locales/ru/translations.ts
+++ b/plane-src/packages/i18n/src/locales/ru/translations.ts
@@ -16,6 +16,7 @@ export default {
views: "Представления",
analytics: "Аналитика",
work_items: "Внутренний контур",
+ external_contours: "Внешние контуры",
cycles: "Циклы",
modules: "Модули",
intake: "Предложения",
@@ -389,6 +390,7 @@ export default {
modules: "Модули",
pages: "Страницы",
intake: "Предложения",
+ external_contours: "Внешние контуры",
time_tracking: "Учет времени",
work_management: "Управление рабочими элементами",
projects_and_issues: "Проекты и рабочие элементы",
@@ -434,6 +436,21 @@ export default {
you_can_see_here_if_someone_invites_you_to_a_workspace: "Здесь отображаются приглашения в рабочие пространства",
back_to_home: "Вернуться на главную",
workspace_name: "название-рабочего-пространства",
+ external_contours_page: {
+ page_label: "{workspace} - Внешние контуры",
+ title: "Внешние контуры",
+ empty_state: {
+ title: "Модуль внешних контуров подготовлен",
+ description:
+ "Здесь появятся форма отправки задачи в другой проект, список отправленных запросов и их статусные маркеры.",
+ request_title: "Отправка во внешний контур",
+ request_description:
+ "На следующем этапе здесь будет форма выбора целевого проекта, исполнителя, приоритета, срока и описания.",
+ list_title: "Отправленные запросы",
+ list_description:
+ "Здесь будет список межпроектных запросов с текущим статусом, ссылкой на целевую задачу и дальнейшей синхронизацией.",
+ },
+ },
deactivate_your_account: "Деактивировать ваш аккаунт",
deactivate_your_account_description:
"После деактивации вы не сможете получать рабочие элементы и оплачивать рабочее пространство. Для реактивации потребуется новое приглашение.",