Files
EpicNext-Cms/src/features/housekeeping/domains/content/widgets.ts
T

95 lines
2.4 KiB
TypeScript

import { PERMS } from "@/lib/permission-slugs";
import { authorizeHousekeeping } from "../../foundation/authorization";
import {
anyCapability,
type CapabilityRequirement,
fail,
type HousekeepingCapabilityContext,
type HousekeepingWidgetDefinition,
ok,
} from "../../foundation/contracts";
export const CONTENT_WIDGET_IDS = [
"content.editorial-summary",
"content.media-summary",
"content.localization-summary",
] as const;
type ContentWidgetLoader = (
context: HousekeepingCapabilityContext,
signal: AbortSignal,
) => Promise<Readonly<Record<string, number>>>;
export interface ContentWidgetAdapters {
readonly editorial: ContentWidgetLoader;
readonly media: ContentWidgetLoader;
readonly localization: ContentWidgetLoader;
}
function createWidget(
id: (typeof CONTENT_WIDGET_IDS)[number],
kind: "mandatory" | "optional",
capability: CapabilityRequirement,
load: ContentWidgetLoader,
): HousekeepingWidgetDefinition {
return {
id,
owner: "content",
kind,
capability,
async load(context, signal) {
const authorization = authorizeHousekeeping(context, capability);
if (!authorization.ok) return authorization;
try {
return ok(await load(context, signal), authorization.correlationId);
} catch {
return fail(
"DEPENDENCY_UNAVAILABLE",
"errors.housekeeping.dependencyUnavailable",
authorization.correlationId,
);
}
},
};
}
export function createContentWidgets(
adapters: ContentWidgetAdapters,
): readonly HousekeepingWidgetDefinition[] {
return [
createWidget(
"content.editorial-summary",
"mandatory",
anyCapability(PERMS.NEWS_VIEW),
adapters.editorial,
),
createWidget(
"content.media-summary",
"optional",
anyCapability(PERMS.PAGES_VIEW, PERMS.BANNERS_VIEW),
adapters.media,
),
createWidget(
"content.localization-summary",
"optional",
anyCapability(PERMS.SETTINGS_VIEW),
adapters.localization,
),
];
}
export const CONTENT_WIDGETS = createContentWidgets({
async editorial(context, signal) {
const { loadContentWidget } = await import("./widgets-production");
return loadContentWidget("editorial", context, signal);
},
async media(context, signal) {
const { loadContentWidget } = await import("./widgets-production");
return loadContentWidget("media", context, signal);
},
async localization(context, signal) {
const { loadContentWidget } = await import("./widgets-production");
return loadContentWidget("localization", context, signal);
},
});