160 lines
5.0 KiB
TypeScript
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,
|
|
}),
|
|
]);
|