254 lines
6.4 KiB
TypeScript
254 lines
6.4 KiB
TypeScript
/**
|
|
* Template utility functions.
|
|
*
|
|
* Pure functions for parsing and formatting template data.
|
|
* These functions have no React dependencies and can be used
|
|
* in both TUI and CLI contexts.
|
|
*/
|
|
|
|
import type { XOTemplate, XOTemplateAction } from "@xo-cash/types";
|
|
|
|
/**
|
|
* Formatted template list item data.
|
|
*/
|
|
export interface FormattedTemplateItem {
|
|
/** The display label for the template */
|
|
label: string;
|
|
/** The template description */
|
|
description?: string;
|
|
/** Whether the template data is valid */
|
|
isValid: boolean;
|
|
}
|
|
|
|
/**
|
|
* Formatted action list item data.
|
|
*/
|
|
export interface FormattedActionItem {
|
|
/** The display label for the action */
|
|
label: string;
|
|
/** The action description */
|
|
description?: string;
|
|
/** Number of roles that can start this action */
|
|
roleCount: number;
|
|
/** Whether the action data is valid */
|
|
isValid: boolean;
|
|
}
|
|
|
|
/**
|
|
* A unique starting action (deduplicated by action identifier).
|
|
* Multiple roles that can start the same action are counted
|
|
* but not shown as separate entries.
|
|
*/
|
|
export interface UniqueStartingAction {
|
|
actionIdentifier: string;
|
|
name: string;
|
|
description?: string;
|
|
roleCount: number;
|
|
}
|
|
|
|
/**
|
|
* Role information from a template.
|
|
*/
|
|
export interface TemplateRole {
|
|
roleId: string;
|
|
name: string;
|
|
description?: string;
|
|
}
|
|
|
|
/**
|
|
* Format a template for display in a list.
|
|
*
|
|
* @param template - The template to format
|
|
* @param index - Optional index for numbered display
|
|
* @returns Formatted item data for display
|
|
*/
|
|
export function formatTemplateListItem(
|
|
template: XOTemplate | null | undefined,
|
|
index?: number,
|
|
): FormattedTemplateItem {
|
|
if (!template) {
|
|
return {
|
|
label: "",
|
|
description: undefined,
|
|
isValid: false,
|
|
};
|
|
}
|
|
|
|
const name = template.name || "Unnamed Template";
|
|
const prefix = index !== undefined ? `${index + 1}. ` : "";
|
|
|
|
return {
|
|
label: `${prefix}${name}`,
|
|
description: template.description,
|
|
isValid: true,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Format an action for display in a list.
|
|
*
|
|
* @param actionId - The action identifier
|
|
* @param action - The action definition from the template
|
|
* @param roleCount - Number of roles that can start this action
|
|
* @param index - Optional index for numbered display
|
|
* @returns Formatted item data for display
|
|
*/
|
|
export function formatActionListItem(
|
|
actionId: string,
|
|
action: XOTemplateAction | null | undefined,
|
|
roleCount: number = 1,
|
|
index?: number,
|
|
): FormattedActionItem {
|
|
if (!actionId) {
|
|
return {
|
|
label: "",
|
|
description: undefined,
|
|
roleCount: 0,
|
|
isValid: false,
|
|
};
|
|
}
|
|
|
|
const name = action?.name || actionId;
|
|
const prefix = index !== undefined ? `${index + 1}. ` : "";
|
|
const roleSuffix = roleCount > 1 ? ` (${roleCount} roles)` : "";
|
|
|
|
return {
|
|
label: `${prefix}${name}${roleSuffix}`,
|
|
description: action?.description,
|
|
roleCount,
|
|
isValid: true,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Deduplicate starting actions from a template.
|
|
* Multiple roles that can start the same action are counted
|
|
* but returned as a single entry.
|
|
*
|
|
* @param template - The template to process
|
|
* @param startingActions - Array of { action, role } pairs
|
|
* @returns Array of unique starting actions with role counts
|
|
*/
|
|
export function deduplicateStartingActions(
|
|
template: XOTemplate,
|
|
startingActions: Array<{ action: string; role: string }>,
|
|
): UniqueStartingAction[] {
|
|
const actionMap = new Map<string, UniqueStartingAction>();
|
|
|
|
for (const sa of startingActions) {
|
|
if (actionMap.has(sa.action)) {
|
|
actionMap.get(sa.action)!.roleCount++;
|
|
} else {
|
|
const actionDef = template.actions?.[sa.action];
|
|
actionMap.set(sa.action, {
|
|
actionIdentifier: sa.action,
|
|
name: actionDef?.name || sa.action,
|
|
description: actionDef?.description,
|
|
roleCount: 1,
|
|
});
|
|
}
|
|
}
|
|
|
|
return Array.from(actionMap.values());
|
|
}
|
|
|
|
/**
|
|
* Get all roles from a template.
|
|
*
|
|
* @param template - The template to process
|
|
* @returns Array of role information
|
|
*/
|
|
export function getTemplateRoles(template: XOTemplate): TemplateRole[] {
|
|
if (!template.roles) return [];
|
|
|
|
return Object.entries(template.roles).map(([roleId, role]) => {
|
|
// Handle case where role might be a string instead of object
|
|
const roleObj = typeof role === "object" ? role : null;
|
|
return {
|
|
roleId,
|
|
name: roleObj?.name || roleId,
|
|
description: roleObj?.description,
|
|
};
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get roles that can start a specific action.
|
|
*
|
|
* @param template - The template to check
|
|
* @param actionIdentifier - The action to check
|
|
* @returns Array of role information for roles that can start this action
|
|
*/
|
|
export function getRolesForAction(
|
|
template: XOTemplate,
|
|
actionIdentifier: string,
|
|
): TemplateRole[] {
|
|
const startEntries = (template.start ?? []).filter(
|
|
(s) => s.action === actionIdentifier,
|
|
);
|
|
|
|
return startEntries.map((entry) => {
|
|
const roleDef = template.roles?.[entry.role || ''];
|
|
const roleObj = typeof roleDef === "object" ? roleDef : null;
|
|
|
|
// TODO: This is ugly. Lot of conditionals. Need to take a much closer look at this.
|
|
return {
|
|
roleId: entry.role || '',
|
|
name: roleObj?.name || entry.role || '',
|
|
description: roleObj?.description,
|
|
};
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get template name safely.
|
|
*
|
|
* @param template - The template
|
|
* @returns The template name or a default
|
|
*/
|
|
export function getTemplateName(
|
|
template: XOTemplate | null | undefined,
|
|
): string {
|
|
return template?.name || "Unknown Template";
|
|
}
|
|
|
|
/**
|
|
* Get template description safely.
|
|
*
|
|
* @param template - The template
|
|
* @returns The template description or undefined
|
|
*/
|
|
export function getTemplateDescription(
|
|
template: XOTemplate | null | undefined,
|
|
): string | undefined {
|
|
return template?.description;
|
|
}
|
|
|
|
/**
|
|
* Get action name safely.
|
|
*
|
|
* @param template - The template containing the action
|
|
* @param actionId - The action identifier
|
|
* @returns The action name or the action ID as fallback
|
|
*/
|
|
export function getActionName(
|
|
template: XOTemplate | null | undefined,
|
|
actionId: string,
|
|
): string {
|
|
return template?.actions?.[actionId]?.name || actionId;
|
|
}
|
|
|
|
/**
|
|
* Get action description safely.
|
|
*
|
|
* @param template - The template containing the action
|
|
* @param actionId - The action identifier
|
|
* @returns The action description or undefined
|
|
*/
|
|
export function getActionDescription(
|
|
template: XOTemplate | null | undefined,
|
|
actionId: string,
|
|
): string | undefined {
|
|
return template?.actions?.[actionId]?.description;
|
|
}
|