packages/kbar/src/hooks/useRefineKbar/index.tsx
import { useEffect, useState, useContext } from "react";
import {
useNavigation,
useDelete,
useTranslate,
useResource,
IResourceItem,
useCanWithoutCache,
useUserFriendlyName,
useRouterType,
useGo,
} from "@refinedev/core";
import {
useRegisterActions,
createAction,
Action,
KBarContext,
VisualState,
} from "kbar";
import { capitalize } from "@definitions";
import { useGetToPath } from "@refinedev/core";
enum RefineKbarActionType {
List = "list",
Create = "create",
Show = "show",
Edit = "edit",
Delete = "delete",
}
export const useRefineKbar = (): void => {
const t = useTranslate();
const {
resource: resourceFromParams,
resources,
id: idFromParams,
action: actionFromParams,
} = useResource();
const routerType = useRouterType();
const getToPath = useGetToPath();
const go = useGo();
const { mutate } = useDelete();
const {
push,
list: goToList,
create: goToCreate,
show: goToShow,
edit: goToEdit,
} = useNavigation();
const getUserFriendlyName = useUserFriendlyName();
const kbarContext = useContext(KBarContext);
const { can } = useCanWithoutCache();
const [actions, setActions] = useState<Action[]>([]);
useEffect(() => {
const preaparedActions = async () => {
return await Promise.all(
moveActionToFirst().flatMap((resource) => {
return createActionWithResource(resource);
}),
);
};
preaparedActions().then((actions) => {
return setActions(actions.flatMap((action) => action));
});
}, [resources, idFromParams, resourceFromParams, actionFromParams]);
useEffect(() => {
if (actions.length === 0) {
kbarContext.query.setVisualState(VisualState.hidden);
}
}, [actions]);
const moveActionToFirst = (): IResourceItem[] => {
const orderedResources = [...resources];
const fromIndex = orderedResources?.findIndex(
(resource) =>
(resource.identifier ?? resource?.name) ===
(resourceFromParams?.identifier ?? resourceFromParams?.name),
);
if (fromIndex > 0) {
const element = orderedResources[fromIndex];
orderedResources.splice(fromIndex, 1);
orderedResources.splice(0, 0, element);
}
return orderedResources;
};
const createActionWithResource = async (resource: IResourceItem) => {
const {
name,
label: deprecatedLabel,
list,
create,
canCreate,
canEdit,
canShow,
icon: deprecatedIcon,
show,
canDelete: deprecatedCanDelete,
edit,
route,
} = resource;
const label =
resource?.meta?.label ?? resource?.options?.label ?? deprecatedLabel;
const icon =
resource?.meta?.icon ?? resource?.options?.icon ?? deprecatedIcon;
const canDelete =
resource?.meta?.canDelete ??
resource?.options?.canDelete ??
deprecatedCanDelete;
const section =
label ??
t(
`${resource.name}.${resource.name}`,
getUserFriendlyName(resource.name, "plural"),
);
const tempActions: Action[] = [];
if (
list &&
((resourceFromParams !== undefined &&
resourceFromParams?.name !== name) ||
(actionFromParams !== undefined && resourceFromParams?.name === name))
) {
const { can: canList } = (await can?.({
resource: name,
action: RefineKbarActionType.List,
params: { id: idFromParams, resource },
})) || { can: true };
if (canList) {
tempActions.push(
createAction({
name: t("actions.list", capitalize(RefineKbarActionType.List)),
section,
icon,
perform: () => {
const p = getToPath({
resource,
action: "list",
legacy: routerType === "legacy",
});
if (p) {
if (routerType === "legacy") {
push(p);
} else {
go({ to: p });
}
}
},
}),
);
}
}
if (
(canCreate || !!create) &&
create &&
(RefineKbarActionType.Create !== actionFromParams ||
resourceFromParams?.name !== name)
) {
const { can: canAccessCreate } = (await can?.({
resource: name,
action: RefineKbarActionType.Create,
params: { resource },
})) || { can: true };
if (canAccessCreate) {
tempActions.push(
createAction({
name: t("actions.create", capitalize(RefineKbarActionType.Create)),
section,
icon,
keywords: "new",
perform: () => {
const p = getToPath({
resource,
action: "create",
legacy: routerType === "legacy",
});
if (p) {
if (routerType === "legacy") {
push(p);
} else {
go({ to: p });
}
}
},
}),
);
}
}
if (resourceFromParams?.name === name && idFromParams) {
if (
(canShow || !!show) &&
show &&
RefineKbarActionType.Show !== actionFromParams
) {
const { can: canAccessShow } = (await can?.({
resource: name,
action: RefineKbarActionType.Show,
params: { id: idFromParams, resource },
})) || { can: true };
if (canAccessShow) {
tempActions.push(
createAction({
name: t("actions.show", capitalize(RefineKbarActionType.Show)),
section,
icon,
perform: () => {
const p = getToPath({
resource,
action: "show",
legacy: routerType === "legacy",
meta: {
id: idFromParams,
},
});
if (p) {
if (routerType === "legacy") {
push(p);
} else {
go({ to: p });
}
}
},
}),
);
}
}
if (
(canEdit || !!edit) &&
edit &&
RefineKbarActionType.Edit !== actionFromParams
) {
const { can: canAccessEdit } = (await can?.({
resource: name,
action: RefineKbarActionType.Edit,
params: { id: idFromParams, resource },
})) || { can: true };
if (canAccessEdit) {
tempActions.push(
createAction({
name: t("actions.edit", capitalize(RefineKbarActionType.Edit)),
section,
icon,
perform: () => {
const p = getToPath({
resource,
action: "edit",
legacy: routerType === "legacy",
meta: {
id: idFromParams,
},
});
if (p) {
if (routerType === "legacy") {
push(p);
} else {
go({ to: p });
}
}
},
}),
);
}
}
if (canDelete) {
const { can: canAccessDelete } = (await can?.({
resource: name,
action: RefineKbarActionType.Delete,
params: { id: idFromParams, resource },
})) || { can: true };
if (canAccessDelete) {
tempActions.push(
{
id: "delete",
name: t(
"actions.delete",
capitalize(RefineKbarActionType.Delete),
),
section,
icon,
},
createAction({
name: t(
"buttons.delete",
capitalize(RefineKbarActionType.Delete),
),
section: t("buttons.confirm", "Are you sure?"),
parent: "delete",
perform: () => {
mutate(
{
resource: resource.name,
id: idFromParams,
},
{
onSuccess: () => {
const p = getToPath({
resource,
action: "list",
legacy: routerType === "legacy",
});
if (p) {
if (routerType === "legacy") {
push(p);
} else {
go({ to: p });
}
}
},
},
);
},
}),
createAction({
name: t("buttons.cancel", "Cancel"),
parent: "delete",
perform: () => null,
}),
);
}
}
}
return tempActions;
};
useRegisterActions(actions, [actions]);
};