Skip to content

Commit e6f70c6

Browse files
committed
polish
1 parent 536b078 commit e6f70c6

File tree

11 files changed

+424
-72
lines changed

11 files changed

+424
-72
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
* Confirmation screen component for submitting the ask_user form
3+
* Shown as the final "question" after all questions are answered
4+
*/
5+
6+
import React from 'react'
7+
import { TextAttributes } from '@opentui/core'
8+
import { useTheme } from '../../../hooks/use-theme'
9+
import { Button } from '../../button'
10+
import { BORDER_CHARS } from '../../../utils/ui-constants'
11+
12+
export interface AnswerSummary {
13+
question: string
14+
header?: string
15+
answer: string
16+
}
17+
18+
export interface ConfirmScreenProps {
19+
onSubmit: () => void
20+
onBack: () => void
21+
submitFocused: boolean
22+
backFocused: boolean
23+
onSubmitMouseOver: () => void
24+
onBackMouseOver: () => void
25+
answers: AnswerSummary[]
26+
}
27+
28+
export const ConfirmScreen: React.FC<ConfirmScreenProps> = ({
29+
onSubmit,
30+
onBack,
31+
submitFocused,
32+
backFocused,
33+
onSubmitMouseOver,
34+
onBackMouseOver,
35+
answers,
36+
}) => {
37+
const theme = useTheme()
38+
39+
return (
40+
<box style={{ flexDirection: 'column', gap: 1 }}>
41+
<text
42+
style={{
43+
fg: theme.foreground,
44+
attributes: TextAttributes.BOLD,
45+
}}
46+
>
47+
Your answers:
48+
</text>
49+
50+
{/* Answer summary */}
51+
<box style={{ flexDirection: 'column', gap: 0, marginTop: 0, paddingLeft: 1 }}>
52+
{answers.map((item, idx) => (
53+
<box key={idx} style={{ flexDirection: 'row', gap: 1 }}>
54+
<text style={{ fg: theme.muted }}>{idx + 1}.</text>
55+
<text style={{ fg: theme.primary }}>{item.answer}</text>
56+
</box>
57+
))}
58+
</box>
59+
60+
<box style={{ flexDirection: 'row', gap: 2, marginTop: 1 }}>
61+
<Button
62+
onClick={onBack}
63+
onMouseOver={onBackMouseOver}
64+
style={{
65+
borderStyle: 'single',
66+
borderColor: backFocused ? theme.secondary : theme.muted,
67+
customBorderChars: BORDER_CHARS,
68+
paddingLeft: 2,
69+
paddingRight: 2,
70+
backgroundColor: backFocused ? theme.surface : undefined,
71+
}}
72+
>
73+
<text
74+
style={{
75+
fg: backFocused ? theme.foreground : theme.muted,
76+
attributes: backFocused ? TextAttributes.BOLD : undefined,
77+
}}
78+
>
79+
← Back
80+
</text>
81+
</Button>
82+
83+
<Button
84+
onClick={onSubmit}
85+
onMouseOver={onSubmitMouseOver}
86+
style={{
87+
borderStyle: 'single',
88+
borderColor: submitFocused ? theme.primary : theme.secondary,
89+
customBorderChars: BORDER_CHARS,
90+
paddingLeft: 2,
91+
paddingRight: 2,
92+
backgroundColor: submitFocused ? theme.surface : undefined,
93+
}}
94+
>
95+
<text
96+
style={{
97+
fg: submitFocused ? theme.primary : theme.foreground,
98+
attributes: submitFocused ? TextAttributes.BOLD : undefined,
99+
}}
100+
>
101+
Submit ↵
102+
</text>
103+
</Button>
104+
</box>
105+
</box>
106+
)
107+
}

cli/src/components/ask-user/components/other-text-input.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export const OtherTextInput: React.FC<OtherTextInputProps> = ({
3939
flexDirection: 'row',
4040
gap: 1,
4141
backgroundColor: isFocused ? theme.surface : undefined,
42-
marginTop: 1,
42+
marginTop: 0,
4343
paddingTop: 0,
4444
paddingBottom: 0,
4545
}}

