11"use client"
22
3- import { useMemo , useState } from "react"
3+ import { useCallback , useMemo , useState } from "react"
44import { Button } from "@/components/ui/button"
55import { Undo2 } from "lucide-react"
66
@@ -101,6 +101,9 @@ export function InstructionsWizard({ onClose, selectedFileId }: InstructionsWiza
101101 const [ pendingConfirmation , setPendingConfirmation ] = useState < WizardConfirmationIntent | null > ( null )
102102 const [ generatedFile , setGeneratedFile ] = useState < GeneratedFileResult | null > ( null )
103103 const [ isGenerating , setIsGenerating ] = useState ( false )
104+ const [ isFrameworkFastTrackPromptVisible , setIsFrameworkFastTrackPromptVisible ] = useState ( false )
105+ const [ autoFilledQuestionMap , setAutoFilledQuestionMap ] = useState < Record < string , boolean > > ( { } )
106+ const [ autoFillNotice , setAutoFillNotice ] = useState < string | null > ( null )
104107
105108 const selectedFile = useMemo ( ( ) => {
106109 if ( selectedFileId ) {
@@ -123,6 +126,11 @@ export function InstructionsWizard({ onClose, selectedFileId }: InstructionsWiza
123126 [ dynamicSteps ]
124127 )
125128
129+ const nonFrameworkSteps = useMemo (
130+ ( ) => wizardSteps . filter ( ( step ) => step . id !== FRAMEWORK_STEP_ID ) ,
131+ [ wizardSteps ]
132+ )
133+
126134 const currentStep = wizardSteps [ currentStepIndex ] ?? null
127135 const currentQuestion = currentStep ?. questions [ currentQuestionIndex ] ?? null
128136
@@ -131,9 +139,21 @@ export function InstructionsWizard({ onClose, selectedFileId }: InstructionsWiza
131139 [ wizardSteps ]
132140 )
133141
142+ const remainingQuestionCount = useMemo (
143+ ( ) => nonFrameworkSteps . reduce ( ( count , step ) => count + step . questions . length , 0 ) ,
144+ [ nonFrameworkSteps ]
145+ )
146+
134147 const completionSummary = useMemo (
135- ( ) => buildCompletionSummary ( selectedFile ?? null , selectedFileFormatLabel , wizardSteps , responses ) ,
136- [ selectedFile , selectedFileFormatLabel , wizardSteps , responses ]
148+ ( ) =>
149+ buildCompletionSummary (
150+ selectedFile ?? null ,
151+ selectedFileFormatLabel ,
152+ wizardSteps ,
153+ responses ,
154+ autoFilledQuestionMap
155+ ) ,
156+ [ selectedFile , selectedFileFormatLabel , wizardSteps , responses , autoFilledQuestionMap ]
137157 )
138158
139159 const currentAnswerValue = currentQuestion ? responses [ currentQuestion . id ] : undefined
@@ -167,6 +187,37 @@ export function InstructionsWizard({ onClose, selectedFileId }: InstructionsWiza
167187 ? `Use default (${ defaultAnswer . label } )`
168188 : "Use default"
169189
190+ const showFrameworkPivot = ! isComplete && isFrameworkFastTrackPromptVisible
191+ const showQuestionControls = ! isComplete && ! isFrameworkFastTrackPromptVisible
192+
193+ const markQuestionsAutoFilled = useCallback ( ( questionIds : string [ ] ) => {
194+ if ( questionIds . length === 0 ) {
195+ return
196+ }
197+
198+ setAutoFilledQuestionMap ( ( prev ) => {
199+ const next = { ...prev }
200+ questionIds . forEach ( ( id ) => {
201+ if ( id !== FRAMEWORK_QUESTION_ID ) {
202+ next [ id ] = true
203+ }
204+ } )
205+ return next
206+ } )
207+ } , [ ] )
208+
209+ const clearAutoFilledFlag = useCallback ( ( questionId : string ) => {
210+ setAutoFilledQuestionMap ( ( prev ) => {
211+ if ( ! prev [ questionId ] ) {
212+ return prev
213+ }
214+
215+ const next = { ...prev }
216+ delete next [ questionId ]
217+ return next
218+ } )
219+ } , [ ] )
220+
170221 if ( ! currentStep || ! currentQuestion ) {
171222 return null
172223 }
@@ -198,6 +249,10 @@ export function InstructionsWizard({ onClose, selectedFileId }: InstructionsWiza
198249 }
199250
200251 const goToPrevious = ( ) => {
252+ if ( isFrameworkFastTrackPromptVisible ) {
253+ setIsFrameworkFastTrackPromptVisible ( false )
254+ }
255+
201256 const isFirstQuestionInStep = currentQuestionIndex === 0
202257 const isFirstStep = currentStepIndex === 0
203258
@@ -233,6 +288,12 @@ export function InstructionsWizard({ onClose, selectedFileId }: InstructionsWiza
233288 answers : question . answers . map ( mapAnswerSourceToWizard ) ,
234289 } ) )
235290
291+ const followUpQuestionCount =
292+ mappedQuestions . length + suffixSteps . reduce ( ( count , step ) => count + step . questions . length , 0 )
293+
294+ setAutoFilledQuestionMap ( { } )
295+ setAutoFillNotice ( null )
296+
236297 setDynamicSteps ( [
237298 {
238299 id : `framework-${ frameworkId } ` ,
@@ -241,6 +302,8 @@ export function InstructionsWizard({ onClose, selectedFileId }: InstructionsWiza
241302 } ,
242303 ] )
243304
305+ setIsFrameworkFastTrackPromptVisible ( followUpQuestionCount > 0 )
306+
244307 setResponses ( ( prev ) => {
245308 const next = { ...prev }
246309 mappedQuestions . forEach ( ( question ) => {
@@ -255,10 +318,75 @@ export function InstructionsWizard({ onClose, selectedFileId }: InstructionsWiza
255318 } catch ( error ) {
256319 console . error ( `Unable to load questions for framework "${ frameworkId } "` , error )
257320 setDynamicSteps ( [ ] )
321+ setIsFrameworkFastTrackPromptVisible ( false )
258322 } finally {
259323 }
260324 }
261325
326+ const applyDefaultsAcrossWizard = ( ) => {
327+ setGeneratedFile ( null )
328+ setAutoFilledQuestionMap ( { } )
329+
330+ const autoFilledIds : string [ ] = [ ]
331+
332+ setResponses ( ( prev ) => {
333+ const next : Responses = { ...prev }
334+
335+ wizardSteps . forEach ( ( step ) => {
336+ step . questions . forEach ( ( question ) => {
337+ if ( question . id === FRAMEWORK_QUESTION_ID ) {
338+ return
339+ }
340+
341+ const defaultAnswers = question . answers . filter ( ( answer ) => answer . isDefault && ! answer . disabled )
342+
343+ if ( defaultAnswers . length === 0 ) {
344+ return
345+ }
346+
347+ autoFilledIds . push ( question . id )
348+
349+ next [ question . id ] = question . allowMultiple
350+ ? defaultAnswers . map ( ( answer ) => answer . value )
351+ : defaultAnswers [ 0 ] ?. value
352+ } )
353+ } )
354+
355+ return next
356+ } )
357+
358+ markQuestionsAutoFilled ( autoFilledIds )
359+
360+ if ( autoFilledIds . length > 0 ) {
361+ setAutoFillNotice ( "We applied the recommended defaults for you. Tweak any section before generating." )
362+ } else {
363+ setAutoFillNotice ( null )
364+ }
365+
366+ setIsFrameworkFastTrackPromptVisible ( false )
367+
368+ const lastStepIndex = Math . max ( wizardSteps . length - 1 , 0 )
369+ const lastStep = wizardSteps [ lastStepIndex ]
370+ const lastQuestionIndex = lastStep ? Math . max ( lastStep . questions . length - 1 , 0 ) : 0
371+
372+ setCurrentStepIndex ( lastStepIndex )
373+ setCurrentQuestionIndex ( lastQuestionIndex )
374+ setIsComplete ( true )
375+ }
376+
377+ const beginStepByStepFlow = ( ) => {
378+ const firstNonFrameworkIndex = wizardSteps . findIndex ( ( step ) => step . id !== FRAMEWORK_STEP_ID )
379+
380+ if ( firstNonFrameworkIndex !== - 1 ) {
381+ setCurrentStepIndex ( firstNonFrameworkIndex )
382+ setCurrentQuestionIndex ( 0 )
383+ }
384+
385+ setIsFrameworkFastTrackPromptVisible ( false )
386+ setIsComplete ( false )
387+ setAutoFillNotice ( null )
388+ }
389+
262390 const handleAnswerClick = async ( answer : WizardAnswer ) => {
263391 if ( answer . disabled ) {
264392 return
@@ -292,6 +420,8 @@ export function InstructionsWizard({ onClose, selectedFileId }: InstructionsWiza
292420 [ currentQuestion . id ] : nextValue ,
293421 } ) )
294422
423+ clearAutoFilledFlag ( currentQuestion . id )
424+
295425 const isFrameworkQuestion = currentQuestion . id === FRAMEWORK_QUESTION_ID
296426 const shouldAutoAdvance =
297427 ! isFrameworkQuestion &&
@@ -309,6 +439,7 @@ export function InstructionsWizard({ onClose, selectedFileId }: InstructionsWiza
309439 await loadFrameworkQuestions ( answer . value , answer . label )
310440 } else {
311441 setDynamicSteps ( [ ] )
442+ setIsFrameworkFastTrackPromptVisible ( false )
312443 }
313444 }
314445 }
@@ -329,6 +460,8 @@ export function InstructionsWizard({ onClose, selectedFileId }: InstructionsWiza
329460 [ currentQuestion . id ] : nextValue ,
330461 } ) )
331462
463+ clearAutoFilledFlag ( currentQuestion . id )
464+
332465 const isFrameworkQuestion = currentQuestion . id === FRAMEWORK_QUESTION_ID
333466
334467 if ( isFrameworkQuestion ) {
@@ -348,6 +481,9 @@ export function InstructionsWizard({ onClose, selectedFileId }: InstructionsWiza
348481 setIsComplete ( false )
349482 setGeneratedFile ( null )
350483 setIsGenerating ( false )
484+ setIsFrameworkFastTrackPromptVisible ( false )
485+ setAutoFilledQuestionMap ( { } )
486+ setAutoFillNotice ( null )
351487 }
352488
353489 const resetWizard = ( ) => {
@@ -456,15 +592,18 @@ export function InstructionsWizard({ onClose, selectedFileId }: InstructionsWiza
456592 : undefined
457593 const showChangeFile = Boolean ( onClose && selectedFile )
458594
595+ const topButtonLabel = showFrameworkPivot ? "Choose a different network" : "Start Over"
596+ const topButtonHandler = showFrameworkPivot ? ( ) => goToPrevious ( ) : ( ) => requestResetWizard ( )
597+
459598 const wizardLayout = (
460599 < div className = "mx-auto flex w-full max-w-4xl flex-col gap-6" >
461600 < Button
462601 variant = "destructive"
463602 size = "sm"
464- onClick = { requestResetWizard }
603+ onClick = { topButtonHandler }
465604 className = "fixed left-4 top-4 z-40"
466605 >
467- Start Over
606+ { topButtonLabel }
468607 </ Button >
469608
470609 { selectedFile ? (
@@ -484,14 +623,38 @@ export function InstructionsWizard({ onClose, selectedFileId }: InstructionsWiza
484623 </ section >
485624 ) : null }
486625
487- { isComplete ? (
488- < WizardCompletionSummary
489- summary = { completionSummary }
490- onBack = { goToPrevious }
491- onGenerate = { ( ) => void generateInstructionsFile ( ) }
492- isGenerating = { isGenerating }
493- />
494- ) : (
626+ { showFrameworkPivot ? (
627+ < section className = "rounded-3xl border border-border/80 bg-card/95 p-6 shadow-lg" >
628+ < div className = "flex flex-col gap-6" >
629+ < div className = "space-y-4" >
630+ < div className = "space-y-2" >
631+ < h1 className = "text-3xl font-semibold text-foreground" > Skip the deep dive?</ h1 >
632+ < p className = "text-sm text-muted-foreground" >
633+ We can auto-apply the recommended answers for the next { remainingQuestionCount } { " " }
634+ { remainingQuestionCount === 1 ? "question" : "questions" } across these sections. (You can still tweak the defaults.)
635+ </ p >
636+ </ div >
637+ < div className = "flex flex-wrap items-center justify-between gap-3" >
638+ < div className = "flex flex-wrap gap-2" >
639+ < Button variant = "secondary" className = "px-5" onClick = { ( ) => beginStepByStepFlow ( ) } >
640+ Fill it out step-by-step
641+ </ Button >
642+ < Button
643+ onClick = { ( ) => applyDefaultsAcrossWizard ( ) }
644+ disabled = { remainingQuestionCount === 0 }
645+ className = "px-5"
646+ >
647+ Use recommended defaults
648+ </ Button >
649+ </ div >
650+ </ div >
651+ </ div >
652+
653+ </ div >
654+ </ section >
655+ ) : null }
656+
657+ { showQuestionControls ? (
495658 < section className = "rounded-3xl border border-border/80 bg-card/95 p-6 shadow-lg" >
496659 < div className = "flex flex-col gap-4" >
497660 < div className = "flex flex-wrap items-center gap-2" >
@@ -531,7 +694,17 @@ export function InstructionsWizard({ onClose, selectedFileId }: InstructionsWiza
531694 </ div >
532695 </ div >
533696 </ section >
534- ) }
697+ ) : null }
698+
699+ { isComplete ? (
700+ < WizardCompletionSummary
701+ summary = { completionSummary }
702+ onBack = { goToPrevious }
703+ onGenerate = { ( ) => void generateInstructionsFile ( ) }
704+ isGenerating = { isGenerating }
705+ autoFillNotice = { autoFillNotice }
706+ />
707+ ) : null }
535708
536709 { pendingConfirmation ? (
537710 < WizardConfirmationDialog
0 commit comments