121 lines
3.3 KiB
TypeScript
121 lines
3.3 KiB
TypeScript
import type { GroupPermissionKey } from "@homarr/definitions";
|
|
|
|
export const universalCreateActionKeys = [
|
|
"widget",
|
|
"app",
|
|
"integration",
|
|
"container",
|
|
"board",
|
|
"workshop",
|
|
"customWidget",
|
|
] as const;
|
|
|
|
export type UniversalCreateActionKey = (typeof universalCreateActionKeys)[number];
|
|
export type UniversalCreateActionGroup = "currentBoard" | "library" | "boards";
|
|
|
|
export interface UniversalCreateContext {
|
|
hasBoardContext: boolean;
|
|
permissions: readonly GroupPermissionKey[];
|
|
}
|
|
|
|
interface UniversalCreateActionDefinition {
|
|
key: UniversalCreateActionKey;
|
|
group: UniversalCreateActionGroup;
|
|
priority: number;
|
|
isVisible: (context: UniversalCreateContext) => boolean;
|
|
}
|
|
|
|
const hasPermission = (context: UniversalCreateContext, permission: GroupPermissionKey) =>
|
|
context.permissions.includes(permission);
|
|
|
|
const definitions: readonly UniversalCreateActionDefinition[] = [
|
|
{
|
|
key: "widget",
|
|
group: "currentBoard",
|
|
priority: 100,
|
|
isVisible: ({ hasBoardContext }) => hasBoardContext,
|
|
},
|
|
{
|
|
key: "app",
|
|
group: "currentBoard",
|
|
priority: 90,
|
|
isVisible: (context) => context.hasBoardContext || hasPermission(context, "app-create"),
|
|
},
|
|
{
|
|
key: "integration",
|
|
group: "library",
|
|
priority: 80,
|
|
isVisible: (context) => hasPermission(context, "integration-create"),
|
|
},
|
|
{
|
|
key: "container",
|
|
group: "currentBoard",
|
|
priority: 70,
|
|
isVisible: ({ hasBoardContext }) => hasBoardContext,
|
|
},
|
|
{
|
|
key: "board",
|
|
group: "boards",
|
|
priority: 60,
|
|
isVisible: (context) => hasPermission(context, "board-create"),
|
|
},
|
|
{
|
|
key: "workshop",
|
|
group: "library",
|
|
priority: 40,
|
|
isVisible: (context) => hasPermission(context, "admin"),
|
|
},
|
|
{
|
|
key: "customWidget",
|
|
group: "library",
|
|
priority: 30,
|
|
isVisible: (context) => hasPermission(context, "admin"),
|
|
},
|
|
];
|
|
|
|
export interface RankedUniversalCreateAction {
|
|
key: UniversalCreateActionKey;
|
|
group: UniversalCreateActionGroup;
|
|
priority: number;
|
|
name: string;
|
|
description: string;
|
|
keywords?: readonly string[];
|
|
}
|
|
|
|
export const getUniversalCreateActionDefinitions = (context: UniversalCreateContext) =>
|
|
definitions
|
|
.filter((definition) => definition.isVisible(context))
|
|
.map((definition) =>
|
|
definition.key === "app" && !context.hasBoardContext ? { ...definition, group: "library" as const } : definition,
|
|
);
|
|
|
|
const normalize = (value: string) => value.trim().toLocaleLowerCase();
|
|
|
|
export const filterAndRankUniversalCreateActions = <TAction extends RankedUniversalCreateAction>(
|
|
actions: readonly TAction[],
|
|
query: string,
|
|
) => {
|
|
const normalizedQuery = normalize(query);
|
|
|
|
return actions
|
|
.map((action) => {
|
|
const name = normalize(action.name);
|
|
const searchableText = normalize([action.name, action.description, ...(action.keywords ?? [])].join(" "));
|
|
const relevance =
|
|
normalizedQuery.length === 0
|
|
? 0
|
|
: name === normalizedQuery
|
|
? 300
|
|
: name.startsWith(normalizedQuery)
|
|
? 200
|
|
: searchableText.includes(normalizedQuery)
|
|
? 100
|
|
: -1;
|
|
|
|
return { action, relevance };
|
|
})
|
|
.filter(({ relevance }) => relevance >= 0)
|
|
.toSorted((left, right) => right.relevance - left.relevance || right.action.priority - left.action.priority)
|
|
.map(({ action }) => action);
|
|
};
|