Initial commit: Plane
Some checks failed
Branch Build CE / Build Setup (push) Has been cancelled
Branch Build CE / Build-Push Admin Docker Image (push) Has been cancelled
Branch Build CE / Build-Push Web Docker Image (push) Has been cancelled
Branch Build CE / Build-Push Space Docker Image (push) Has been cancelled
Branch Build CE / Build-Push Live Collaboration Docker Image (push) Has been cancelled
Branch Build CE / Build-Push API Server Docker Image (push) Has been cancelled
Branch Build CE / Build-Push Proxy Docker Image (push) Has been cancelled
Branch Build CE / Build-Push AIO Docker Image (push) Has been cancelled
Branch Build CE / Upload Build Assets (push) Has been cancelled
Branch Build CE / Build Release (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Codespell / Check for spelling errors (push) Has been cancelled
Sync Repositories / sync_changes (push) Has been cancelled

Synced from upstream: 8853637e981ed7d8a6cff32bd98e7afe20f54362
This commit is contained in:
chuan
2025-11-07 00:00:52 +08:00
commit 8ebde8aa05
4886 changed files with 462270 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
import { useCallback } from "react";
import { LogOut, Mails } from "lucide-react";
// plane imports
import { useTranslation } from "@plane/i18n";
import { setToast, TOAST_TYPE } from "@plane/propel/toast";
// components
import type { TPowerKCommandConfig } from "@/components/power-k/core/types";
// hooks
import { useUser } from "@/hooks/store/user";
import { useAppRouter } from "@/hooks/use-app-router";
/**
* Account commands - Account related commands
*/
export const usePowerKAccountCommands = (): TPowerKCommandConfig[] => {
// navigation
const router = useAppRouter();
// store
const { signOut } = useUser();
// translation
const { t } = useTranslation();
const handleSignOut = useCallback(() => {
signOut().catch(() =>
setToast({
type: TOAST_TYPE.ERROR,
title: t("sign_out.toast.error.title"),
message: t("sign_out.toast.error.message"),
})
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [signOut]);
return [
{
id: "workspace_invites",
type: "action",
group: "account",
i18n_title: "power_k.account_actions.workspace_invites",
icon: Mails,
action: () => router.push("/invitations"),
isEnabled: () => true,
isVisible: () => true,
closeOnSelect: true,
},
{
id: "sign_out",
type: "action",
group: "account",
i18n_title: "power_k.account_actions.sign_out",
icon: LogOut,
action: handleSignOut,
isEnabled: () => true,
isVisible: () => true,
closeOnSelect: true,
},
];
};

View File

@@ -0,0 +1,29 @@
// local imports
import type { TPowerKCommandConfig } from "../core/types";
import { usePowerKContextBasedActions } from "../ui/pages/context-based";
import { usePowerKAccountCommands } from "./account-commands";
import { usePowerKCreationCommands } from "./creation/root";
import { usePowerKHelpCommands } from "./help-commands";
import { usePowerKMiscellaneousCommands } from "./miscellaneous-commands";
import { usePowerKNavigationCommands } from "./navigation/root";
import { usePowerKPreferencesCommands } from "./preferences-commands";
export const useProjectsAppPowerKCommands = (): TPowerKCommandConfig[] => {
const navigationCommands = usePowerKNavigationCommands();
const creationCommands = usePowerKCreationCommands();
const contextualCommands = usePowerKContextBasedActions();
const accountCommands = usePowerKAccountCommands();
const miscellaneousCommands = usePowerKMiscellaneousCommands();
const preferencesCommands = usePowerKPreferencesCommands();
const helpCommands = usePowerKHelpCommands();
return [
...navigationCommands,
...creationCommands,
...contextualCommands,
...accountCommands,
...miscellaneousCommands,
...preferencesCommands,
...helpCommands,
];
};

View File

@@ -0,0 +1,151 @@
import { FileText, FolderPlus, Layers, SquarePlus } from "lucide-react";
// plane imports
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import { ContrastIcon, DiceIcon, LayersIcon } from "@plane/propel/icons";
// components
import { EUserProjectRoles } from "@plane/types";
import type { TPowerKCommandConfig, TPowerKContext } from "@/components/power-k/core/types";
// hooks
import { useCommandPalette } from "@/hooks/store/use-command-palette";
import { useProject } from "@/hooks/store/use-project";
import { useUser } from "@/hooks/store/user";
// plane web imports
import { getIsWorkspaceCreationDisabled } from "@/plane-web/helpers/instance.helper";
export type TPowerKCreationCommandKeys =
| "create_work_item"
| "create_page"
| "create_view"
| "create_cycle"
| "create_module"
| "create_project"
| "create_workspace";
/**
* Creation commands - Create any entity in the app
*/
export const usePowerKCreationCommandsRecord = (): Record<TPowerKCreationCommandKeys, TPowerKCommandConfig> => {
// store
const {
canPerformAnyCreateAction,
permission: { allowPermissions },
} = useUser();
const { workspaceProjectIds, getPartialProjectById } = useProject();
const {
toggleCreateIssueModal,
toggleCreateProjectModal,
toggleCreateCycleModal,
toggleCreateModuleModal,
toggleCreateViewModal,
toggleCreatePageModal,
} = useCommandPalette();
// derived values
const canCreateWorkItem = canPerformAnyCreateAction && workspaceProjectIds && workspaceProjectIds.length > 0;
const canCreateProject = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.WORKSPACE
);
const hasProjectMemberLevelPermissions = (ctx: TPowerKContext) =>
allowPermissions(
[EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER],
EUserPermissionsLevel.PROJECT,
ctx.params.workspaceSlug?.toString(),
ctx.params.projectId?.toString()
);
const isWorkspaceCreationDisabled = getIsWorkspaceCreationDisabled();
const getProjectDetails = (ctx: TPowerKContext) =>
ctx.params.projectId ? getPartialProjectById(ctx.params.projectId.toString()) : undefined;
return {
create_work_item: {
id: "create_work_item",
type: "action",
group: "create",
i18n_title: "power_k.creation_actions.create_work_item",
icon: LayersIcon,
keySequence: "ni",
action: () => toggleCreateIssueModal(true),
isEnabled: () => Boolean(canCreateWorkItem),
isVisible: () => Boolean(canCreateWorkItem),
closeOnSelect: true,
},
create_page: {
id: "create_page",
type: "action",
group: "create",
i18n_title: "power_k.creation_actions.create_page",
icon: FileText,
keySequence: "nd",
action: () => toggleCreatePageModal({ isOpen: true }),
isEnabled: (ctx) => Boolean(getProjectDetails(ctx)?.page_view && hasProjectMemberLevelPermissions(ctx)),
isVisible: (ctx) =>
Boolean(ctx.params.projectId && getProjectDetails(ctx)?.page_view && hasProjectMemberLevelPermissions(ctx)),
closeOnSelect: true,
},
create_view: {
id: "create_view",
type: "action",
group: "create",
i18n_title: "power_k.creation_actions.create_view",
icon: Layers,
keySequence: "nv",
action: () => toggleCreateViewModal(true),
isEnabled: (ctx) => Boolean(getProjectDetails(ctx)?.issue_views_view && hasProjectMemberLevelPermissions(ctx)),
isVisible: (ctx) =>
Boolean(
ctx.params.projectId && getProjectDetails(ctx)?.issue_views_view && hasProjectMemberLevelPermissions(ctx)
),
closeOnSelect: true,
},
create_cycle: {
id: "create_cycle",
type: "action",
group: "create",
i18n_title: "power_k.creation_actions.create_cycle",
icon: ContrastIcon,
keySequence: "nc",
action: () => toggleCreateCycleModal(true),
isEnabled: (ctx) => Boolean(getProjectDetails(ctx)?.cycle_view && hasProjectMemberLevelPermissions(ctx)),
isVisible: (ctx) =>
Boolean(ctx.params.projectId && getProjectDetails(ctx)?.cycle_view && hasProjectMemberLevelPermissions(ctx)),
closeOnSelect: true,
},
create_module: {
id: "create_module",
type: "action",
group: "create",
i18n_title: "power_k.creation_actions.create_module",
icon: DiceIcon,
keySequence: "nm",
action: () => toggleCreateModuleModal(true),
isEnabled: (ctx) => Boolean(getProjectDetails(ctx)?.module_view && hasProjectMemberLevelPermissions(ctx)),
isVisible: (ctx) =>
Boolean(ctx.params.projectId && getProjectDetails(ctx)?.module_view && hasProjectMemberLevelPermissions(ctx)),
closeOnSelect: true,
},
create_project: {
id: "create_project",
type: "action",
group: "create",
i18n_title: "power_k.creation_actions.create_project",
icon: FolderPlus,
keySequence: "np",
action: () => toggleCreateProjectModal(true),
isEnabled: () => Boolean(canCreateProject),
isVisible: () => Boolean(canCreateProject),
closeOnSelect: true,
},
create_workspace: {
id: "create_workspace",
type: "action",
group: "create",
i18n_title: "power_k.creation_actions.create_workspace",
icon: SquarePlus,
action: (ctx) => ctx.router.push("/create-workspace"),
isEnabled: () => Boolean(!isWorkspaceCreationDisabled),
isVisible: () => Boolean(!isWorkspaceCreationDisabled),
closeOnSelect: true,
},
};
};

View File

@@ -0,0 +1,18 @@
// types
import type { TPowerKCommandConfig } from "@/components/power-k/core/types";
// local imports
import { usePowerKCreationCommandsRecord } from "./command";
import type { TPowerKCreationCommandKeys } from "./command";
export const usePowerKCreationCommands = (): TPowerKCommandConfig[] => {
const optionsList: Record<TPowerKCreationCommandKeys, TPowerKCommandConfig> = usePowerKCreationCommandsRecord();
return [
optionsList["create_work_item"],
optionsList["create_page"],
optionsList["create_view"],
optionsList["create_cycle"],
optionsList["create_module"],
optionsList["create_project"],
optionsList["create_workspace"],
];
};

View File

@@ -0,0 +1,82 @@
import { FileText, GithubIcon, MessageSquare, Rocket } from "lucide-react";
// plane imports
import { DiscordIcon } from "@plane/propel/icons";
// components
import type { TPowerKCommandConfig } from "@/components/power-k/core/types";
// hooks
import { usePowerK } from "@/hooks/store/use-power-k";
import { useTransient } from "@/hooks/store/use-transient";
/**
* Help commands - Help related commands
*/
export const usePowerKHelpCommands = (): TPowerKCommandConfig[] => {
// store
const { toggleShortcutsListModal } = usePowerK();
const { toggleIntercom } = useTransient();
return [
{
id: "open_keyboard_shortcuts",
type: "action",
group: "help",
i18n_title: "power_k.help_actions.open_keyboard_shortcuts",
icon: Rocket,
modifierShortcut: "cmd+/",
action: () => toggleShortcutsListModal(true),
isEnabled: () => true,
isVisible: () => true,
closeOnSelect: true,
},
{
id: "open_plane_documentation",
type: "action",
group: "help",
i18n_title: "power_k.help_actions.open_plane_documentation",
icon: FileText,
action: () => {
window.open("https://docs.plane.so/", "_blank", "noopener,noreferrer");
},
isEnabled: () => true,
isVisible: () => true,
closeOnSelect: true,
},
{
id: "join_discord",
type: "action",
group: "help",
i18n_title: "power_k.help_actions.join_discord",
icon: DiscordIcon,
action: () => {
window.open("https://discord.com/invite/A92xrEGCge", "_blank", "noopener,noreferrer");
},
isEnabled: () => true,
isVisible: () => true,
closeOnSelect: true,
},
{
id: "report_bug",
type: "action",
group: "help",
i18n_title: "power_k.help_actions.report_bug",
icon: GithubIcon,
action: () => {
window.open("https://github.com/makeplane/plane/issues/new/choose", "_blank", "noopener,noreferrer");
},
isEnabled: () => true,
isVisible: () => true,
closeOnSelect: true,
},
{
id: "chat_with_us",
type: "action",
group: "help",
i18n_title: "power_k.help_actions.chat_with_us",
icon: MessageSquare,
action: () => toggleIntercom(true),
isEnabled: () => true,
isVisible: () => true,
closeOnSelect: true,
},
];
};

View File

@@ -0,0 +1,62 @@
import { useCallback } from "react";
import { Link, PanelLeft } from "lucide-react";
// plane imports
import { useTranslation } from "@plane/i18n";
import { setToast, TOAST_TYPE } from "@plane/propel/toast";
import { copyTextToClipboard } from "@plane/utils";
// components
import type { TPowerKCommandConfig } from "@/components/power-k/core/types";
// hooks
import { useAppTheme } from "@/hooks/store/use-app-theme";
export const usePowerKMiscellaneousCommands = (): TPowerKCommandConfig[] => {
// store hooks
const { toggleSidebar } = useAppTheme();
// translation
const { t } = useTranslation();
const copyCurrentPageUrlToClipboard = useCallback(() => {
const url = new URL(window.location.href);
copyTextToClipboard(url.href)
.then(() => {
setToast({
type: TOAST_TYPE.SUCCESS,
title: t("power_k.miscellaneous_actions.copy_current_page_url_toast_success"),
});
})
.catch(() => {
setToast({
type: TOAST_TYPE.ERROR,
title: t("power_k.miscellaneous_actions.copy_current_page_url_toast_error"),
});
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return [
{
id: "toggle_app_sidebar",
group: "miscellaneous",
type: "action",
i18n_title: "power_k.miscellaneous_actions.toggle_app_sidebar",
icon: PanelLeft,
action: () => toggleSidebar(),
modifierShortcut: "cmd+b",
isEnabled: () => true,
isVisible: () => true,
closeOnSelect: true,
},
{
id: "copy_current_page_url",
group: "miscellaneous",
type: "action",
i18n_title: "power_k.miscellaneous_actions.copy_current_page_url",
icon: Link,
action: copyCurrentPageUrlToClipboard,
modifierShortcut: "cmd+shift+c",
isEnabled: () => true,
isVisible: () => true,
closeOnSelect: true,
},
];
};

View File

@@ -0,0 +1,519 @@
import { BarChart2, Briefcase, FileText, Home, Inbox, Layers, PenSquare, Settings } from "lucide-react";
// plane imports
import { EUserPermissionsLevel } from "@plane/constants";
import { ArchiveIcon, UserActivityIcon, LayersIcon, ContrastIcon, DiceIcon, Intake } from "@plane/propel/icons";
import type { ICycle, IModule, IPartialProject, IProjectView, IWorkspace } from "@plane/types";
import { EUserProjectRoles, EUserWorkspaceRoles } from "@plane/types";
// components
import type { TPowerKCommandConfig, TPowerKContext } from "@/components/power-k/core/types";
import { handlePowerKNavigate } from "@/components/power-k/utils/navigation";
// hooks
import { useProject } from "@/hooks/store/use-project";
import { useUser } from "@/hooks/store/user";
export type TPowerKNavigationCommandKeys =
| "open_workspace"
| "nav_home"
| "nav_inbox"
| "nav_your_work"
| "nav_account_settings"
| "open_project"
| "nav_projects_list"
| "nav_all_workspace_work_items"
| "nav_assigned_workspace_work_items"
| "nav_created_workspace_work_items"
| "nav_subscribed_workspace_work_items"
| "nav_workspace_analytics"
| "nav_workspace_drafts"
| "nav_workspace_archives"
| "open_workspace_setting"
| "nav_workspace_settings"
| "nav_project_work_items"
| "open_project_cycle"
| "nav_project_cycles"
| "open_project_module"
| "nav_project_modules"
| "open_project_view"
| "nav_project_views"
| "nav_project_pages"
| "nav_project_intake"
| "nav_project_archives"
| "open_project_setting"
| "nav_project_settings";
/**
* Navigation commands - Navigate to all pages in the app
*/
export const usePowerKNavigationCommandsRecord = (): Record<TPowerKNavigationCommandKeys, TPowerKCommandConfig> => {
// store hooks
const {
data: currentUser,
permission: { allowPermissions },
} = useUser();
const { getPartialProjectById } = useProject();
// derived values
const hasWorkspaceMemberLevelPermissions = (ctx: TPowerKContext) =>
allowPermissions(
[EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER],
EUserPermissionsLevel.WORKSPACE,
ctx.params.workspaceSlug?.toString()
);
const hasProjectMemberLevelPermissions = (ctx: TPowerKContext) =>
allowPermissions(
[EUserProjectRoles.ADMIN, EUserProjectRoles.MEMBER],
EUserPermissionsLevel.PROJECT,
ctx.params.workspaceSlug?.toString(),
ctx.params.projectId?.toString()
);
const baseWorkspaceConditions = (ctx: TPowerKContext) => Boolean(ctx.params.workspaceSlug?.toString());
const baseProjectConditions = (ctx: TPowerKContext) =>
Boolean(ctx.params.workspaceSlug?.toString() && ctx.params.projectId?.toString());
const getContextProject = (ctx: TPowerKContext) => getPartialProjectById(ctx.params.projectId?.toString());
return {
open_workspace: {
id: "open_workspace",
type: "change-page",
group: "navigation",
i18n_title: "power_k.navigation_actions.open_workspace",
icon: Briefcase,
keySequence: "ow",
page: "open-workspace",
onSelect: (data, ctx) => {
const workspaceDetails = data as IWorkspace;
handlePowerKNavigate(ctx, [workspaceDetails.slug]);
},
isEnabled: (ctx) => baseWorkspaceConditions(ctx),
isVisible: (ctx) => baseWorkspaceConditions(ctx),
closeOnSelect: true,
},
nav_home: {
id: "nav_home",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_home",
icon: Home,
keySequence: "gh",
action: (ctx) => handlePowerKNavigate(ctx, [ctx.params.workspaceSlug?.toString()]),
isEnabled: (ctx) => baseWorkspaceConditions(ctx),
isVisible: (ctx) => baseWorkspaceConditions(ctx),
closeOnSelect: true,
},
nav_inbox: {
id: "nav_inbox",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_inbox",
icon: Inbox,
keySequence: "gx",
action: (ctx) => handlePowerKNavigate(ctx, [ctx.params.workspaceSlug?.toString(), "notifications"]),
isEnabled: (ctx) => baseWorkspaceConditions(ctx),
isVisible: (ctx) => baseWorkspaceConditions(ctx),
closeOnSelect: true,
},
nav_your_work: {
id: "nav_your_work",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_your_work",
icon: UserActivityIcon,
keySequence: "gy",
action: (ctx) => handlePowerKNavigate(ctx, [ctx.params.workspaceSlug?.toString(), "profile", currentUser?.id]),
isEnabled: (ctx) => baseWorkspaceConditions(ctx) && hasWorkspaceMemberLevelPermissions(ctx),
isVisible: (ctx) => baseWorkspaceConditions(ctx) && hasWorkspaceMemberLevelPermissions(ctx),
closeOnSelect: true,
},
nav_account_settings: {
id: "nav_account_settings",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_account_settings",
icon: Settings,
action: (ctx) => handlePowerKNavigate(ctx, [ctx.params.workspaceSlug?.toString(), "settings", "account"]),
isEnabled: (ctx) => baseWorkspaceConditions(ctx),
isVisible: (ctx) => baseWorkspaceConditions(ctx),
closeOnSelect: true,
},
open_project: {
id: "open_project",
type: "change-page",
group: "navigation",
i18n_title: "power_k.navigation_actions.open_project",
icon: Briefcase,
keySequence: "op",
page: "open-project",
onSelect: (data, ctx) => {
const projectDetails = data as IPartialProject;
handlePowerKNavigate(ctx, [ctx.params.workspaceSlug?.toString(), "projects", projectDetails.id, "issues"]);
},
isEnabled: (ctx) => baseWorkspaceConditions(ctx),
isVisible: (ctx) => baseWorkspaceConditions(ctx),
closeOnSelect: true,
},
nav_projects_list: {
id: "nav_projects_list",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_projects_list",
icon: Briefcase,
keySequence: "gp",
action: (ctx) => handlePowerKNavigate(ctx, [ctx.params.workspaceSlug?.toString(), "projects"]),
isEnabled: (ctx) => baseWorkspaceConditions(ctx),
isVisible: (ctx) => baseWorkspaceConditions(ctx),
closeOnSelect: true,
},
nav_all_workspace_work_items: {
id: "nav_all_workspace_work_items",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_all_workspace_work_items",
icon: Layers,
action: (ctx) =>
handlePowerKNavigate(ctx, [ctx.params.workspaceSlug?.toString(), "workspace-views", "all-issues"]),
isEnabled: (ctx) => baseWorkspaceConditions(ctx),
isVisible: (ctx) => baseWorkspaceConditions(ctx),
closeOnSelect: true,
},
nav_assigned_workspace_work_items: {
id: "nav_assigned_workspace_work_items",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_assigned_workspace_work_items",
icon: Layers,
action: (ctx) => handlePowerKNavigate(ctx, [ctx.params.workspaceSlug?.toString(), "workspace-views", "assigned"]),
isEnabled: (ctx) => baseWorkspaceConditions(ctx),
isVisible: (ctx) => baseWorkspaceConditions(ctx),
closeOnSelect: true,
},
nav_created_workspace_work_items: {
id: "nav_created_workspace_work_items",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_created_workspace_work_items",
icon: Layers,
action: (ctx) => handlePowerKNavigate(ctx, [ctx.params.workspaceSlug?.toString(), "workspace-views", "created"]),
isEnabled: (ctx) => baseWorkspaceConditions(ctx),
isVisible: (ctx) => baseWorkspaceConditions(ctx),
closeOnSelect: true,
},
nav_subscribed_workspace_work_items: {
id: "nav_subscribed_workspace_work_items",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_subscribed_workspace_work_items",
icon: Layers,
action: (ctx) =>
handlePowerKNavigate(ctx, [ctx.params.workspaceSlug?.toString(), "workspace-views", "subscribed"]),
isEnabled: (ctx) => baseWorkspaceConditions(ctx),
isVisible: (ctx) => baseWorkspaceConditions(ctx),
closeOnSelect: true,
},
nav_workspace_analytics: {
id: "nav_workspace_analytics",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_workspace_analytics",
icon: BarChart2,
keySequence: "ga",
action: (ctx) => handlePowerKNavigate(ctx, [ctx.params.workspaceSlug?.toString(), "analytics", "overview"]),
isEnabled: (ctx) => baseWorkspaceConditions(ctx) && hasWorkspaceMemberLevelPermissions(ctx),
isVisible: (ctx) => baseWorkspaceConditions(ctx) && hasWorkspaceMemberLevelPermissions(ctx),
closeOnSelect: true,
},
nav_workspace_drafts: {
id: "nav_workspace_drafts",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_workspace_drafts",
icon: PenSquare,
keySequence: "gj",
action: (ctx) => handlePowerKNavigate(ctx, [ctx.params.workspaceSlug?.toString(), "drafts"]),
isEnabled: (ctx) => baseWorkspaceConditions(ctx) && hasWorkspaceMemberLevelPermissions(ctx),
isVisible: (ctx) => baseWorkspaceConditions(ctx) && hasWorkspaceMemberLevelPermissions(ctx),
closeOnSelect: true,
},
nav_workspace_archives: {
id: "nav_workspace_archives",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_workspace_archives",
icon: ArchiveIcon,
keySequence: "gr",
action: (ctx) => handlePowerKNavigate(ctx, [ctx.params.workspaceSlug?.toString(), "projects", "archives"]),
isEnabled: (ctx) =>
baseWorkspaceConditions(ctx) && !baseProjectConditions(ctx) && hasWorkspaceMemberLevelPermissions(ctx),
isVisible: (ctx) =>
baseWorkspaceConditions(ctx) && !baseProjectConditions(ctx) && hasWorkspaceMemberLevelPermissions(ctx),
closeOnSelect: true,
},
open_workspace_setting: {
id: "open_workspace_setting",
type: "change-page",
group: "navigation",
i18n_title: "power_k.navigation_actions.open_workspace_setting",
icon: Settings,
keySequence: "os",
page: "open-workspace-setting",
onSelect: (data, ctx) => {
const settingsHref = data as string;
handlePowerKNavigate(ctx, [ctx.params.workspaceSlug?.toString(), settingsHref]);
},
isEnabled: (ctx) => baseWorkspaceConditions(ctx) && !baseProjectConditions(ctx),
isVisible: (ctx) => baseWorkspaceConditions(ctx) && !baseProjectConditions(ctx),
closeOnSelect: true,
},
nav_workspace_settings: {
id: "nav_workspace_settings",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_workspace_settings",
icon: Settings,
keySequence: "gs",
action: (ctx) => handlePowerKNavigate(ctx, [ctx.params.workspaceSlug?.toString(), "settings"]),
isEnabled: (ctx) => baseWorkspaceConditions(ctx) && !baseProjectConditions(ctx),
isVisible: (ctx) => baseWorkspaceConditions(ctx) && !baseProjectConditions(ctx),
closeOnSelect: true,
},
nav_project_work_items: {
id: "nav_project_work_items",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_project_work_items",
icon: LayersIcon,
keySequence: "gi",
action: (ctx) =>
handlePowerKNavigate(ctx, [
ctx.params.workspaceSlug?.toString(),
"projects",
ctx.params.projectId?.toString(),
"issues",
]),
isEnabled: (ctx) => baseProjectConditions(ctx),
isVisible: (ctx) => baseProjectConditions(ctx),
closeOnSelect: true,
},
open_project_cycle: {
id: "open_project_cycle",
type: "change-page",
group: "navigation",
i18n_title: "power_k.navigation_actions.open_project_cycle",
icon: ContrastIcon,
keySequence: "oc",
page: "open-project-cycle",
onSelect: (data, ctx) => {
const cycleDetails = data as ICycle;
handlePowerKNavigate(ctx, [
ctx.params.workspaceSlug?.toString(),
"projects",
ctx.params.projectId?.toString(),
"cycles",
cycleDetails.id,
]);
},
isEnabled: (ctx) =>
baseProjectConditions(ctx) && hasProjectMemberLevelPermissions(ctx) && !!getContextProject(ctx)?.cycle_view,
isVisible: (ctx) =>
baseProjectConditions(ctx) && hasProjectMemberLevelPermissions(ctx) && !!getContextProject(ctx)?.cycle_view,
closeOnSelect: true,
},
nav_project_cycles: {
id: "nav_project_cycles",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_project_cycles",
icon: ContrastIcon,
keySequence: "gc",
action: (ctx) =>
handlePowerKNavigate(ctx, [
ctx.params.workspaceSlug?.toString(),
"projects",
ctx.params.projectId?.toString(),
"cycles",
]),
isEnabled: (ctx) =>
baseProjectConditions(ctx) && hasProjectMemberLevelPermissions(ctx) && !!getContextProject(ctx)?.cycle_view,
isVisible: (ctx) =>
baseProjectConditions(ctx) && hasProjectMemberLevelPermissions(ctx) && !!getContextProject(ctx)?.cycle_view,
closeOnSelect: true,
},
open_project_module: {
id: "open_project_module",
type: "change-page",
group: "navigation",
i18n_title: "power_k.navigation_actions.open_project_module",
icon: DiceIcon,
keySequence: "om",
page: "open-project-module",
onSelect: (data, ctx) => {
const moduleDetails = data as IModule;
handlePowerKNavigate(ctx, [
ctx.params.workspaceSlug?.toString(),
"projects",
ctx.params.projectId?.toString(),
"modules",
moduleDetails.id,
]);
},
isEnabled: (ctx) =>
baseProjectConditions(ctx) && hasProjectMemberLevelPermissions(ctx) && !!getContextProject(ctx)?.module_view,
isVisible: (ctx) =>
baseProjectConditions(ctx) && hasProjectMemberLevelPermissions(ctx) && !!getContextProject(ctx)?.module_view,
closeOnSelect: true,
},
nav_project_modules: {
id: "nav_project_modules",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_project_modules",
icon: DiceIcon,
keySequence: "gm",
action: (ctx) =>
handlePowerKNavigate(ctx, [
ctx.params.workspaceSlug?.toString(),
"projects",
ctx.params.projectId?.toString(),
"modules",
]),
isEnabled: (ctx) =>
baseProjectConditions(ctx) && hasProjectMemberLevelPermissions(ctx) && !!getContextProject(ctx)?.module_view,
isVisible: (ctx) =>
baseProjectConditions(ctx) && hasProjectMemberLevelPermissions(ctx) && !!getContextProject(ctx)?.module_view,
closeOnSelect: true,
},
open_project_view: {
id: "open_project_view",
type: "change-page",
group: "navigation",
i18n_title: "power_k.navigation_actions.open_project_view",
icon: Layers,
keySequence: "ov",
page: "open-project-view",
onSelect: (data, ctx) => {
const viewDetails = data as IProjectView;
handlePowerKNavigate(ctx, [
ctx.params.workspaceSlug?.toString(),
"projects",
ctx.params.projectId?.toString(),
"views",
viewDetails.id,
]);
},
isEnabled: (ctx) => baseProjectConditions(ctx) && !!getContextProject(ctx)?.issue_views_view,
isVisible: (ctx) => baseProjectConditions(ctx) && !!getContextProject(ctx)?.issue_views_view,
closeOnSelect: true,
},
nav_project_views: {
id: "nav_project_views",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_project_views",
icon: Layers,
keySequence: "gv",
action: (ctx) =>
handlePowerKNavigate(ctx, [
ctx.params.workspaceSlug?.toString(),
"projects",
ctx.params.projectId?.toString(),
"views",
]),
isEnabled: (ctx) => baseProjectConditions(ctx) && !!getContextProject(ctx)?.issue_views_view,
isVisible: (ctx) => baseProjectConditions(ctx) && !!getContextProject(ctx)?.issue_views_view,
closeOnSelect: true,
},
nav_project_pages: {
id: "nav_project_pages",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_project_pages",
icon: FileText,
keySequence: "gd",
action: (ctx) =>
handlePowerKNavigate(ctx, [
ctx.params.workspaceSlug?.toString(),
"projects",
ctx.params.projectId?.toString(),
"pages",
]),
isEnabled: (ctx) => baseProjectConditions(ctx) && !!getContextProject(ctx)?.page_view,
isVisible: (ctx) => baseProjectConditions(ctx) && !!getContextProject(ctx)?.page_view,
closeOnSelect: true,
},
nav_project_intake: {
id: "nav_project_intake",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_project_intake",
icon: Intake,
keySequence: "gk",
action: (ctx) =>
handlePowerKNavigate(ctx, [
ctx.params.workspaceSlug?.toString(),
"projects",
ctx.params.projectId?.toString(),
"intake",
]),
isEnabled: (ctx) => baseProjectConditions(ctx) && !!getContextProject(ctx)?.inbox_view,
isVisible: (ctx) => baseProjectConditions(ctx) && !!getContextProject(ctx)?.inbox_view,
closeOnSelect: true,
},
nav_project_archives: {
id: "nav_project_archives",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_project_archives",
icon: ArchiveIcon,
keySequence: "gr",
action: (ctx) =>
handlePowerKNavigate(ctx, [
ctx.params.workspaceSlug?.toString(),
"projects",
ctx.params.projectId?.toString(),
"archives",
"issues",
]),
isEnabled: (ctx) => baseProjectConditions(ctx) && hasProjectMemberLevelPermissions(ctx),
isVisible: (ctx) => baseProjectConditions(ctx) && hasProjectMemberLevelPermissions(ctx),
closeOnSelect: true,
},
open_project_setting: {
id: "open_project_setting",
type: "change-page",
group: "navigation",
i18n_title: "power_k.navigation_actions.open_project_setting",
icon: Settings,
keySequence: "os",
page: "open-project-setting",
onSelect: (data, ctx) => {
const settingsHref = data as string;
handlePowerKNavigate(ctx, [
ctx.params.workspaceSlug?.toString(),
"settings",
"projects",
ctx.params.projectId?.toString(),
settingsHref,
]);
},
isEnabled: (ctx) => baseProjectConditions(ctx),
isVisible: (ctx) => baseProjectConditions(ctx),
closeOnSelect: true,
},
nav_project_settings: {
id: "nav_project_settings",
type: "action",
group: "navigation",
i18n_title: "power_k.navigation_actions.nav_project_settings",
icon: Settings,
keySequence: "gs",
action: (ctx) =>
handlePowerKNavigate(ctx, [
ctx.params.workspaceSlug?.toString(),
"settings",
"projects",
ctx.params.projectId?.toString(),
]),
isEnabled: (ctx) => baseProjectConditions(ctx),
isVisible: (ctx) => baseProjectConditions(ctx),
closeOnSelect: true,
},
};
};

View File

@@ -0,0 +1,45 @@
// components
import type { TPowerKCommandConfig } from "@/components/power-k/core/types";
// local imports
import type { TPowerKNavigationCommandKeys } from "./commands";
import { usePowerKNavigationCommandsRecord } from "./commands";
export const usePowerKNavigationCommands = (): TPowerKCommandConfig[] => {
const optionsList: Record<TPowerKNavigationCommandKeys, TPowerKCommandConfig> = usePowerKNavigationCommandsRecord();
return [
// Open actions from lowest to highest scope
optionsList["open_project_cycle"],
optionsList["open_project_module"],
optionsList["open_project_view"],
optionsList["open_project_setting"],
optionsList["open_project"],
optionsList["open_workspace_setting"],
optionsList["open_workspace"],
// User-Level Navigation
optionsList["nav_home"],
optionsList["nav_inbox"],
optionsList["nav_your_work"],
// Project-Level Navigation (Only visible in project context)
optionsList["nav_project_work_items"],
optionsList["nav_project_pages"],
optionsList["nav_project_cycles"],
optionsList["nav_project_modules"],
optionsList["nav_project_views"],
optionsList["nav_project_intake"],
optionsList["nav_project_settings"],
optionsList["nav_project_archives"],
// Navigate to workspace-level pages
optionsList["nav_all_workspace_work_items"],
optionsList["nav_assigned_workspace_work_items"],
optionsList["nav_created_workspace_work_items"],
optionsList["nav_subscribed_workspace_work_items"],
optionsList["nav_workspace_analytics"],
optionsList["nav_workspace_settings"],
optionsList["nav_workspace_drafts"],
optionsList["nav_workspace_archives"],
optionsList["nav_projects_list"],
// Account-Level Navigation
optionsList["nav_account_settings"],
];
};

View File

@@ -0,0 +1,153 @@
import { useCallback } from "react";
import { useTheme } from "next-themes";
import { Calendar, Earth, Languages, Palette } from "lucide-react";
// plane imports
import { useTranslation } from "@plane/i18n";
import { setToast, TOAST_TYPE } from "@plane/propel/toast";
import type { EStartOfTheWeek, TUserProfile } from "@plane/types";
// components
import type { TPowerKCommandConfig } from "@/components/power-k/core/types";
// hooks
import { useUser, useUserProfile } from "@/hooks/store/user";
/**
* Preferences commands - Preferences related commands
*/
export const usePowerKPreferencesCommands = (): TPowerKCommandConfig[] => {
// store hooks
const { setTheme } = useTheme();
const { updateCurrentUser } = useUser();
const { updateUserProfile, updateUserTheme } = useUserProfile();
// translation
const { t } = useTranslation();
const handleUpdateTheme = useCallback(
async (newTheme: string) => {
setTheme(newTheme);
return updateUserTheme({ theme: newTheme })
.then(() => {
setToast({
type: TOAST_TYPE.SUCCESS,
title: t("toast.success"),
message: t("power_k.preferences_actions.toast.theme.success"),
});
})
.catch(() => {
setToast({
type: TOAST_TYPE.ERROR,
title: t("toast.error"),
message: t("power_k.preferences_actions.toast.theme.error"),
});
});
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[setTheme, updateUserTheme]
);
const handleUpdateTimezone = useCallback(
(value: string) => {
updateCurrentUser({ user_timezone: value })
.then(() => {
setToast({
type: TOAST_TYPE.SUCCESS,
title: t("toast.success"),
message: t("power_k.preferences_actions.toast.timezone.success"),
});
})
.catch(() => {
setToast({
type: TOAST_TYPE.ERROR,
title: t("toast.error"),
message: t("power_k.preferences_actions.toast.timezone.error"),
});
});
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[updateCurrentUser]
);
const handleUpdateUserProfile = useCallback(
(payload: Partial<TUserProfile>) => {
updateUserProfile(payload)
.then(() => {
setToast({
type: TOAST_TYPE.SUCCESS,
title: t("toast.success"),
message: t("power_k.preferences_actions.toast.generic.success"),
});
})
.catch(() => {
setToast({
type: TOAST_TYPE.ERROR,
title: t("toast.error"),
message: t("power_k.preferences_actions.toast.generic.error"),
});
});
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[updateUserProfile]
);
return [
{
id: "update_interface_theme",
group: "preferences",
type: "change-page",
page: "update-theme",
i18n_title: "power_k.preferences_actions.update_theme",
icon: Palette,
onSelect: (data) => {
const theme = data as string;
handleUpdateTheme(theme);
},
isEnabled: () => true,
isVisible: () => true,
closeOnSelect: true,
},
{
id: "update_timezone",
group: "preferences",
page: "update-timezone",
type: "change-page",
i18n_title: "power_k.preferences_actions.update_timezone",
icon: Earth,
onSelect: (data) => {
const timezone = data as string;
handleUpdateTimezone(timezone);
},
isEnabled: () => true,
isVisible: () => true,
closeOnSelect: true,
},
{
id: "update_start_of_week",
group: "preferences",
page: "update-start-of-week",
type: "change-page",
i18n_title: "power_k.preferences_actions.update_start_of_week",
icon: Calendar,
onSelect: (data) => {
const startOfWeek = data as EStartOfTheWeek;
handleUpdateUserProfile({ start_of_the_week: startOfWeek });
},
isEnabled: () => true,
isVisible: () => true,
closeOnSelect: true,
},
{
id: "update_interface_language",
group: "preferences",
page: "update-language",
type: "change-page",
i18n_title: "power_k.preferences_actions.update_language",
icon: Languages,
onSelect: (data) => {
const language = data as string;
handleUpdateUserProfile({ language });
},
isEnabled: () => true,
isVisible: () => true,
closeOnSelect: true,
},
];
};