fix(wizard): preserve form data and model selection on back nav

- Add initialValues and onValuesChange props to ProviderForm
- Save provider form data and selected models in wizard state
- Fix canProceed missing modelsAdded dependency
- Restore form data and selections when navigating back
This commit is contained in:
langbot-dev
2026-08-12 02:11:59 +08:00
parent 1f0371e177
commit 259e127696
2 changed files with 64 additions and 8 deletions
@@ -31,14 +31,25 @@ const getFormSchema = (t: (key: string) => string) =>
api_key: z.string().optional(), api_key: z.string().optional(),
}); });
export interface ProviderFormInitialValues {
name?: string;
requester?: string;
base_url?: string;
api_key?: string;
}
interface ProviderFormProps { interface ProviderFormProps {
providerId?: string; providerId?: string;
initialValues?: ProviderFormInitialValues;
onValuesChange?: (values: ProviderFormInitialValues) => void;
onFormSubmit: (providerUuid?: string) => void; onFormSubmit: (providerUuid?: string) => void;
onFormCancel: () => void; onFormCancel: () => void;
} }
export default function ProviderForm({ export default function ProviderForm({
providerId, providerId,
initialValues,
onValuesChange,
onFormSubmit, onFormSubmit,
onFormCancel, onFormCancel,
}: ProviderFormProps) { }: ProviderFormProps) {
@@ -48,13 +59,22 @@ export default function ProviderForm({
const form = useForm<z.infer<typeof formSchema>>({ const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema), resolver: zodResolver(formSchema),
defaultValues: { defaultValues: {
name: '', name: initialValues?.name ?? '',
requester: '', requester: initialValues?.requester ?? '',
base_url: '', base_url: initialValues?.base_url ?? '',
api_key: '', api_key: initialValues?.api_key ?? '',
}, },
}); });
const { setValue } = form; const { setValue, watch } = form;
// Report form values changes to parent
useEffect(() => {
if (!onValuesChange) return;
const subscription = watch((values) => {
onValuesChange(values as ProviderFormInitialValues);
});
return () => subscription.unsubscribe();
}, [watch, onValuesChange]);
const [requesterList, setRequesterList] = useState< const [requesterList, setRequesterList] = useState<
{ {
+39 -3
View File
@@ -109,6 +109,17 @@ export default function WizardPage() {
const [modelsAdded, setModelsAdded] = useState(false); const [modelsAdded, setModelsAdded] = useState(false);
// Sub-layer within Step 2: 0=main, 1=sub-choice, 2=provider, 3=scan, 4=config // Sub-layer within Step 2: 0=main, 1=sub-choice, 2=provider, 3=scan, 4=config
const [step2Layer, setStep2Layer] = useState(0); const [step2Layer, setStep2Layer] = useState(0);
// Saved provider form data for back navigation
const [savedProviderForm, setSavedProviderForm] = useState<{
name?: string;
requester?: string;
base_url?: string;
api_key?: string;
}>({});
// Saved selected scanned models for back navigation
const [savedSelectedModels, setSavedSelectedModels] = useState<Set<string>>(
new Set(),
);
const [createdBotUuid, setCreatedBotUuid] = useState<string | null>(null); const [createdBotUuid, setCreatedBotUuid] = useState<string | null>(null);
const [webhookUrl, setWebhookUrl] = useState<string>(''); const [webhookUrl, setWebhookUrl] = useState<string>('');
const [extraWebhookUrl, setExtraWebhookUrl] = useState<string>(''); const [extraWebhookUrl, setExtraWebhookUrl] = useState<string>('');
@@ -334,7 +345,7 @@ export default function WizardPage() {
aiEngineMode, aiEngineMode,
selectedRunner, selectedRunner,
modelSource, modelSource,
providerCreated, modelsAdded,
]); ]);
const goNext = useCallback(() => { const goNext = useCallback(() => {
@@ -748,6 +759,10 @@ export default function WizardPage() {
saveProgress({ step: 2, selected_runner: 'local-agent' }); saveProgress({ step: 2, selected_runner: 'local-agent' });
}} }}
onResetModelSource={() => setModelSource(null)} onResetModelSource={() => setModelSource(null)}
savedProviderForm={savedProviderForm}
onSavedProviderFormChange={setSavedProviderForm}
savedSelectedModels={savedSelectedModels}
onSavedSelectedModelsChange={setSavedSelectedModels}
runnerConfigItems={selectedRunnerConfigItems} runnerConfigItems={selectedRunnerConfigItems}
runnerConfigValues={runnerConfig} runnerConfigValues={runnerConfig}
onRunnerConfigChange={setRunnerConfig} onRunnerConfigChange={setRunnerConfig}
@@ -1088,9 +1103,13 @@ function StepBotConfig({
function WizardModelScan({ function WizardModelScan({
providerUuid, providerUuid,
initialSelectedModels,
onSelectedModelsChange,
onModelsAdded, onModelsAdded,
}: { }: {
providerUuid: string | null; providerUuid: string | null;
initialSelectedModels: Set<string>;
onSelectedModelsChange: (v: Set<string>) => void;
onModelsAdded: () => void; onModelsAdded: () => void;
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -1098,7 +1117,9 @@ function WizardModelScan({
const [scannedModels, setScannedModels] = useState< const [scannedModels, setScannedModels] = useState<
{ name: string; context_length?: number | null }[] { name: string; context_length?: number | null }[]
>([]); >([]);
const [selectedModels, setSelectedModels] = useState<Set<string>>(new Set()); const [selectedModels, setSelectedModels] = useState<Set<string>>(
initialSelectedModels,
);
const [adding, setAdding] = useState(false); const [adding, setAdding] = useState(false);
const [scanDone, setScanDone] = useState(false); const [scanDone, setScanDone] = useState(false);
@@ -1133,6 +1154,7 @@ function WizardModelScan({
const next = new Set(prev); const next = new Set(prev);
if (next.has(name)) next.delete(name); if (next.has(name)) next.delete(name);
else next.add(name); else next.add(name);
onSelectedModelsChange(next);
return next; return next;
}); });
}; };
@@ -1261,6 +1283,10 @@ function StepAIEngine({
onProviderCreated, onProviderCreated,
onModelsAdded, onModelsAdded,
onResetModelSource, onResetModelSource,
savedProviderForm,
onSavedProviderFormChange,
savedSelectedModels,
onSavedSelectedModelsChange,
runnerConfigItems, runnerConfigItems,
runnerConfigValues, runnerConfigValues,
onRunnerConfigChange, onRunnerConfigChange,
@@ -1281,6 +1307,10 @@ function StepAIEngine({
onProviderCreated: (uuid?: string) => void; onProviderCreated: (uuid?: string) => void;
onModelsAdded: () => void; onModelsAdded: () => void;
onResetModelSource: () => void; onResetModelSource: () => void;
savedProviderForm: { name?: string; requester?: string; base_url?: string; api_key?: string };
onSavedProviderFormChange: (v: { name?: string; requester?: string; base_url?: string; api_key?: string }) => void;
savedSelectedModels: Set<string>;
onSavedSelectedModelsChange: (v: Set<string>) => void;
runnerConfigItems: IDynamicFormItemSchema[]; runnerConfigItems: IDynamicFormItemSchema[];
runnerConfigValues: Record<string, unknown>; runnerConfigValues: Record<string, unknown>;
onRunnerConfigChange: (v: Record<string, unknown>) => void; onRunnerConfigChange: (v: Record<string, unknown>) => void;
@@ -1481,7 +1511,11 @@ function StepAIEngine({
</div> </div>
<div className="border rounded-lg p-6 bg-card"> <div className="border rounded-lg p-6 bg-card">
<ProviderForm <ProviderForm
onFormSubmit={onProviderCreated} initialValues={savedProviderForm}
onValuesChange={onSavedProviderFormChange}
onFormSubmit={(uuid) => {
onProviderCreated(uuid);
}}
onFormCancel={onResetModelSource} onFormCancel={onResetModelSource}
/> />
</div> </div>
@@ -1494,6 +1528,8 @@ function StepAIEngine({
return ( return (
<WizardModelScan <WizardModelScan
providerUuid={createdProviderUuid} providerUuid={createdProviderUuid}
initialSelectedModels={savedSelectedModels}
onSelectedModelsChange={onSavedSelectedModelsChange}
onModelsAdded={onModelsAdded} onModelsAdded={onModelsAdded}
/> />
); );