[WEB-5256]chore: quick actions refactor (#8019)
* chore: quick actions refactor * chore: lint fix * chore: unified factory for actions * chore: lint fix * * chore: removed redundant files * chore: updated imports * chore: updated interfaces to types * chore: updated undefined handling
This commit is contained in:
parent
4b59998e52
commit
2f45bfb7f6
11 changed files with 365 additions and 250 deletions
1
apps/web/ce/components/common/quick-actions-factory.tsx
Normal file
1
apps/web/ce/components/common/quick-actions-factory.tsx
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export { useQuickActionsFactory } from "@/components/common/quick-actions-factory";
|
||||||
|
|
@ -1,2 +1 @@
|
||||||
export * from "./modal";
|
export * from "./modal";
|
||||||
export * from "./use-end-cycle";
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
export const useEndCycle = (isCurrentCycle: boolean) => ({
|
|
||||||
isEndCycleModalOpen: false,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
setEndCycleModalOpen: (value: boolean) => {},
|
|
||||||
endCycleContextMenu: undefined,
|
|
||||||
});
|
|
||||||
|
|
@ -1,7 +1,4 @@
|
||||||
import { ExternalLink, Link, Pencil, Trash2 } from "lucide-react";
|
|
||||||
import { useTranslation } from "@plane/i18n";
|
|
||||||
import type { EIssueLayoutTypes, IProjectView } from "@plane/types";
|
import type { EIssueLayoutTypes, IProjectView } from "@plane/types";
|
||||||
import type { TContextMenuItem } from "@plane/ui";
|
|
||||||
import type { TWorkspaceLayoutProps } from "@/components/views/helper";
|
import type { TWorkspaceLayoutProps } from "@/components/views/helper";
|
||||||
|
|
||||||
export type TLayoutSelectionProps = {
|
export type TLayoutSelectionProps = {
|
||||||
|
|
@ -18,68 +15,6 @@ export function WorkspaceAdditionalLayouts(props: TWorkspaceLayoutProps) {
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TMenuItemsFactoryProps = {
|
|
||||||
isOwner: boolean;
|
|
||||||
isAdmin: boolean;
|
|
||||||
setDeleteViewModal: (open: boolean) => void;
|
|
||||||
setCreateUpdateViewModal: (open: boolean) => void;
|
|
||||||
handleOpenInNewTab: () => void;
|
|
||||||
handleCopyText: () => void;
|
|
||||||
isLocked: boolean;
|
|
||||||
workspaceSlug: string;
|
|
||||||
projectId?: string;
|
|
||||||
viewId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useMenuItemsFactory = (props: TMenuItemsFactoryProps) => {
|
|
||||||
const { isOwner, isAdmin, setDeleteViewModal, setCreateUpdateViewModal, handleOpenInNewTab, handleCopyText } = props;
|
|
||||||
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const editMenuItem = () => ({
|
|
||||||
key: "edit",
|
|
||||||
action: () => setCreateUpdateViewModal(true),
|
|
||||||
title: t("edit"),
|
|
||||||
icon: Pencil,
|
|
||||||
shouldRender: isOwner,
|
|
||||||
});
|
|
||||||
|
|
||||||
const openInNewTabMenuItem = () => ({
|
|
||||||
key: "open-new-tab",
|
|
||||||
action: handleOpenInNewTab,
|
|
||||||
title: t("open_in_new_tab"),
|
|
||||||
icon: ExternalLink,
|
|
||||||
});
|
|
||||||
|
|
||||||
const copyLinkMenuItem = () => ({
|
|
||||||
key: "copy-link",
|
|
||||||
action: handleCopyText,
|
|
||||||
title: t("copy_link"),
|
|
||||||
icon: Link,
|
|
||||||
});
|
|
||||||
|
|
||||||
const deleteMenuItem = () => ({
|
|
||||||
key: "delete",
|
|
||||||
action: () => setDeleteViewModal(true),
|
|
||||||
title: t("delete"),
|
|
||||||
icon: Trash2,
|
|
||||||
shouldRender: isOwner || isAdmin,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
editMenuItem,
|
|
||||||
openInNewTabMenuItem,
|
|
||||||
copyLinkMenuItem,
|
|
||||||
deleteMenuItem,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useViewMenuItems = (props: TMenuItemsFactoryProps): TContextMenuItem[] => {
|
|
||||||
const factory = useMenuItemsFactory(props);
|
|
||||||
|
|
||||||
return [factory.editMenuItem(), factory.openInNewTabMenuItem(), factory.copyLinkMenuItem(), factory.deleteMenuItem()];
|
|
||||||
};
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
export function AdditionalHeaderItems(view: IProjectView) {
|
export function AdditionalHeaderItems(view: IProjectView) {
|
||||||
return <></>;
|
return <></>;
|
||||||
|
|
|
||||||
82
apps/web/core/components/common/quick-actions-factory.tsx
Normal file
82
apps/web/core/components/common/quick-actions-factory.tsx
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
import { Pencil, ExternalLink, Link, Trash2, ArchiveRestoreIcon } from "lucide-react";
|
||||||
|
import { useTranslation } from "@plane/i18n";
|
||||||
|
import { ArchiveIcon } from "@plane/propel/icons";
|
||||||
|
import type { TContextMenuItem } from "@plane/ui";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unified factory for creating menu items across all entities (cycles, modules, views, epics)
|
||||||
|
*/
|
||||||
|
export const useQuickActionsFactory = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return {
|
||||||
|
// Common menu items
|
||||||
|
createEditMenuItem: (handler: () => void, shouldRender: boolean = true): TContextMenuItem => ({
|
||||||
|
key: "edit",
|
||||||
|
title: t("edit"),
|
||||||
|
icon: Pencil,
|
||||||
|
action: handler,
|
||||||
|
shouldRender,
|
||||||
|
}),
|
||||||
|
|
||||||
|
createOpenInNewTabMenuItem: (handler: () => void): TContextMenuItem => ({
|
||||||
|
key: "open-new-tab",
|
||||||
|
title: t("open_in_new_tab"),
|
||||||
|
icon: ExternalLink,
|
||||||
|
action: handler,
|
||||||
|
}),
|
||||||
|
|
||||||
|
createCopyLinkMenuItem: (handler: () => void): TContextMenuItem => ({
|
||||||
|
key: "copy-link",
|
||||||
|
title: t("copy_link"),
|
||||||
|
icon: Link,
|
||||||
|
action: handler,
|
||||||
|
}),
|
||||||
|
|
||||||
|
createArchiveMenuItem: (
|
||||||
|
handler: () => void,
|
||||||
|
opts: { shouldRender?: boolean; disabled?: boolean; description?: string }
|
||||||
|
): TContextMenuItem => ({
|
||||||
|
key: "archive",
|
||||||
|
title: t("archive"),
|
||||||
|
icon: ArchiveIcon,
|
||||||
|
action: handler,
|
||||||
|
className: "items-start",
|
||||||
|
iconClassName: "mt-1",
|
||||||
|
description: opts.description,
|
||||||
|
disabled: opts.disabled,
|
||||||
|
shouldRender: opts.shouldRender,
|
||||||
|
}),
|
||||||
|
|
||||||
|
createRestoreMenuItem: (handler: () => void, shouldRender: boolean = true): TContextMenuItem => ({
|
||||||
|
key: "restore",
|
||||||
|
title: t("restore"),
|
||||||
|
icon: ArchiveRestoreIcon,
|
||||||
|
action: handler,
|
||||||
|
shouldRender,
|
||||||
|
}),
|
||||||
|
|
||||||
|
createDeleteMenuItem: (handler: () => void, shouldRender: boolean = true): TContextMenuItem => ({
|
||||||
|
key: "delete",
|
||||||
|
title: t("delete"),
|
||||||
|
icon: Trash2,
|
||||||
|
action: handler,
|
||||||
|
shouldRender,
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Layout-level actions (for work item list views)
|
||||||
|
createOpenInNewTab: (handler: () => void): TContextMenuItem => ({
|
||||||
|
key: "open-in-new-tab",
|
||||||
|
title: "Open in new tab",
|
||||||
|
icon: ExternalLink,
|
||||||
|
action: handler,
|
||||||
|
}),
|
||||||
|
|
||||||
|
createCopyLayoutLinkMenuItem: (handler: () => void): TContextMenuItem => ({
|
||||||
|
key: "copy-link",
|
||||||
|
title: "Copy link",
|
||||||
|
icon: Link,
|
||||||
|
action: handler,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
};
|
||||||
145
apps/web/core/components/common/quick-actions-helper.tsx
Normal file
145
apps/web/core/components/common/quick-actions-helper.tsx
Normal file
|
|
@ -0,0 +1,145 @@
|
||||||
|
// types
|
||||||
|
import type { ICycle, IModule, IProjectView, IWorkspaceView } from "@plane/types";
|
||||||
|
import type { TContextMenuItem } from "@plane/ui";
|
||||||
|
// hooks
|
||||||
|
import { useQuickActionsFactory } from "@/plane-web/components/common/quick-actions-factory";
|
||||||
|
|
||||||
|
// Types
|
||||||
|
interface UseCycleMenuItemsProps {
|
||||||
|
cycleDetails: ICycle | undefined;
|
||||||
|
isEditingAllowed: boolean;
|
||||||
|
workspaceSlug: string;
|
||||||
|
projectId: string;
|
||||||
|
cycleId: string;
|
||||||
|
handleEdit: () => void;
|
||||||
|
handleArchive: () => void;
|
||||||
|
handleRestore: () => void;
|
||||||
|
handleDelete: () => void;
|
||||||
|
handleCopyLink: () => void;
|
||||||
|
handleOpenInNewTab: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UseModuleMenuItemsProps {
|
||||||
|
moduleDetails: IModule | undefined;
|
||||||
|
isEditingAllowed: boolean;
|
||||||
|
workspaceSlug: string;
|
||||||
|
projectId: string;
|
||||||
|
moduleId: string;
|
||||||
|
handleEdit: () => void;
|
||||||
|
handleArchive: () => void;
|
||||||
|
handleRestore: () => void;
|
||||||
|
handleDelete: () => void;
|
||||||
|
handleCopyLink: () => void;
|
||||||
|
handleOpenInNewTab: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UseViewMenuItemsProps {
|
||||||
|
isOwner: boolean;
|
||||||
|
isAdmin: boolean;
|
||||||
|
workspaceSlug: string;
|
||||||
|
projectId?: string;
|
||||||
|
view: IProjectView | IWorkspaceView;
|
||||||
|
handleEdit: () => void;
|
||||||
|
handleDelete: () => void;
|
||||||
|
handleCopyLink: () => void;
|
||||||
|
handleOpenInNewTab: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UseLayoutMenuItemsProps {
|
||||||
|
workspaceSlug: string;
|
||||||
|
projectId: string;
|
||||||
|
storeType: "PROJECT" | "EPIC";
|
||||||
|
handleCopyLink: () => void;
|
||||||
|
handleOpenInNewTab: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
type MenuResult = {
|
||||||
|
items: TContextMenuItem[];
|
||||||
|
modals: JSX.Element | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useCycleMenuItems = (props: UseCycleMenuItemsProps): MenuResult => {
|
||||||
|
const factory = useQuickActionsFactory();
|
||||||
|
const { cycleDetails, isEditingAllowed, ...handlers } = props;
|
||||||
|
|
||||||
|
const isArchived = !!cycleDetails?.archived_at;
|
||||||
|
const isCompleted = cycleDetails?.status?.toLowerCase() === "completed";
|
||||||
|
|
||||||
|
// Assemble final menu items - order defined here
|
||||||
|
const items = [
|
||||||
|
factory.createEditMenuItem(handlers.handleEdit, isEditingAllowed && !isCompleted && !isArchived),
|
||||||
|
factory.createOpenInNewTabMenuItem(handlers.handleOpenInNewTab),
|
||||||
|
factory.createCopyLinkMenuItem(handlers.handleCopyLink),
|
||||||
|
factory.createArchiveMenuItem(handlers.handleArchive, {
|
||||||
|
shouldRender: isEditingAllowed && !isArchived,
|
||||||
|
disabled: !isCompleted,
|
||||||
|
description: isCompleted ? undefined : "Only completed cycles can be archived",
|
||||||
|
}),
|
||||||
|
factory.createRestoreMenuItem(handlers.handleRestore, isEditingAllowed && isArchived),
|
||||||
|
factory.createDeleteMenuItem(handlers.handleDelete, isEditingAllowed && !isCompleted && !isArchived),
|
||||||
|
].filter((item) => item.shouldRender !== false);
|
||||||
|
|
||||||
|
return { items, modals: null };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useModuleMenuItems = (props: UseModuleMenuItemsProps): MenuResult => {
|
||||||
|
const factory = useQuickActionsFactory();
|
||||||
|
const { moduleDetails, isEditingAllowed, ...handlers } = props;
|
||||||
|
|
||||||
|
const isArchived = !!moduleDetails?.archived_at;
|
||||||
|
const moduleState = moduleDetails?.status?.toLocaleLowerCase();
|
||||||
|
const isInArchivableGroup = !!moduleState && ["completed", "cancelled"].includes(moduleState);
|
||||||
|
|
||||||
|
// Assemble final menu items - order defined here
|
||||||
|
const items = [
|
||||||
|
factory.createEditMenuItem(handlers.handleEdit, isEditingAllowed && !isArchived),
|
||||||
|
factory.createOpenInNewTabMenuItem(handlers.handleOpenInNewTab),
|
||||||
|
factory.createCopyLinkMenuItem(handlers.handleCopyLink),
|
||||||
|
factory.createArchiveMenuItem(handlers.handleArchive, {
|
||||||
|
shouldRender: isEditingAllowed && !isArchived,
|
||||||
|
disabled: !isInArchivableGroup,
|
||||||
|
description: isInArchivableGroup ? undefined : "Only completed or cancelled modules can be archived",
|
||||||
|
}),
|
||||||
|
factory.createRestoreMenuItem(handlers.handleRestore, isEditingAllowed && isArchived),
|
||||||
|
factory.createDeleteMenuItem(handlers.handleDelete, isEditingAllowed && !isArchived),
|
||||||
|
].filter((item) => item.shouldRender !== false);
|
||||||
|
|
||||||
|
return { items, modals: null };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useViewMenuItems = (props: UseViewMenuItemsProps): MenuResult => {
|
||||||
|
const factory = useQuickActionsFactory();
|
||||||
|
const { workspaceSlug, isOwner, isAdmin, projectId, view, ...handlers } = props;
|
||||||
|
|
||||||
|
if (!view) return { items: [], modals: null };
|
||||||
|
|
||||||
|
// Assemble final menu items - order defined here
|
||||||
|
const items = [
|
||||||
|
factory.createEditMenuItem(handlers.handleEdit, isOwner),
|
||||||
|
factory.createOpenInNewTabMenuItem(handlers.handleOpenInNewTab),
|
||||||
|
factory.createCopyLinkMenuItem(handlers.handleCopyLink),
|
||||||
|
factory.createDeleteMenuItem(handlers.handleDelete, isOwner || isAdmin),
|
||||||
|
].filter((item) => item.shouldRender !== false);
|
||||||
|
|
||||||
|
return { items, modals: null };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useLayoutMenuItems = (props: UseLayoutMenuItemsProps): MenuResult => {
|
||||||
|
const factory = useQuickActionsFactory();
|
||||||
|
const { ...handlers } = props;
|
||||||
|
|
||||||
|
// Assemble final menu items - order defined here
|
||||||
|
const items = [
|
||||||
|
factory.createOpenInNewTab(handlers.handleOpenInNewTab),
|
||||||
|
factory.createCopyLayoutLinkMenuItem(handlers.handleCopyLink),
|
||||||
|
].filter((item) => item.shouldRender !== false);
|
||||||
|
|
||||||
|
return { items, modals: null };
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
export const useIntakeHeaderMenuItems = (props: {
|
||||||
|
workspaceSlug: string;
|
||||||
|
projectId: string;
|
||||||
|
handleCopyLink: () => void;
|
||||||
|
}): MenuResult => ({ items: [], modals: null });
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
|
|
||||||
// icons
|
|
||||||
import { ArchiveRestoreIcon, ExternalLink, LinkIcon, Pencil, Trash2 } from "lucide-react";
|
|
||||||
// ui
|
// ui
|
||||||
import {
|
import {
|
||||||
CYCLE_TRACKER_EVENTS,
|
CYCLE_TRACKER_EVENTS,
|
||||||
|
|
@ -11,18 +9,17 @@ import {
|
||||||
CYCLE_TRACKER_ELEMENTS,
|
CYCLE_TRACKER_ELEMENTS,
|
||||||
} from "@plane/constants";
|
} from "@plane/constants";
|
||||||
import { useTranslation } from "@plane/i18n";
|
import { useTranslation } from "@plane/i18n";
|
||||||
import { ArchiveIcon } from "@plane/propel/icons";
|
|
||||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||||
import type { TContextMenuItem } from "@plane/ui";
|
import type { TContextMenuItem } from "@plane/ui";
|
||||||
import { ContextMenu, CustomMenu } from "@plane/ui";
|
import { ContextMenu, CustomMenu } from "@plane/ui";
|
||||||
import { copyUrlToClipboard, cn } from "@plane/utils";
|
import { copyUrlToClipboard, cn } from "@plane/utils";
|
||||||
// helpers
|
// helpers
|
||||||
// hooks
|
// hooks
|
||||||
|
import { useCycleMenuItems } from "@/components/common/quick-actions-helper";
|
||||||
import { captureClick, captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
import { captureClick, captureError, captureSuccess } from "@/helpers/event-tracker.helper";
|
||||||
import { useCycle } from "@/hooks/store/use-cycle";
|
import { useCycle } from "@/hooks/store/use-cycle";
|
||||||
import { useUserPermissions } from "@/hooks/store/user";
|
import { useUserPermissions } from "@/hooks/store/user";
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
import { useEndCycle, EndCycleModal } from "@/plane-web/components/cycles";
|
|
||||||
// local imports
|
// local imports
|
||||||
import { ArchiveCycleModal } from "./archived-cycles/modal";
|
import { ArchiveCycleModal } from "./archived-cycles/modal";
|
||||||
import { CycleDeleteModal } from "./delete-modal";
|
import { CycleDeleteModal } from "./delete-modal";
|
||||||
|
|
@ -50,12 +47,6 @@ export const CycleQuickActions = observer(function CycleQuickActions(props: Prop
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
// derived values
|
// derived values
|
||||||
const cycleDetails = getCycleById(cycleId);
|
const cycleDetails = getCycleById(cycleId);
|
||||||
const isArchived = !!cycleDetails?.archived_at;
|
|
||||||
const isCompleted = cycleDetails?.status?.toLowerCase() === "completed";
|
|
||||||
const isCurrentCycle = cycleDetails?.status?.toLowerCase() === "current";
|
|
||||||
const transferrableIssuesCount = cycleDetails
|
|
||||||
? cycleDetails.total_issues - (cycleDetails.cancelled_issues + cycleDetails.completed_issues)
|
|
||||||
: 0;
|
|
||||||
// auth
|
// auth
|
||||||
const isEditingAllowed = allowPermissions(
|
const isEditingAllowed = allowPermissions(
|
||||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
|
@ -64,8 +55,6 @@ export const CycleQuickActions = observer(function CycleQuickActions(props: Prop
|
||||||
projectId
|
projectId
|
||||||
);
|
);
|
||||||
|
|
||||||
const { isEndCycleModalOpen, setEndCycleModalOpen, endCycleContextMenu } = useEndCycle(isCurrentCycle);
|
|
||||||
|
|
||||||
const cycleLink = `${workspaceSlug}/projects/${projectId}/cycles/${cycleId}`;
|
const cycleLink = `${workspaceSlug}/projects/${projectId}/cycles/${cycleId}`;
|
||||||
const handleCopyText = () =>
|
const handleCopyText = () =>
|
||||||
copyUrlToClipboard(cycleLink).then(() => {
|
copyUrlToClipboard(cycleLink).then(() => {
|
||||||
|
|
@ -77,12 +66,6 @@ export const CycleQuickActions = observer(function CycleQuickActions(props: Prop
|
||||||
});
|
});
|
||||||
const handleOpenInNewTab = () => window.open(`/${cycleLink}`, "_blank");
|
const handleOpenInNewTab = () => window.open(`/${cycleLink}`, "_blank");
|
||||||
|
|
||||||
const handleEditCycle = () => {
|
|
||||||
setUpdateModal(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleArchiveCycle = () => setArchiveCycleModal(true);
|
|
||||||
|
|
||||||
const handleRestoreCycle = async () =>
|
const handleRestoreCycle = async () =>
|
||||||
await restoreCycle(workspaceSlug, projectId, cycleId)
|
await restoreCycle(workspaceSlug, projectId, cycleId)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
@ -113,60 +96,22 @@ export const CycleQuickActions = observer(function CycleQuickActions(props: Prop
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleDeleteCycle = () => {
|
const menuResult = useCycleMenuItems({
|
||||||
setDeleteModal(true);
|
cycleDetails: cycleDetails ?? undefined,
|
||||||
};
|
workspaceSlug,
|
||||||
|
projectId,
|
||||||
|
cycleId,
|
||||||
|
isEditingAllowed,
|
||||||
|
handleEdit: () => setUpdateModal(true),
|
||||||
|
handleArchive: () => setArchiveCycleModal(true),
|
||||||
|
handleRestore: handleRestoreCycle,
|
||||||
|
handleDelete: () => setDeleteModal(true),
|
||||||
|
handleCopyLink: handleCopyText,
|
||||||
|
handleOpenInNewTab,
|
||||||
|
});
|
||||||
|
|
||||||
const MENU_ITEMS: TContextMenuItem[] = [
|
const MENU_ITEMS: TContextMenuItem[] = Array.isArray(menuResult) ? menuResult : menuResult.items;
|
||||||
{
|
const additionalModals = Array.isArray(menuResult) ? null : menuResult.modals;
|
||||||
key: "edit",
|
|
||||||
title: t("edit"),
|
|
||||||
icon: Pencil,
|
|
||||||
action: handleEditCycle,
|
|
||||||
shouldRender: isEditingAllowed && !isCompleted && !isArchived,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "open-new-tab",
|
|
||||||
action: handleOpenInNewTab,
|
|
||||||
title: t("open_in_new_tab"),
|
|
||||||
icon: ExternalLink,
|
|
||||||
shouldRender: !isArchived,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "copy-link",
|
|
||||||
action: handleCopyText,
|
|
||||||
title: t("copy_link"),
|
|
||||||
icon: LinkIcon,
|
|
||||||
shouldRender: !isArchived,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "archive",
|
|
||||||
action: handleArchiveCycle,
|
|
||||||
title: t("archive"),
|
|
||||||
description: isCompleted ? undefined : t("project_cycles.only_completed_cycles_can_be_archived"),
|
|
||||||
icon: ArchiveIcon,
|
|
||||||
className: "items-start",
|
|
||||||
iconClassName: "mt-1",
|
|
||||||
shouldRender: isEditingAllowed && !isArchived,
|
|
||||||
disabled: !isCompleted,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "restore",
|
|
||||||
action: handleRestoreCycle,
|
|
||||||
title: t("restore"),
|
|
||||||
icon: ArchiveRestoreIcon,
|
|
||||||
shouldRender: isEditingAllowed && isArchived,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "delete",
|
|
||||||
action: handleDeleteCycle,
|
|
||||||
title: t("delete"),
|
|
||||||
icon: Trash2,
|
|
||||||
shouldRender: isEditingAllowed && !isCompleted && !isArchived,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
if (endCycleContextMenu) MENU_ITEMS.splice(3, 0, endCycleContextMenu);
|
|
||||||
|
|
||||||
const CONTEXT_MENU_ITEMS = MENU_ITEMS.map(function CONTEXT_MENU_ITEMS(item) {
|
const CONTEXT_MENU_ITEMS = MENU_ITEMS.map(function CONTEXT_MENU_ITEMS(item) {
|
||||||
return {
|
return {
|
||||||
|
|
@ -206,17 +151,7 @@ export const CycleQuickActions = observer(function CycleQuickActions(props: Prop
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
/>
|
/>
|
||||||
{isCurrentCycle && (
|
{additionalModals}
|
||||||
<EndCycleModal
|
|
||||||
isOpen={isEndCycleModalOpen}
|
|
||||||
handleClose={() => setEndCycleModalOpen(false)}
|
|
||||||
cycleId={cycleId}
|
|
||||||
projectId={projectId}
|
|
||||||
workspaceSlug={workspaceSlug}
|
|
||||||
transferrableIssuesCount={transferrableIssuesCount}
|
|
||||||
cycleName={cycleDetails.name}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<ContextMenu parentRef={parentRef} items={CONTEXT_MENU_ITEMS} />
|
<ContextMenu parentRef={parentRef} items={CONTEXT_MENU_ITEMS} />
|
||||||
|
|
|
||||||
72
apps/web/core/components/issues/layout-quick-actions.tsx
Normal file
72
apps/web/core/components/issues/layout-quick-actions.tsx
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { observer } from "mobx-react";
|
||||||
|
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||||
|
import type { TContextMenuItem } from "@plane/ui";
|
||||||
|
import { CustomMenu } from "@plane/ui";
|
||||||
|
import { copyUrlToClipboard, cn } from "@plane/utils";
|
||||||
|
import { useLayoutMenuItems } from "@/components/common/quick-actions-helper";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
workspaceSlug: string;
|
||||||
|
projectId: string;
|
||||||
|
storeType: "PROJECT" | "EPIC";
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LayoutQuickActions: React.FC<Props> = observer((props) => {
|
||||||
|
const { workspaceSlug, projectId, storeType } = props;
|
||||||
|
|
||||||
|
const layoutLink = `${workspaceSlug}/projects/${projectId}/${storeType === "EPIC" ? "epics" : "issues"}`;
|
||||||
|
|
||||||
|
const handleCopyLink = () =>
|
||||||
|
copyUrlToClipboard(layoutLink).then(() => {
|
||||||
|
setToast({
|
||||||
|
type: TOAST_TYPE.SUCCESS,
|
||||||
|
title: "Link copied",
|
||||||
|
message: `${storeType === "EPIC" ? "Epics" : "Work items"} link copied to clipboard.`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleOpenInNewTab = () => window.open(`/${layoutLink}`, "_blank");
|
||||||
|
|
||||||
|
const menuResult = useLayoutMenuItems({
|
||||||
|
workspaceSlug,
|
||||||
|
projectId,
|
||||||
|
storeType,
|
||||||
|
handleCopyLink,
|
||||||
|
handleOpenInNewTab,
|
||||||
|
});
|
||||||
|
|
||||||
|
const MENU_ITEMS: TContextMenuItem[] = Array.isArray(menuResult) ? menuResult : menuResult.items;
|
||||||
|
const additionalModals = Array.isArray(menuResult) ? null : menuResult.modals;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{additionalModals}
|
||||||
|
<CustomMenu
|
||||||
|
ellipsis
|
||||||
|
placement="bottom-end"
|
||||||
|
closeOnSelect
|
||||||
|
maxHeight="lg"
|
||||||
|
className="flex-shrink-0 flex items-center justify-center size-[26px] bg-custom-background-80/70 rounded"
|
||||||
|
>
|
||||||
|
{MENU_ITEMS.map((item) => {
|
||||||
|
if (item.shouldRender === false) return null;
|
||||||
|
return (
|
||||||
|
<CustomMenu.MenuItem
|
||||||
|
key={item.key}
|
||||||
|
onClick={item.action}
|
||||||
|
className={cn("flex items-center gap-2", {
|
||||||
|
"text-custom-text-400": item.disabled,
|
||||||
|
})}
|
||||||
|
disabled={item.disabled}
|
||||||
|
>
|
||||||
|
{item.icon && <item.icon className="h-3 w-3" />}
|
||||||
|
<span>{item.title}</span>
|
||||||
|
</CustomMenu.MenuItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</CustomMenu>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
|
|
||||||
// icons
|
|
||||||
import { ArchiveRestoreIcon, ExternalLink, LinkIcon, Pencil, Trash2 } from "lucide-react";
|
|
||||||
// plane imports
|
// plane imports
|
||||||
import {
|
import {
|
||||||
EUserPermissions,
|
EUserPermissions,
|
||||||
|
|
@ -11,13 +9,12 @@ import {
|
||||||
MODULE_TRACKER_EVENTS,
|
MODULE_TRACKER_EVENTS,
|
||||||
} from "@plane/constants";
|
} from "@plane/constants";
|
||||||
import { useTranslation } from "@plane/i18n";
|
import { useTranslation } from "@plane/i18n";
|
||||||
// ui
|
|
||||||
import { ArchiveIcon } from "@plane/propel/icons";
|
|
||||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||||
import type { TContextMenuItem } from "@plane/ui";
|
import type { TContextMenuItem } from "@plane/ui";
|
||||||
import { ContextMenu, CustomMenu } from "@plane/ui";
|
import { ContextMenu, CustomMenu } from "@plane/ui";
|
||||||
import { copyUrlToClipboard, cn } from "@plane/utils";
|
import { copyUrlToClipboard, cn } from "@plane/utils";
|
||||||
// components
|
// components
|
||||||
|
import { useModuleMenuItems } from "@/components/common/quick-actions-helper";
|
||||||
import { ArchiveModuleModal, CreateUpdateModuleModal, DeleteModuleModal } from "@/components/modules";
|
import { ArchiveModuleModal, CreateUpdateModuleModal, DeleteModuleModal } from "@/components/modules";
|
||||||
// helpers
|
// helpers
|
||||||
import { captureClick, captureSuccess, captureError } from "@/helpers/event-tracker.helper";
|
import { captureClick, captureSuccess, captureError } from "@/helpers/event-tracker.helper";
|
||||||
|
|
@ -50,7 +47,6 @@ export const ModuleQuickActions = observer(function ModuleQuickActions(props: Pr
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
// derived values
|
// derived values
|
||||||
const moduleDetails = getModuleById(moduleId);
|
const moduleDetails = getModuleById(moduleId);
|
||||||
const isArchived = !!moduleDetails?.archived_at;
|
|
||||||
// auth
|
// auth
|
||||||
const isEditingAllowed = allowPermissions(
|
const isEditingAllowed = allowPermissions(
|
||||||
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
|
||||||
|
|
@ -59,9 +55,6 @@ export const ModuleQuickActions = observer(function ModuleQuickActions(props: Pr
|
||||||
projectId
|
projectId
|
||||||
);
|
);
|
||||||
|
|
||||||
const moduleState = moduleDetails?.status?.toLocaleLowerCase();
|
|
||||||
const isInArchivableGroup = !!moduleState && ["completed", "cancelled"].includes(moduleState);
|
|
||||||
|
|
||||||
const moduleLink = `${workspaceSlug}/projects/${projectId}/modules/${moduleId}`;
|
const moduleLink = `${workspaceSlug}/projects/${projectId}/modules/${moduleId}`;
|
||||||
const handleCopyText = () =>
|
const handleCopyText = () =>
|
||||||
copyUrlToClipboard(moduleLink).then(() => {
|
copyUrlToClipboard(moduleLink).then(() => {
|
||||||
|
|
@ -73,12 +66,6 @@ export const ModuleQuickActions = observer(function ModuleQuickActions(props: Pr
|
||||||
});
|
});
|
||||||
const handleOpenInNewTab = () => window.open(`/${moduleLink}`, "_blank");
|
const handleOpenInNewTab = () => window.open(`/${moduleLink}`, "_blank");
|
||||||
|
|
||||||
const handleEditModule = () => {
|
|
||||||
setEditModal(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleArchiveModule = () => setArchiveModuleModal(true);
|
|
||||||
|
|
||||||
const handleRestoreModule = async () =>
|
const handleRestoreModule = async () =>
|
||||||
await restoreModule(workspaceSlug, projectId, moduleId)
|
await restoreModule(workspaceSlug, projectId, moduleId)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
@ -106,72 +93,35 @@ export const ModuleQuickActions = observer(function ModuleQuickActions(props: Pr
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleDeleteModule = () => {
|
// Use unified menu hook from plane-web (resolves to CE or EE)
|
||||||
setDeleteModal(true);
|
const menuResult = useModuleMenuItems({
|
||||||
};
|
moduleDetails: moduleDetails ?? undefined,
|
||||||
|
workspaceSlug,
|
||||||
const MENU_ITEMS: TContextMenuItem[] = [
|
projectId,
|
||||||
{
|
moduleId,
|
||||||
key: "edit",
|
isEditingAllowed,
|
||||||
title: t("edit"),
|
handleEdit: () => setEditModal(true),
|
||||||
icon: Pencil,
|
handleArchive: () => setArchiveModuleModal(true),
|
||||||
action: handleEditModule,
|
handleRestore: handleRestoreModule,
|
||||||
shouldRender: isEditingAllowed && !isArchived,
|
handleDelete: () => setDeleteModal(true),
|
||||||
},
|
handleCopyLink: handleCopyText,
|
||||||
{
|
handleOpenInNewTab,
|
||||||
key: "open-new-tab",
|
|
||||||
action: handleOpenInNewTab,
|
|
||||||
title: t("open_in_new_tab"),
|
|
||||||
icon: ExternalLink,
|
|
||||||
shouldRender: !isArchived,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "copy-link",
|
|
||||||
action: handleCopyText,
|
|
||||||
title: t("copy_link"),
|
|
||||||
icon: LinkIcon,
|
|
||||||
shouldRender: !isArchived,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "archive",
|
|
||||||
action: handleArchiveModule,
|
|
||||||
title: t("archive"),
|
|
||||||
description: isInArchivableGroup ? undefined : t("project_module.quick_actions.archive_module_description"),
|
|
||||||
icon: ArchiveIcon,
|
|
||||||
className: "items-start",
|
|
||||||
iconClassName: "mt-1",
|
|
||||||
shouldRender: isEditingAllowed && !isArchived,
|
|
||||||
disabled: !isInArchivableGroup,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "restore",
|
|
||||||
action: handleRestoreModule,
|
|
||||||
title: t("restore"),
|
|
||||||
icon: ArchiveRestoreIcon,
|
|
||||||
shouldRender: isEditingAllowed && isArchived,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "delete",
|
|
||||||
action: handleDeleteModule,
|
|
||||||
title: t("delete"),
|
|
||||||
icon: Trash2,
|
|
||||||
shouldRender: isEditingAllowed,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const CONTEXT_MENU_ITEMS = MENU_ITEMS.map(function CONTEXT_MENU_ITEMS(item) {
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
|
|
||||||
onClick: () => {
|
|
||||||
captureClick({
|
|
||||||
elementName: MODULE_TRACKER_ELEMENTS.CONTEXT_MENU,
|
|
||||||
});
|
|
||||||
item.action();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Handle both CE (array) and EE (object) return types
|
||||||
|
const MENU_ITEMS: TContextMenuItem[] = Array.isArray(menuResult) ? menuResult : menuResult.items;
|
||||||
|
const additionalModals = Array.isArray(menuResult) ? null : menuResult.modals;
|
||||||
|
|
||||||
|
const CONTEXT_MENU_ITEMS: TContextMenuItem[] = MENU_ITEMS.map((item) => ({
|
||||||
|
...item,
|
||||||
|
action: () => {
|
||||||
|
captureClick({
|
||||||
|
elementName: MODULE_TRACKER_ELEMENTS.CONTEXT_MENU,
|
||||||
|
});
|
||||||
|
item.action();
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{moduleDetails && (
|
{moduleDetails && (
|
||||||
|
|
@ -191,6 +141,7 @@ export const ModuleQuickActions = observer(function ModuleQuickActions(props: Pr
|
||||||
handleClose={() => setArchiveModuleModal(false)}
|
handleClose={() => setArchiveModuleModal(false)}
|
||||||
/>
|
/>
|
||||||
<DeleteModuleModal data={moduleDetails} isOpen={deleteModal} onClose={() => setDeleteModal(false)} />
|
<DeleteModuleModal data={moduleDetails} isOpen={deleteModal} onClose={() => setDeleteModal(false)} />
|
||||||
|
{additionalModals}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<ContextMenu parentRef={parentRef} items={CONTEXT_MENU_ITEMS} />
|
<ContextMenu parentRef={parentRef} items={CONTEXT_MENU_ITEMS} />
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,10 @@ import type { TContextMenuItem } from "@plane/ui";
|
||||||
import { ContextMenu, CustomMenu } from "@plane/ui";
|
import { ContextMenu, CustomMenu } from "@plane/ui";
|
||||||
import { copyUrlToClipboard, cn } from "@plane/utils";
|
import { copyUrlToClipboard, cn } from "@plane/utils";
|
||||||
// helpers
|
// helpers
|
||||||
|
import { useViewMenuItems } from "@/components/common/quick-actions-helper";
|
||||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser, useUserPermissions } from "@/hooks/store/user";
|
import { useUser, useUserPermissions } from "@/hooks/store/user";
|
||||||
import { useViewMenuItems } from "@/plane-web/components/views/helper";
|
|
||||||
import { PublishViewModal, useViewPublish } from "@/plane-web/components/views/publish";
|
import { PublishViewModal, useViewPublish } from "@/plane-web/components/views/publish";
|
||||||
// local imports
|
// local imports
|
||||||
import { DeleteProjectViewModal } from "./delete-view-modal";
|
import { DeleteProjectViewModal } from "./delete-view-modal";
|
||||||
|
|
@ -54,19 +54,22 @@ export const ViewQuickActions = observer(function ViewQuickActions(props: Props)
|
||||||
});
|
});
|
||||||
const handleOpenInNewTab = () => window.open(`/${viewLink}`, "_blank");
|
const handleOpenInNewTab = () => window.open(`/${viewLink}`, "_blank");
|
||||||
|
|
||||||
const MENU_ITEMS: TContextMenuItem[] = useViewMenuItems({
|
const menuResult = useViewMenuItems({
|
||||||
isOwner,
|
isOwner,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
setDeleteViewModal,
|
|
||||||
setCreateUpdateViewModal,
|
|
||||||
handleOpenInNewTab,
|
|
||||||
handleCopyText,
|
|
||||||
isLocked: view.is_locked,
|
|
||||||
workspaceSlug,
|
workspaceSlug,
|
||||||
projectId,
|
projectId,
|
||||||
viewId: view.id,
|
view,
|
||||||
|
handleEdit: () => setCreateUpdateViewModal(true),
|
||||||
|
handleDelete: () => setDeleteViewModal(true),
|
||||||
|
handleCopyLink: handleCopyText,
|
||||||
|
handleOpenInNewTab,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Handle both CE (array) and EE (object) return types
|
||||||
|
const MENU_ITEMS: TContextMenuItem[] = Array.isArray(menuResult) ? menuResult : menuResult.items;
|
||||||
|
const additionalModals = Array.isArray(menuResult) ? null : menuResult.modals;
|
||||||
|
|
||||||
if (publishContextMenu) MENU_ITEMS.splice(2, 0, publishContextMenu);
|
if (publishContextMenu) MENU_ITEMS.splice(2, 0, publishContextMenu);
|
||||||
|
|
||||||
const CONTEXT_MENU_ITEMS = MENU_ITEMS.map(function CONTEXT_MENU_ITEMS(item) {
|
const CONTEXT_MENU_ITEMS = MENU_ITEMS.map(function CONTEXT_MENU_ITEMS(item) {
|
||||||
|
|
@ -91,6 +94,7 @@ export const ViewQuickActions = observer(function ViewQuickActions(props: Props)
|
||||||
/>
|
/>
|
||||||
<DeleteProjectViewModal data={view} isOpen={deleteViewModal} onClose={() => setDeleteViewModal(false)} />
|
<DeleteProjectViewModal data={view} isOpen={deleteViewModal} onClose={() => setDeleteViewModal(false)} />
|
||||||
<PublishViewModal isOpen={isPublishModalOpen} onClose={() => setPublishModalOpen(false)} view={view} />
|
<PublishViewModal isOpen={isPublishModalOpen} onClose={() => setPublishModalOpen(false)} view={view} />
|
||||||
|
{additionalModals}
|
||||||
<ContextMenu parentRef={parentRef} items={CONTEXT_MENU_ITEMS} />
|
<ContextMenu parentRef={parentRef} items={CONTEXT_MENU_ITEMS} />
|
||||||
<CustomMenu ellipsis placement="bottom-end" closeOnSelect buttonClassName={customClassName}>
|
<CustomMenu ellipsis placement="bottom-end" closeOnSelect buttonClassName={customClassName}>
|
||||||
{MENU_ITEMS.map((item) => {
|
{MENU_ITEMS.map((item) => {
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,13 @@ import { observer } from "mobx-react";
|
||||||
import { EUserPermissions, EUserPermissionsLevel, GLOBAL_VIEW_TRACKER_ELEMENTS } from "@plane/constants";
|
import { EUserPermissions, EUserPermissionsLevel, GLOBAL_VIEW_TRACKER_ELEMENTS } from "@plane/constants";
|
||||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||||
import type { IWorkspaceView } from "@plane/types";
|
import type { IWorkspaceView } from "@plane/types";
|
||||||
import type { TContextMenuItem } from "@plane/ui";
|
|
||||||
import { CustomMenu } from "@plane/ui";
|
import { CustomMenu } from "@plane/ui";
|
||||||
import { copyUrlToClipboard, cn } from "@plane/utils";
|
import { copyUrlToClipboard, cn } from "@plane/utils";
|
||||||
// helpers
|
// helpers
|
||||||
|
import { useViewMenuItems } from "@/components/common/quick-actions-helper";
|
||||||
import { captureClick } from "@/helpers/event-tracker.helper";
|
import { captureClick } from "@/helpers/event-tracker.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useUser, useUserPermissions } from "@/hooks/store/user";
|
import { useUser, useUserPermissions } from "@/hooks/store/user";
|
||||||
import { useViewMenuItems } from "@/plane-web/components/views/helper";
|
|
||||||
// local imports
|
// local imports
|
||||||
import { DeleteGlobalViewModal } from "./delete-view-modal";
|
import { DeleteGlobalViewModal } from "./delete-view-modal";
|
||||||
import { CreateUpdateWorkspaceViewModal } from "./modal";
|
import { CreateUpdateWorkspaceViewModal } from "./modal";
|
||||||
|
|
@ -44,16 +43,15 @@ export const WorkspaceViewQuickActions = observer(function WorkspaceViewQuickAct
|
||||||
});
|
});
|
||||||
const handleOpenInNewTab = () => window.open(`/${viewLink}`, "_blank");
|
const handleOpenInNewTab = () => window.open(`/${viewLink}`, "_blank");
|
||||||
|
|
||||||
const MENU_ITEMS: TContextMenuItem[] = useViewMenuItems({
|
const MENU_ITEMS = useViewMenuItems({
|
||||||
isOwner,
|
isOwner,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
setDeleteViewModal,
|
handleDelete: () => setDeleteViewModal(true),
|
||||||
setCreateUpdateViewModal: setUpdateViewModal,
|
handleEdit: () => setUpdateViewModal(true),
|
||||||
handleOpenInNewTab,
|
handleOpenInNewTab,
|
||||||
handleCopyText,
|
handleCopyLink: handleCopyText,
|
||||||
isLocked: view.is_locked,
|
|
||||||
workspaceSlug,
|
workspaceSlug,
|
||||||
viewId: view.id,
|
view,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -66,7 +64,7 @@ export const WorkspaceViewQuickActions = observer(function WorkspaceViewQuickAct
|
||||||
closeOnSelect
|
closeOnSelect
|
||||||
buttonClassName="flex-shrink-0 flex items-center justify-center size-[26px] bg-custom-background-80/70 rounded"
|
buttonClassName="flex-shrink-0 flex items-center justify-center size-[26px] bg-custom-background-80/70 rounded"
|
||||||
>
|
>
|
||||||
{MENU_ITEMS.map((item) => {
|
{MENU_ITEMS.items.map((item) => {
|
||||||
if (item.shouldRender === false) return null;
|
if (item.shouldRender === false) return null;
|
||||||
return (
|
return (
|
||||||
<CustomMenu.MenuItem
|
<CustomMenu.MenuItem
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue