Files
EpicNext-Cms/src/features/housekeeping/domains/hotel/widgets.ts
T
Simo 2e95a106e0
CI / check (pull_request) Successful in 38s
CI / release (pull_request) Skipped
CI / deploy (pull_request) Skipped
feat(housekeeping): integrate studio operations
2026-08-30 15:29:58 +02:00

69 lines
2.0 KiB
TypeScript

import { PERMS } from "@/lib/permission-slugs";
import { authorizeHousekeeping } from "../../foundation/authorization";
import {
anyCapability,
fail,
type HousekeepingCapabilityContext,
type HousekeepingWidgetDefinition,
ok,
} from "../../foundation/contracts";
import { studioOperationRepository } from "./queries/studio";
export const HOTEL_WIDGET_IDS = ["hotel.studio.active-operations"] as const;
type HotelWidgetLoader = (
context: HousekeepingCapabilityContext,
signal: AbortSignal,
) => Promise<Readonly<Record<string, number>>>;
export interface HotelWidgetAdapters {
readonly activeOperations: HotelWidgetLoader;
}
export function createHotelWidgets(
adapters: HotelWidgetAdapters,
): readonly HousekeepingWidgetDefinition[] {
const capability = anyCapability(PERMS.ASSETS_IMPORT, PERMS.CATALOG_EDIT);
return [
{
id: "hotel.studio.active-operations",
owner: "hotel",
kind: "mandatory",
capability,
async load(context, signal) {
const authorization = authorizeHousekeeping(context, capability);
if (!authorization.ok) return authorization;
try {
if (signal.aborted) throw new Error("aborted Hotel widget");
return ok(
await adapters.activeOperations(context, signal),
authorization.correlationId,
);
} catch {
return fail(
"DEPENDENCY_UNAVAILABLE",
"errors.housekeeping.dependencyUnavailable",
authorization.correlationId,
);
}
},
},
];
}
export const HOTEL_WIDGETS = createHotelWidgets({
async activeOperations(_context, signal) {
if (signal.aborted) throw new Error("aborted Hotel operations widget");
const { items } = await studioOperationRepository.list({
pageSize: 100,
offset: 0,
});
return {
queued: items.filter((item) => item.phase === "queued").length,
running: items.filter((item) => item.phase === "running").length,
partial: items.filter((item) => item.phase === "partial").length,
failed: items.filter((item) => item.phase === "failed").length,
};
},
});