cli/src/components/ask-user/components/progress-indicator.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,18 @@ export interface ProgressIndicatorProps {
1313
currentIndex: number
1414
answeredStates: boolean[]
1515
allAnswered: boolean
16+
isOnConfirmScreen: boolean
1617
onNavigate?: (index: number) => void
18+
onNavigateToConfirm?: () => void
1719
}
1820

1921
export const ProgressIndicator: React.FC<ProgressIndicatorProps> = ({
2022
currentIndex,
2123
answeredStates,
2224
allAnswered,
25+
isOnConfirmScreen,
2326
onNavigate,
27+
onNavigateToConfirm,
2428
}) => {
2529
const theme = useTheme()
2630

@@ -45,9 +49,15 @@ export const ProgressIndicator: React.FC<ProgressIndicatorProps> = ({
4549
</Button>
4650
)
4751
})}
48-
{allAnswered && (
49-
<text style={{ fg: theme.primary, marginLeft: 1 }}>Complete! ✓</text>
50-
)}
52+
{/* Confirm dot - always shown, clickable when all answered */}
53+
<Button
54+
onClick={() => { if (allAnswered) onNavigateToConfirm?.() }}
55+
style={{ padding: 0 }}
56+
>
57+
<text style={{ fg: isOnConfirmScreen ? theme.primary : allAnswered ? theme.foreground : theme.muted }}>
58+
{isOnConfirmScreen ? SYMBOLS.COMPLETED : allAnswered ? SYMBOLS.CURRENT : SYMBOLS.UNSELECTED}
59+
</text>
60+
</Button>
5161
</box>
5262
)
5363
}

