mirror of
https://github.com/makeplane/plane.git
synced 2026-07-13 22:09:12 +02:00
[WEB-4342] fix: copy to clipboard text + added redirection for pages (#3447)
* fix: copy to clipboard text + added redirection for pages * fix: translations * fix: feature flagged * fix: refactor * chore: featureflag check --------- Co-authored-by: sangeethailango <sangeethailango21@gmail.com>
This commit is contained in:
@@ -11,6 +11,8 @@ from plane.ee.models.issue import WorkItemPage
|
||||
from plane.db.models import Page, Workspace
|
||||
from plane.ee.serializers import WorkItemPageSerializer
|
||||
from plane.app.permissions import ProjectLitePermission
|
||||
from plane.payment.flags.flag import FeatureFlag
|
||||
from plane.payment.flags.flag_decorator import check_feature_flag
|
||||
|
||||
|
||||
class IssuePageViewSet(BaseAPIView):
|
||||
@@ -21,6 +23,7 @@ class IssuePageViewSet(BaseAPIView):
|
||||
workspace=workspace, project_id=project_id, issue_id=issue_id
|
||||
)
|
||||
|
||||
@check_feature_flag(FeatureFlag.LINK_PAGES)
|
||||
def post(self, request, slug, project_id, issue_id):
|
||||
workspace = Workspace.objects.get(slug=slug)
|
||||
pages_ids = request.data.get("pages_ids", [])
|
||||
@@ -61,12 +64,14 @@ class IssuePageViewSet(BaseAPIView):
|
||||
serializer = WorkItemPageSerializer(work_item_pages, many=True)
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
|
||||
@check_feature_flag(FeatureFlag.LINK_PAGES)
|
||||
def get(self, request, slug, project_id, issue_id, page_id=None):
|
||||
workspace = Workspace.objects.get(slug=slug)
|
||||
work_item_pages = self.filter_work_item_pages(workspace, project_id, issue_id)
|
||||
serializer = WorkItemPageSerializer(work_item_pages, many=True)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
@check_feature_flag(FeatureFlag.LINK_PAGES)
|
||||
def delete(self, request, slug, project_id, issue_id, page_id):
|
||||
workspace = Workspace.objects.get(slug=slug)
|
||||
work_item_page = self.filter_work_item_pages(
|
||||
@@ -79,6 +84,7 @@ class IssuePageViewSet(BaseAPIView):
|
||||
class PageSearchViewSet(BaseAPIView):
|
||||
permission_classes = [ProjectLitePermission]
|
||||
|
||||
@check_feature_flag(FeatureFlag.LINK_PAGES)
|
||||
def get(self, request, slug, project_id):
|
||||
is_global = request.query_params.get("is_global", False)
|
||||
search = request.query_params.get("search", "")
|
||||
|
||||
@@ -78,6 +78,8 @@ class FeatureFlag(Enum):
|
||||
ANALYTICS_ADVANCED = "ANALYTICS_ADVANCED"
|
||||
# Notion Importer
|
||||
NOTION_IMPORTER = "NOTION_IMPORTER"
|
||||
# Link Pages
|
||||
LINK_PAGES = "LINK_PAGES"
|
||||
|
||||
|
||||
class AdminFeatureFlag(Enum):
|
||||
|
||||
@@ -39,6 +39,7 @@ export enum E_FEATURE_FLAGS {
|
||||
TIME_ESTIMATES = "TIME_ESTIMATES",
|
||||
PROJECT_TEMPLATES_PUBLISH = "PROJECT_TEMPLATES_PUBLISH",
|
||||
COPY_WORK_ITEM = "COPY_WORK_ITEM",
|
||||
LINK_PAGES = "LINK_PAGES",
|
||||
|
||||
// ====== silo importers ======
|
||||
SILO_IMPORTERS = "SILO_IMPORTERS",
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"duplicate": {
|
||||
"modal": {
|
||||
"title": "Make a copy to another project",
|
||||
"description1": "This creates a copy of the work item, its attachments, links.",
|
||||
"description1": "This creates a copy of the work item.",
|
||||
"description2": "All property data will be removed while duplicating.",
|
||||
"placeholder": "Select a project"
|
||||
}
|
||||
@@ -557,7 +557,7 @@
|
||||
},
|
||||
"duplicate": {
|
||||
"modal": {
|
||||
"description1": "This creates a copy of the epic, its attachments, links.",
|
||||
"description1": "This creates a copy of the epic.",
|
||||
"description2": "All property data will be removed while duplicating.",
|
||||
"epics_not_enabled": "Epics not enabled"
|
||||
}
|
||||
|
||||
@@ -1,28 +1,33 @@
|
||||
import { FC } from "react";
|
||||
import { FileText } from "lucide-react";
|
||||
import { E_FEATURE_FLAGS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { TWorkItemAdditionalWidgetActionButtonsProps } from "@/ce/components/issues/issue-detail-widgets/action-buttons";
|
||||
import { IssueDetailWidgetButton } from "@/components/issues";
|
||||
import { WithFeatureFlagHOC } from "@/plane-web/components/feature-flags";
|
||||
import { PagesActionButton } from "./pages";
|
||||
|
||||
export const WorkItemAdditionalWidgetActionButtons: FC<TWorkItemAdditionalWidgetActionButtonsProps> = (props) => {
|
||||
const { workspaceSlug, workItemId, disabled, issueServiceType, hideWidgets } = props;
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
{!hideWidgets?.includes("pages") && (
|
||||
<PagesActionButton
|
||||
issueServiceType={issueServiceType}
|
||||
disabled={disabled}
|
||||
workItemId={workItemId}
|
||||
customButton={
|
||||
<IssueDetailWidgetButton
|
||||
title={t("issue.pages.link_pages")}
|
||||
icon={<FileText className="h-3.5 w-3.5 flex-shrink-0" strokeWidth={2} />}
|
||||
disabled={disabled}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<WithFeatureFlagHOC workspaceSlug={workspaceSlug} flag={E_FEATURE_FLAGS.LINK_PAGES} fallback={<></>}>
|
||||
{!hideWidgets?.includes("pages") && (
|
||||
<PagesActionButton
|
||||
issueServiceType={issueServiceType}
|
||||
disabled={disabled}
|
||||
workItemId={workItemId}
|
||||
customButton={
|
||||
<IssueDetailWidgetButton
|
||||
title={t("issue.pages.link_pages")}
|
||||
icon={<FileText className="h-3.5 w-3.5 flex-shrink-0" strokeWidth={2} />}
|
||||
disabled={disabled}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</WithFeatureFlagHOC>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { FC } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
// ce imports
|
||||
import { E_FEATURE_FLAGS } from "@plane/constants";
|
||||
import { TWorkItemAdditionalWidgetCollapsiblesProps } from "@/ce/components/issues/issue-detail-widgets/collapsibles";
|
||||
// hooks
|
||||
import { useIssueDetail } from "@/hooks/store";
|
||||
// plane web imports
|
||||
import { WithFeatureFlagHOC } from "@/plane-web/components/feature-flags";
|
||||
import { CustomerRequestsCollapsible } from "@/plane-web/components/issues/issue-detail-widgets";
|
||||
import { useCustomers } from "@/plane-web/hooks/store";
|
||||
import { PagesCollapsible } from "./pages";
|
||||
@@ -27,15 +29,17 @@ export const WorkItemAdditionalWidgetCollapsibles: FC<TWorkItemAdditionalWidgetC
|
||||
{shouldRenderCustomerRequest && isCustomersFeatureEnabled && (
|
||||
<CustomerRequestsCollapsible workItemId={workItemId} workspaceSlug={workspaceSlug} disabled={disabled} />
|
||||
)}
|
||||
{shouldRenderPages && (
|
||||
<PagesCollapsible
|
||||
workItemId={workItemId}
|
||||
workspaceSlug={workspaceSlug}
|
||||
disabled={disabled}
|
||||
projectId={issue?.project_id}
|
||||
issueServiceType={issueServiceType}
|
||||
/>
|
||||
)}
|
||||
<WithFeatureFlagHOC workspaceSlug={workspaceSlug} flag={E_FEATURE_FLAGS.LINK_PAGES} fallback={<></>}>
|
||||
{shouldRenderPages && (
|
||||
<PagesCollapsible
|
||||
workItemId={workItemId}
|
||||
workspaceSlug={workspaceSlug}
|
||||
disabled={disabled}
|
||||
projectId={issue?.project_id}
|
||||
issueServiceType={issueServiceType}
|
||||
/>
|
||||
)}
|
||||
</WithFeatureFlagHOC>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import React, { FC } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import Link from "next/link";
|
||||
import { CircleX, Files, FileText, Link2 } from "lucide-react";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { TIssuePage, TIssueServiceType, TLogoProps } from "@plane/types";
|
||||
import { setToast, TContextMenuItem, TOAST_TYPE, CustomMenu, Logo } from "@plane/ui";
|
||||
import { calculateTimeAgo, cn, copyUrlToClipboard } from "@plane/utils";
|
||||
import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
|
||||
import { useIssueDetail, useProject, useWorkspace } from "@/hooks/store";
|
||||
import { useIssueDetail, useProject } from "@/hooks/store";
|
||||
|
||||
type TProps = {
|
||||
issueServiceType: TIssueServiceType;
|
||||
@@ -21,12 +22,10 @@ export const PagesCollapsibleContentBlock: FC<TProps> = observer((props) => {
|
||||
// hooks
|
||||
const { t } = useTranslation();
|
||||
const { getProjectById } = useProject();
|
||||
const { getWorkspaceBySlug } = useWorkspace();
|
||||
const {
|
||||
pages: { deleteIssuePages },
|
||||
} = useIssueDetail(issueServiceType);
|
||||
// derived
|
||||
const workspace = getWorkspaceBySlug(workspaceSlug);
|
||||
const project = getProjectById(projectId);
|
||||
|
||||
const handleCopyText = () => {
|
||||
@@ -36,7 +35,7 @@ export const PagesCollapsibleContentBlock: FC<TProps> = observer((props) => {
|
||||
setToast({
|
||||
type: TOAST_TYPE.SUCCESS,
|
||||
title: t("link_copied"),
|
||||
message: t("project_link_copied_to_clipboard"),
|
||||
message: t("entity.link_copied_to_clipboard", { entity: t("page") }),
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -79,7 +78,15 @@ export const PagesCollapsibleContentBlock: FC<TProps> = observer((props) => {
|
||||
key={page.id}
|
||||
className="flex flex-col gap-2 rounded-xl border border-custom-border-100 p-4 pb-2 min-h-[166px]"
|
||||
>
|
||||
<div className="border-b border-custom-border-100 pb-2 flex flex-col gap-2 flex-1">
|
||||
<Link
|
||||
href={
|
||||
page.is_global
|
||||
? `/${workspaceSlug}/pages/${page.id}`
|
||||
: `/${workspaceSlug}/projects/${projectId}/pages/${page.id}`
|
||||
}
|
||||
target="_blank"
|
||||
className="border-b border-custom-border-100 pb-2 flex flex-col gap-2 flex-1"
|
||||
>
|
||||
<div className="flex gap-2 items-center max-w-full w-fit overflow-hidden bg-custom-background-90 p-1 rounded">
|
||||
<div className="my-auto">
|
||||
{page.is_global ? (
|
||||
@@ -105,7 +112,7 @@ export const PagesCollapsibleContentBlock: FC<TProps> = observer((props) => {
|
||||
{page.description_stripped === "" ? t("issue.pages.no_description") : page.description_stripped}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<div className="flex gap-2 justify-between items-center">
|
||||
<div>{page.created_by && <ButtonAvatars showTooltip userIds={[page.created_by]} />}</div>
|
||||
<div className="flex gap-2">
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import useSWR from "swr";
|
||||
// plane web imports
|
||||
import { E_FEATURE_FLAGS } from "@plane/constants";
|
||||
import { TIssueServiceType } from "@plane/types";
|
||||
import { useIssueDetail } from "@/hooks/store";
|
||||
import { useFlag } from "./store/use-flag";
|
||||
export const useWorkItemProperties = (
|
||||
projectId: string | null | undefined,
|
||||
workspaceSlug: string | null | undefined,
|
||||
@@ -13,11 +15,14 @@ export const useWorkItemProperties = (
|
||||
pages: { fetchPagesByIssueId },
|
||||
} = useIssueDetail(issueServiceType);
|
||||
|
||||
const isPagesInWorkitemWidgetEnabled = useFlag(workspaceSlug?.toString(), E_FEATURE_FLAGS.LINK_PAGES);
|
||||
useSWR(
|
||||
workspaceSlug && projectId && workItemId ? `WORK_ITEM_PAGES_${workspaceSlug}_${projectId}_${workItemId}` : null,
|
||||
workspaceSlug && projectId && workItemId
|
||||
workspaceSlug && projectId && workItemId && isPagesInWorkitemWidgetEnabled
|
||||
? `WORK_ITEM_PAGES_${workspaceSlug}_${projectId}_${workItemId}_${isPagesInWorkitemWidgetEnabled}`
|
||||
: null,
|
||||
workspaceSlug && projectId && workItemId && isPagesInWorkitemWidgetEnabled
|
||||
? () => fetchPagesByIssueId(workspaceSlug, projectId?.toString() ?? "", workItemId)
|
||||
: null,
|
||||
{ revalidateIfStale: false, revalidateOnFocus: true }
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user