Big changes and fixes. Uses action history. Improve role selection. Remove unused logs
This commit is contained in:
@@ -14,7 +14,20 @@ import { colors, logoSmall } from '../theme.js';
|
||||
|
||||
// XO Imports
|
||||
import { generateTemplateIdentifier } from '@xo-cash/engine';
|
||||
import type { XOTemplate, XOTemplateActionRoleRequirement, XOTemplateStartingActions } from '@xo-cash/types';
|
||||
import type { XOTemplate } from '@xo-cash/types';
|
||||
|
||||
/**
|
||||
* A unique starting action (deduplicated by action identifier).
|
||||
* Multiple roles that can start the same action are counted
|
||||
* but not shown as separate entries — role selection happens
|
||||
* inside the Action Wizard.
|
||||
*/
|
||||
interface UniqueStartingAction {
|
||||
actionIdentifier: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
roleCount: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template item with metadata.
|
||||
@@ -22,7 +35,7 @@ import type { XOTemplate, XOTemplateActionRoleRequirement, XOTemplateStartingAct
|
||||
interface TemplateItem {
|
||||
template: XOTemplate;
|
||||
templateIdentifier: string;
|
||||
startingActions: XOTemplateStartingActions;
|
||||
startingActions: UniqueStartingAction[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,8 +72,30 @@ export function TemplateListScreen(): React.ReactElement {
|
||||
const loadedTemplates = await Promise.all(
|
||||
templateList.map(async (template) => {
|
||||
const templateIdentifier = generateTemplateIdentifier(template);
|
||||
const startingActions = await appService.engine.listStartingActions(templateIdentifier);
|
||||
return { template, templateIdentifier, startingActions };
|
||||
const rawStartingActions = await appService.engine.listStartingActions(templateIdentifier);
|
||||
|
||||
// Deduplicate by action identifier — role selection
|
||||
// is handled inside the Action Wizard, not here.
|
||||
const actionMap = new Map<string, UniqueStartingAction>();
|
||||
for (const sa of rawStartingActions) {
|
||||
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 {
|
||||
template,
|
||||
templateIdentifier,
|
||||
startingActions: Array.from(actionMap.values()),
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
@@ -86,6 +121,7 @@ export function TemplateListScreen(): React.ReactElement {
|
||||
|
||||
/**
|
||||
* Handles action selection.
|
||||
* Navigates to the Action Wizard where the user will choose their role.
|
||||
*/
|
||||
const handleActionSelect = useCallback(() => {
|
||||
if (!currentTemplate || currentActions.length === 0) return;
|
||||
@@ -93,11 +129,10 @@ export function TemplateListScreen(): React.ReactElement {
|
||||
const action = currentActions[selectedActionIndex];
|
||||
if (!action) return;
|
||||
|
||||
// Navigate to action wizard with selected template and action
|
||||
// Navigate to the Action Wizard — role selection happens there
|
||||
navigate('wizard', {
|
||||
templateIdentifier: currentTemplate.templateIdentifier,
|
||||
actionIdentifier: action.action,
|
||||
roleIdentifier: action.role,
|
||||
actionIdentifier: action.actionIdentifier,
|
||||
template: currentTemplate.template,
|
||||
});
|
||||
}, [currentTemplate, currentActions, selectedActionIndex, navigate]);
|
||||
@@ -211,20 +246,19 @@ export function TemplateListScreen(): React.ReactElement {
|
||||
}
|
||||
|
||||
// Starting actions state
|
||||
return currentActions.map((action, index) => {
|
||||
const actionDef = currentTemplate.template.actions?.[action.action];
|
||||
const name = actionDef?.name || action.action;
|
||||
return (
|
||||
<Text
|
||||
key={`${action.action}-${action.role}`}
|
||||
color={index === selectedActionIndex ? colors.focus : colors.text}
|
||||
bold={index === selectedActionIndex}
|
||||
>
|
||||
{index === selectedActionIndex && focusedPanel === 'actions' ? '▸ ' : ' '}
|
||||
{index + 1}. {name} (as {action.role})
|
||||
</Text>
|
||||
);
|
||||
});
|
||||
return currentActions.map((action, index) => (
|
||||
<Text
|
||||
key={action.actionIdentifier}
|
||||
color={index === selectedActionIndex ? colors.focus : colors.text}
|
||||
bold={index === selectedActionIndex}
|
||||
>
|
||||
{index === selectedActionIndex && focusedPanel === 'actions' ? '▸ ' : ' '}
|
||||
{index + 1}. {action.name}
|
||||
{action.roleCount > 1
|
||||
? ` (${action.roleCount} roles)`
|
||||
: ''}
|
||||
</Text>
|
||||
));
|
||||
})()}
|
||||
|
||||
</Box>
|
||||
@@ -280,50 +314,34 @@ export function TemplateListScreen(): React.ReactElement {
|
||||
const action = currentActions[selectedActionIndex];
|
||||
if (!action) return null;
|
||||
|
||||
const actionDef = currentTemplate.template.actions?.[action.action];
|
||||
const roleDef = currentTemplate.template.roles?.[action.role];
|
||||
// Collect all roles that can start this action
|
||||
const startEntries = (currentTemplate.template.start ?? [])
|
||||
.filter((s) => s.action === action.actionIdentifier);
|
||||
|
||||
// if (!actionDef || !roleDef) return null;
|
||||
|
||||
const [_roleName, role] = Object.entries(actionDef?.roles ?? {}).find(([roleId, role]) => roleId === action.role) || [];
|
||||
|
||||
console.log(JSON.stringify(role, null, 2));
|
||||
|
||||
const variableKeys = role?.requirements?.variables || []
|
||||
|
||||
console.log('variables', variableKeys);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Text color={colors.text} bold>
|
||||
{actionDef?.name || action.action}
|
||||
{action.name}
|
||||
</Text>
|
||||
<Text color={colors.textMuted}>
|
||||
{actionDef?.description || 'No description available'}
|
||||
{action.description || 'No description available'}
|
||||
</Text>
|
||||
<Box marginTop={1} flexDirection='column'>
|
||||
<Text color={colors.text}>
|
||||
Role: {roleDef?.name || action.role}
|
||||
</Text>
|
||||
{roleDef?.description && (
|
||||
<Text color={colors.textMuted}>
|
||||
{' '}{roleDef.description}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
{/* Display variables if available */}
|
||||
{
|
||||
variableKeys.length > 0 && (
|
||||
<Box marginTop={1} flexDirection='column'>
|
||||
<Text color={colors.text}>Variables:</Text>
|
||||
{variableKeys.map((variableKey) => (
|
||||
<Text key={variableKey} color={colors.text}>
|
||||
- {currentTemplate.template.variables?.[variableKey]?.name || variableKey}: {currentTemplate.template.variables?.[variableKey]?.description || 'No description'}
|
||||
|
||||
{/* List available roles for this action */}
|
||||
{startEntries.length > 0 && (
|
||||
<Box marginTop={1} flexDirection='column'>
|
||||
<Text color={colors.text}>Available Roles:</Text>
|
||||
{startEntries.map((entry) => {
|
||||
const roleDef = currentTemplate.template.roles?.[entry.role];
|
||||
return (
|
||||
<Text key={entry.role} color={colors.textMuted}>
|
||||
{' '}- {roleDef?.name || entry.role}
|
||||
{roleDef?.description ? `: ${roleDef.description}` : ''}
|
||||
</Text>
|
||||
))}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
|
||||
Reference in New Issue
Block a user