90 lines
3.0 KiB
TypeScript
90 lines
3.0 KiB
TypeScript
import { DefaultPermission } from "~/application/dtos/shared/DefaultPermissions";
|
|
import { ScopedCommandAction, CommandRegistryLayout } from "~/services/command-registry/commandRegistry";
|
|
import { AppLoaderData } from "~/utils/data/useAppData";
|
|
import { AppRootData } from "~/utils/data/useRootData";
|
|
import { getUserHasPermission } from "~/utils/helpers/PermissionsHelper";
|
|
|
|
export type CommandSearchInput = {
|
|
actions: ScopedCommandAction[];
|
|
query: string;
|
|
layout: CommandRegistryLayout;
|
|
tenantSlug?: string;
|
|
appData?: AppLoaderData;
|
|
rootData?: AppRootData;
|
|
};
|
|
|
|
export function filterCommandActions({ actions, query, layout, tenantSlug, appData, rootData }: CommandSearchInput): ScopedCommandAction[] {
|
|
const normalizedQuery = normalizeSearchTerm(query);
|
|
return actions.filter((action) => actionMatchesScope(action, { layout, tenantSlug, appData, rootData })).filter((action) => actionMatchesQuery(action, normalizedQuery));
|
|
}
|
|
|
|
function actionMatchesQuery(action: ScopedCommandAction, normalizedQuery: string) {
|
|
if (!normalizedQuery) {
|
|
return true;
|
|
}
|
|
return [action.id, action.name, action.subtitle, action.keywords].some((value) => normalizeSearchTerm(searchableText(value)).includes(normalizedQuery));
|
|
}
|
|
|
|
type ScopeContext = Pick<CommandSearchInput, "layout" | "tenantSlug" | "appData" | "rootData">;
|
|
|
|
function actionMatchesScope(action: ScopedCommandAction, { layout, tenantSlug, appData, rootData }: ScopeContext) {
|
|
const scope = action.scope;
|
|
if (scope.layout !== layout) {
|
|
return false;
|
|
}
|
|
if (scope.tenantSlug && (!tenantSlug || scope.tenantSlug !== tenantSlug)) {
|
|
return false;
|
|
}
|
|
if (scope.adminOnly && !isAdminContext(layout, appData, rootData)) {
|
|
return false;
|
|
}
|
|
if (scope.featureFlag && !featureFlagIsEnabled(scope.featureFlag, rootData)) {
|
|
return false;
|
|
}
|
|
if (scope.permission) {
|
|
if (!appData || !Array.isArray(appData.permissions)) {
|
|
return false;
|
|
}
|
|
return getUserHasPermission(appData, scope.permission as DefaultPermission);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function isAdminContext(layout: CommandRegistryLayout, appData?: AppLoaderData, rootData?: AppRootData) {
|
|
if (appData?.isSuperAdmin === true || userIsAdmin(appData?.user)) {
|
|
return true;
|
|
}
|
|
if (layout === "admin") {
|
|
return userIsAdmin(rootData?.user);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function userIsAdmin(user: unknown) {
|
|
return typeof user === "object" && user !== null && "admin" in user && (user as { admin?: unknown }).admin === true;
|
|
}
|
|
|
|
function featureFlagIsEnabled(featureFlag: string, rootData?: AppRootData) {
|
|
if (!rootData) {
|
|
return false;
|
|
}
|
|
if (featureFlag === "tenantApiKeys") {
|
|
return rootData.appConfiguration.app.features.tenantApiKeys === true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function searchableText(value: unknown) {
|
|
if (Array.isArray(value)) {
|
|
return value.join(" ");
|
|
}
|
|
if (typeof value === "string" || typeof value === "number") {
|
|
return String(value);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function normalizeSearchTerm(value: string) {
|
|
return value.trim().toLowerCase();
|
|
}
|