Clean up and fixes
This commit is contained in:
52
src/tui/screens/action-wizard/steps/InfoStep.tsx
Normal file
52
src/tui/screens/action-wizard/steps/InfoStep.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
import React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { colors } from '../../../theme.js';
|
||||
import type { WizardStepProps } from '../types.js';
|
||||
|
||||
type Props = Pick<
|
||||
WizardStepProps,
|
||||
'template' | 'actionIdentifier' | 'roleIdentifier' | 'actionName'
|
||||
>;
|
||||
|
||||
export function InfoStep({
|
||||
template,
|
||||
actionIdentifier,
|
||||
roleIdentifier,
|
||||
actionName,
|
||||
}: Props): React.ReactElement {
|
||||
const action = template?.actions?.[actionIdentifier];
|
||||
const role = action?.roles?.[roleIdentifier];
|
||||
|
||||
return (
|
||||
<Box flexDirection='column'>
|
||||
<Text color={colors.primary} bold>
|
||||
Action: {actionName}
|
||||
</Text>
|
||||
<Text color={colors.textMuted}>
|
||||
{action?.description || 'No description'}
|
||||
</Text>
|
||||
|
||||
<Box marginTop={1}>
|
||||
<Text color={colors.text}>Your Role: </Text>
|
||||
<Text color={colors.accent}>{roleIdentifier}</Text>
|
||||
</Box>
|
||||
|
||||
{role?.requirements && (
|
||||
<Box marginTop={1} flexDirection='column'>
|
||||
<Text color={colors.text}>Requirements:</Text>
|
||||
{role.requirements.variables?.map((v) => (
|
||||
<Text key={v} color={colors.textMuted}>
|
||||
{' '}• Variable: {v}
|
||||
</Text>
|
||||
))}
|
||||
{role.requirements.slots && (
|
||||
<Text color={colors.textMuted}>
|
||||
{' '}• Slots: {role.requirements.slots.min} min (UTXO selection
|
||||
required)
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
92
src/tui/screens/action-wizard/steps/InputsStep.tsx
Normal file
92
src/tui/screens/action-wizard/steps/InputsStep.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
import React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { colors, formatSatoshis, formatHex } from '../../../theme.js';
|
||||
import type { WizardStepProps } from '../types.js';
|
||||
|
||||
type Props = Pick<
|
||||
WizardStepProps,
|
||||
| 'availableUtxos'
|
||||
| 'selectedUtxoIndex'
|
||||
| 'requiredAmount'
|
||||
| 'fee'
|
||||
| 'selectedAmount'
|
||||
| 'changeAmount'
|
||||
| 'focusArea'
|
||||
>;
|
||||
|
||||
export function InputsStep({
|
||||
availableUtxos,
|
||||
selectedUtxoIndex,
|
||||
requiredAmount,
|
||||
fee,
|
||||
selectedAmount,
|
||||
changeAmount,
|
||||
focusArea,
|
||||
}: Props): React.ReactElement {
|
||||
return (
|
||||
<Box flexDirection='column'>
|
||||
<Text color={colors.text} bold>
|
||||
Select UTXOs to fund the transaction:
|
||||
</Text>
|
||||
|
||||
<Box marginTop={1} flexDirection='column'>
|
||||
<Text color={colors.textMuted}>
|
||||
Required: {formatSatoshis(requiredAmount)} +{' '}
|
||||
{formatSatoshis(fee)} fee
|
||||
</Text>
|
||||
<Text
|
||||
color={
|
||||
selectedAmount >= requiredAmount + fee
|
||||
? colors.success
|
||||
: colors.warning
|
||||
}
|
||||
>
|
||||
Selected: {formatSatoshis(selectedAmount)}
|
||||
</Text>
|
||||
{selectedAmount > requiredAmount + fee && (
|
||||
<Text color={colors.info}>
|
||||
Change: {formatSatoshis(changeAmount)}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
marginTop={1}
|
||||
flexDirection='column'
|
||||
borderStyle='single'
|
||||
borderColor={colors.border}
|
||||
paddingX={1}
|
||||
>
|
||||
{availableUtxos.length === 0 ? (
|
||||
<Text color={colors.textMuted}>No UTXOs available</Text>
|
||||
) : (
|
||||
availableUtxos.map((utxo, index) => {
|
||||
const isCursor =
|
||||
selectedUtxoIndex === index && focusArea === 'content';
|
||||
return (
|
||||
<Box
|
||||
key={`${utxo.outpointTransactionHash}:${utxo.outpointIndex}`}
|
||||
>
|
||||
<Text
|
||||
color={isCursor ? colors.focus : colors.text}
|
||||
bold={isCursor}
|
||||
>
|
||||
{isCursor ? '▸ ' : ' '}[{utxo.selected ? 'X' : ' '}]{' '}
|
||||
{formatSatoshis(utxo.valueSatoshis)} -{' '}
|
||||
{formatHex(utxo.outpointTransactionHash, 12)}:
|
||||
{utxo.outpointIndex}
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Box marginTop={1}>
|
||||
<Text color={colors.textMuted} dimColor>
|
||||
Space/Enter: Toggle • a: Select all • n: Deselect all
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
45
src/tui/screens/action-wizard/steps/PublishStep.tsx
Normal file
45
src/tui/screens/action-wizard/steps/PublishStep.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { colors } from '../../../theme.js';
|
||||
|
||||
interface PublishStepProps {
|
||||
invitationId: string | null;
|
||||
}
|
||||
|
||||
export function PublishStep({
|
||||
invitationId,
|
||||
}: PublishStepProps): React.ReactElement {
|
||||
return (
|
||||
<Box flexDirection='column'>
|
||||
<Text color={colors.success} bold>
|
||||
✓ Invitation Created & Published!
|
||||
</Text>
|
||||
|
||||
<Box marginTop={1} flexDirection='column'>
|
||||
<Text color={colors.text}>Invitation ID:</Text>
|
||||
<Box
|
||||
borderStyle='single'
|
||||
borderColor={colors.primary}
|
||||
paddingX={1}
|
||||
marginTop={1}
|
||||
>
|
||||
<Text color={colors.accent}>
|
||||
{invitationId ?? '(unknown)'}
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box marginTop={1}>
|
||||
<Text color={colors.textMuted}>
|
||||
Share this ID with the other party to complete the transaction.
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Box marginTop={1}>
|
||||
<Text color={colors.warning}>
|
||||
Press 'c' to copy ID to clipboard
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
93
src/tui/screens/action-wizard/steps/ReviewStep.tsx
Normal file
93
src/tui/screens/action-wizard/steps/ReviewStep.tsx
Normal file
@@ -0,0 +1,93 @@
|
||||
import React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { colors, formatSatoshis } from '../../../theme.js';
|
||||
import type { VariableInput, SelectableUTXO } from '../types.js';
|
||||
import type { XOTemplate } from '@xo-cash/types';
|
||||
|
||||
interface ReviewStepProps {
|
||||
template: XOTemplate;
|
||||
actionName: string;
|
||||
roleIdentifier: string;
|
||||
variables: VariableInput[];
|
||||
availableUtxos: SelectableUTXO[];
|
||||
changeAmount: bigint;
|
||||
}
|
||||
|
||||
export function ReviewStep({
|
||||
template,
|
||||
actionName,
|
||||
roleIdentifier,
|
||||
variables,
|
||||
availableUtxos,
|
||||
changeAmount,
|
||||
}: ReviewStepProps): React.ReactElement {
|
||||
const selectedUtxos = availableUtxos.filter((u) => u.selected);
|
||||
|
||||
return (
|
||||
<Box flexDirection='column'>
|
||||
<Text color={colors.text} bold>
|
||||
Review your invitation:
|
||||
</Text>
|
||||
|
||||
{/* Summary */}
|
||||
<Box marginTop={1} flexDirection='column'>
|
||||
<Text color={colors.textMuted}>Template: {template?.name}</Text>
|
||||
<Text color={colors.textMuted}>Action: {actionName}</Text>
|
||||
<Text color={colors.textMuted}>Role: {roleIdentifier}</Text>
|
||||
</Box>
|
||||
|
||||
{/* Variables */}
|
||||
{variables.length > 0 && (
|
||||
<Box marginTop={1} flexDirection='column'>
|
||||
<Text color={colors.text}>Variables:</Text>
|
||||
{variables.map((v) => (
|
||||
<Text key={v.id} color={colors.textMuted}>
|
||||
{' '}
|
||||
{v.name}: {v.value || '(empty)'}
|
||||
</Text>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Inputs */}
|
||||
{selectedUtxos.length > 0 && (
|
||||
<Box marginTop={1} flexDirection='column'>
|
||||
<Text color={colors.text}>
|
||||
Inputs ({selectedUtxos.length}):
|
||||
</Text>
|
||||
{selectedUtxos.slice(0, 3).map((u) => (
|
||||
<Text
|
||||
key={`${u.outpointTransactionHash}:${u.outpointIndex}`}
|
||||
color={colors.textMuted}
|
||||
>
|
||||
{' '}
|
||||
{formatSatoshis(u.valueSatoshis)}
|
||||
</Text>
|
||||
))}
|
||||
{selectedUtxos.length > 3 && (
|
||||
<Text color={colors.textMuted}>
|
||||
{' '}...and {selectedUtxos.length - 3} more
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Outputs */}
|
||||
{changeAmount > 0n && (
|
||||
<Box marginTop={1} flexDirection='column'>
|
||||
<Text color={colors.text}>Outputs:</Text>
|
||||
<Text color={colors.textMuted}>
|
||||
{' '}Change: {formatSatoshis(changeAmount)}
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Confirmation prompt */}
|
||||
<Box marginTop={1}>
|
||||
<Text color={colors.warning}>
|
||||
Press Next to create and publish the invitation.
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
49
src/tui/screens/action-wizard/steps/VariablesStep.tsx
Normal file
49
src/tui/screens/action-wizard/steps/VariablesStep.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import { Box, Text } from 'ink';
|
||||
import { colors } from '../../../theme.js';
|
||||
import { VariableInputField } from '../../../components/VariableInputField.js';
|
||||
import type { WizardStepProps } from '../types.js';
|
||||
|
||||
type Props = Pick<
|
||||
WizardStepProps,
|
||||
| 'variables'
|
||||
| 'updateVariable'
|
||||
| 'handleTextInputSubmit'
|
||||
| 'focusArea'
|
||||
| 'focusedInput'
|
||||
>;
|
||||
|
||||
export function VariablesStep({
|
||||
variables,
|
||||
updateVariable,
|
||||
handleTextInputSubmit,
|
||||
focusArea,
|
||||
focusedInput,
|
||||
}: Props): React.ReactElement {
|
||||
return (
|
||||
<Box flexDirection='column'>
|
||||
<Text color={colors.text} bold>
|
||||
Enter required values:
|
||||
</Text>
|
||||
<Box marginTop={1} flexDirection='column'>
|
||||
{variables.map((variable, index) => (
|
||||
<VariableInputField
|
||||
key={variable.id}
|
||||
variable={variable}
|
||||
index={index}
|
||||
isFocused={focusArea === 'content' && focusedInput === index}
|
||||
onChange={updateVariable}
|
||||
onSubmit={handleTextInputSubmit}
|
||||
borderColor={colors.border as string}
|
||||
focusColor={colors.primary as string}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
<Box marginTop={1}>
|
||||
<Text color={colors.textMuted} dimColor>
|
||||
Type your value, then press Enter to continue
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
5
src/tui/screens/action-wizard/steps/index.ts
Normal file
5
src/tui/screens/action-wizard/steps/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from './InfoStep.js';
|
||||
export * from './VariablesStep.js';
|
||||
export * from './InputsStep.js';
|
||||
export * from './ReviewStep.js';
|
||||
export * from './PublishStep.js';
|
||||
Reference in New Issue
Block a user