feat(FN-1940): add onboarding first-task success handoff

- Track the first task created from onboarding in AppModals and pass it back into the onboarding modal
- Keep onboarding open after launching New Task, then switch to a task-created success state when a task is returned
- Add success actions to view the created task or return to the dashboard, while preserving the no-task completion path
- Style the task-created success panel and add comprehensive ModelOnboardingModal tests for transition, rendering, and button behavior
This commit is contained in:
Fusion
2026-04-17 04:23:27 -07:00
committed by gsxdsm
parent 69d38b620e
commit c36203d162
4 changed files with 460 additions and 54 deletions

View File

@@ -1,3 +1,4 @@
import { useCallback, useEffect, useState } from "react";
import type { ProjectInfo } from "../api";
import type { ColorTheme, Column, MergeResult, Task, TaskCreateInput, ThemeMode } from "@fusion/core";
import type { UseProjectActionsResult } from "../hooks/useProjectActions";
@@ -75,9 +76,42 @@ export function AppModals({
onSettingsClose,
onReopenOnboarding,
}: AppModalsProps) {
const [firstCreatedTask, setFirstCreatedTask] = useState<Task | null>(null);
// Use the override handler if provided, otherwise fall back to modalManager.closeSettings
const handleSettingsClose = onSettingsClose ?? modalManager.closeSettings;
const handleOpenNewTask = useCallback(() => {
modalManager.openNewTask();
}, [modalManager]);
const handleOpenGitHubImport = useCallback(() => {
modalManager.openGitHubImport();
}, [modalManager]);
const handleOnboardingViewTask = useCallback((task: Task) => {
setFirstCreatedTask(null);
modalManager.closeModelOnboarding();
modalManager.openDetailTask(task);
}, [modalManager]);
const handleModalCreateWithOnboardingTracking = useCallback(
async (input: TaskCreateInput): Promise<Task> => {
const task = await taskHandlers.handleModalCreate(input);
if (modalManager.modelOnboardingOpen) {
setFirstCreatedTask(task);
}
return task;
},
[taskHandlers.handleModalCreate, modalManager.modelOnboardingOpen],
);
useEffect(() => {
if (!modalManager.modelOnboardingOpen && firstCreatedTask) {
setFirstCreatedTask(null);
}
}, [modalManager.modelOnboardingOpen, firstCreatedTask]);
return (
<>
{modalManager.detailTask && (
@@ -193,7 +227,7 @@ export function AppModals({
isOpen={modalManager.newTaskModalOpen}
onClose={modalManager.closeNewTask}
tasks={tasks}
onCreateTask={taskHandlers.handleModalCreate}
onCreateTask={handleModalCreateWithOnboardingTracking}
addToast={addToast}
projectId={projectId}
onPlanningMode={modalManager.openPlanningWithInitialPlan}
@@ -253,6 +287,10 @@ export function AppModals({
<ModelOnboardingModal
onComplete={projectActions.handleModelOnboardingComplete}
addToast={addToast}
onOpenNewTask={handleOpenNewTask}
onOpenGitHubImport={handleOpenGitHubImport}
firstCreatedTask={firstCreatedTask}
onViewTask={handleOnboardingViewTask}
/>
)}