cli/src/components/ask-user/components/question-header.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ export interface QuestionHeaderProps {
1515
totalQuestions: number
1616
answeredStates: boolean[]
1717
allAnswered: boolean
18+
isOnConfirmScreen?: boolean
1819
onSkip?: () => void
1920
onNavigate?: (index: number) => void
21+
onNavigateToConfirm?: () => void
2022
onPrev?: () => void
2123
onNext?: () => void
2224
skipButtonFocused?: boolean
@@ -31,8 +33,10 @@ export const QuestionHeader: React.FC<QuestionHeaderProps> = ({
3133
totalQuestions,
3234
answeredStates,
3335
allAnswered,
36+
isOnConfirmScreen = false,
3437
onSkip,
3538
onNavigate,
39+
onNavigateToConfirm,
3640
onPrev,
3741
onNext,
3842
skipButtonFocused = false,
@@ -42,8 +46,8 @@ export const QuestionHeader: React.FC<QuestionHeaderProps> = ({
4246
hasRoomForInlineButtons,
4347
}) => {
4448
const theme = useTheme()
45-
const isFirstQuestion = currentIndex === 0
46-
const isLastQuestion = currentIndex === totalQuestions - 1
49+
const isFirstQuestion = currentIndex === 0 && !isOnConfirmScreen
50+
const isLastQuestion = isOnConfirmScreen || (currentIndex === totalQuestions - 1 && !allAnswered)
4751

4852
return (
4953
<box
@@ -58,15 +62,17 @@ export const QuestionHeader: React.FC<QuestionHeaderProps> = ({
5862
<box style={{ flexDirection: 'column', gap: 0 }}>
5963
<box style={{ flexDirection: 'row', gap: 0 }}>
6064
<text style={{ fg: theme.secondary }}>
61-
Question {currentIndex + 1} of {totalQuestions}
65+
{isOnConfirmScreen ? 'Ready to submit' : `Question ${currentIndex + 1} of ${totalQuestions}`}
6266
</text>
6367
</box>
6468
{totalQuestions > 1 && (
6569
<ProgressIndicator
6670
currentIndex={currentIndex}
6771
answeredStates={answeredStates}
6872
allAnswered={allAnswered}
73+
isOnConfirmScreen={isOnConfirmScreen}
6974
onNavigate={onNavigate}
75+
onNavigateToConfirm={onNavigateToConfirm}
7076
/>
7177
)}
7278
</box>

cli/src/components/ask-user/components/skip-button.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export const SkipButton: React.FC<SkipButtonProps> = ({
4747
attributes: isHighlighted ? TextAttributes.BOLD : undefined,
4848
}}
4949
>
50-
Skip
50+
Skip All
5151
</text>
5252
</Button>
5353
)

cli/src/components/ask-user/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const LAYOUT_BREAKPOINTS = {
3333
COMPACT: 40,
3434

3535
/** Comfortable layout: current behavior */
36-
COMFORTABLE: 55,
36+
COMFORTABLE: 40,
3737

3838
/** Spacious layout: extra padding, inline descriptions */
3939
SPACIOUS: 80,

cli/src/components/ask-user/hooks/use-auto-advance.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,19 +58,18 @@ export function useAutoAdvance(config: AutoAdvanceConfig) {
5858
// Use provided otherTexts or fall back to config
5959
const finalOtherTexts = updatedOtherTexts || config.otherTexts
6060

61-
if (!config.isLastQuestion) {
61+
// Check if all questions are now answered
62+
if (areAllQuestionsAnswered(updatedAnswers, finalOtherTexts)) {
63+
// All answered: go to confirm screen (onSubmit here triggers confirm screen navigation)
64+
config.onSubmit(updatedAnswers, finalOtherTexts)
65+
} else if (!config.isLastQuestion) {
6266
// Auto-advance to next question after delay
6367
const delay = config.delayMs ?? ASK_USER_CONFIG.AUTO_ADVANCE_DELAY_MS
6468

6569
timeoutRef.current = setTimeout(() => {
6670
config.onAdvanceQuestion()
6771
timeoutRef.current = null
6872
}, delay)
69-
} else {
70-
// On last question: submit if all answered
71-
if (areAllQuestionsAnswered(updatedAnswers, finalOtherTexts)) {
72-
config.onSubmit(updatedAnswers, finalOtherTexts)
73-
}
7473
}
7574
},
7675
[

cli/src/components/ask-user/hooks/use-focus-manager.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ export type FocusAction =
2121
| { type: 'SELECT_OPTION'; questionIndex: number; optionIndex: number }
2222
| { type: 'SELECT_TEXT_INPUT'; questionIndex: number }
2323
| { type: 'SELECT_SKIP' }
24+
| { type: 'SELECT_CONFIRM_SUBMIT' }
25+
| { type: 'SELECT_CONFIRM_BACK' }
2426
| { type: 'RESET_TO_QUESTION'; questionIndex: number }
27+
| { type: 'RESET_TO_CONFIRM' }
2528

2629
/**
2730
* Context needed for focus reducer
@@ -78,6 +81,15 @@ function focusReducer(
7881
case 'SELECT_SKIP':
7982
return { type: 'skip' }
8083

84+
case 'SELECT_CONFIRM_SUBMIT':
85+
return { type: 'confirmSubmit' }
86+
87+
case 'SELECT_CONFIRM_BACK':
88+
return { type: 'confirmBack' }
89+
90+
case 'RESET_TO_CONFIRM':
91+
return { type: 'confirmSubmit' }
92+
8193
case 'RESET_TO_QUESTION':
8294
// Reset focus to first option of specified question
8395
return createOptionFocus(action.questionIndex, 0)
@@ -172,13 +184,28 @@ export function useFocusActions(dispatch: (action: FocusAction) => void) {
172184
[dispatch]
173185
)
174186

187+
const selectConfirmSubmit = useCallback(() => {
188+
dispatch({ type: 'SELECT_CONFIRM_SUBMIT' })
189+
}, [dispatch])
190+
191+
const selectConfirmBack = useCallback(() => {
192+
dispatch({ type: 'SELECT_CONFIRM_BACK' })
193+
}, [dispatch])
194+
195+
const resetToConfirm = useCallback(() => {
196+
dispatch({ type: 'RESET_TO_CONFIRM' })
197+
}, [dispatch])
198+
175199
return {
176200
navigateUp,
177201
navigateDown,
178202
tabNext,
179203
selectOption,
180204
selectTextInput,
181205
selectSkip,
206+
selectConfirmSubmit,
207+
selectConfirmBack,
182208
resetToQuestion,
209+
resetToConfirm,
183210
}
184211
}

0 commit comments

Comments
 (0)