Files
EpicNext-Cms/src/features/housekeeping/domains/operations/route-handlers.ts
T

160 lines
5.0 KiB
TypeScript

import { createElement } from "react";
import { satisfiesCapability } from "../../foundation/capability-context";
import { createHousekeepingRegistry } from "../../foundation/registry";
import { HOUSEKEEPING_MANIFESTS } from "../../manifests";
import type { HousekeepingRouteHandler } from "../../route-handlers";
import {
type OperationsWorkspaceLabels,
OperationsWorkspacePage,
} from "./pages/workspace";
import { operationsWorkspaceQuery } from "./queries";
export async function renderOperationsWorkspacePage(
input: Parameters<HousekeepingRouteHandler["render"]>[0],
) {
const translate = input.translate ?? ((key: string) => key);
const message = (key: string, fallback: string) => {
const value = translate(key);
return value === key ? fallback : value;
};
const registry = createHousekeepingRegistry(HOUSEKEEPING_MANIFESTS);
const accessibleDomains = registry.domains.filter(
(domain) =>
domain.id !== "operations" &&
satisfiesCapability(input.context, domain.capability),
);
const routes = accessibleDomains.flatMap((domain) =>
domain.routes.filter((route) =>
satisfiesCapability(input.context, route.capability),
),
);
const widgetDefinitions = registry.domains
.filter((domain) => satisfiesCapability(input.context, domain.capability))
.flatMap((domain) =>
domain.widgets.filter((widget) =>
satisfiesCapability(input.context, widget.capability),
),
);
const labels: OperationsWorkspaceLabels = {
title: message("workspace.title", "Operations workspace"),
description: message(
"workspace.description",
"Inbox, recent work, favorites, and widgets for the domains you can operate.",
),
noDomainsTitle: message(
"workspace.noDomainsTitle",
"No operational domains are available",
),
noDomainsDescription: message(
"workspace.noDomainsDescription",
"No operational domain is authorized for your account.",
),
partial: message(
"workspace.partial",
"Some workspace sections are unavailable",
),
inbox: {
title: message("workspace.inbox.title", "Operational inbox"),
domainFilter: message("workspace.inbox.domainFilter", "Filter by domain"),
stateFilter: message("workspace.inbox.stateFilter", "Filter by state"),
priorityFilter: message(
"workspace.inbox.priorityFilter",
"Filter by priority",
),
all: message("workspace.inbox.all", "All"),
loading: message("workspace.inbox.loading", "Loading operational inbox"),
empty: message("workspace.inbox.empty", "No operational work is waiting"),
partial: message(
"workspace.inbox.partial",
"Some inbox sources are unavailable",
),
open: message("workspace.inbox.open", "Open"),
},
recent: {
title: message("workspace.recent.title", "Recent work"),
empty: message("workspace.recent.empty", "No recent Housekeeping work"),
},
favorites: {
title: message("workspace.favorites.title", "Favorites"),
pin: message("workspace.favorites.pin", "Pin"),
unpin: message("workspace.favorites.unpin", "Unpin"),
error: message(
"workspace.favorites.error",
"Preferences could not be saved",
),
reorderInstructions: message(
"workspace.favorites.reorderInstructions",
"Press Space, then use the arrow keys to reorder.",
),
},
widgetSettings: {
title: message("workspace.widgetSettings.title", "Widget settings"),
mandatory: message("workspace.widgetSettings.mandatory", "Mandatory"),
error: message(
"workspace.widgetSettings.error",
"Preferences could not be saved",
),
},
widgets: {
title: message("workspace.widgets.title", "Widgets"),
partial: message(
"workspace.widgets.partial",
"Some widgets are unavailable",
),
error: message(
"workspace.widgets.error",
"Widget order could not be saved",
),
reorderInstructions: message(
"workspace.widgets.reorderInstructions",
"Press Space, then use the arrow keys to reorder.",
),
},
};
const routeTitles = Object.fromEntries(
routes.map((route) => [
route.labelKey,
message(route.labelKey.slice("pages.housekeeping.".length), route.id),
]),
);
const widgetTitles = Object.fromEntries(
widgetDefinitions.map((widget) => [
widget.id,
message(`widgets.${widget.id}`, widget.id),
]),
);
const workspaceResult = await operationsWorkspaceQuery(input.context);
const inboxTitles = Object.fromEntries(
workspaceResult.model.inbox.items.map((item) => [
item.titleKey,
item.titleKey.startsWith("pages.housekeeping.")
? message(
item.titleKey.slice("pages.housekeeping.".length),
item.domain,
)
: item.titleKey,
]),
);
return createElement(OperationsWorkspacePage, {
result: workspaceResult,
routes,
widgetDefinitions,
surface: input.surface ?? "preview",
labels,
routeTitles,
widgetTitles,
inboxTitles,
...(input.savePreferences
? { savePreferences: input.savePreferences }
: {}),
});
}
export const OPERATIONS_ROUTE_HANDLERS: readonly HousekeepingRouteHandler[] =
Object.freeze([
Object.freeze({
routeId: "operations.workspace",
render: renderOperationsWorkspacePage,
}),
]);