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: "После деактивации вы не сможете получать рабочие элементы и оплачивать рабочее пространство. Для реактивации потребуется новое приглашение.",