fix(FN-2404): scope onboarding first-task creation to active project
- Guard onboarding first-task creation when no project is selected - Pass projectId to createTask so onboarding tasks are created in the active project - Update AppModals wiring tests to verify projectId forwarding for selected and unselected states - Expand onboarding modal and flow regression tests to assert project-scoped task creation calls
This commit is contained in:
@@ -1197,6 +1197,10 @@ export function ModelOnboardingModal({
|
|||||||
}, [completeOnboarding, completedSteps, skippedSteps]);
|
}, [completeOnboarding, completedSteps, skippedSteps]);
|
||||||
|
|
||||||
const handleCreateFirstTask = useCallback(async () => {
|
const handleCreateFirstTask = useCallback(async () => {
|
||||||
|
if (!projectId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const trimmedDescription = firstTaskDescription.trim();
|
const trimmedDescription = firstTaskDescription.trim();
|
||||||
if (!trimmedDescription) {
|
if (!trimmedDescription) {
|
||||||
setTaskCreationError("Please enter a task description.");
|
setTaskCreationError("Please enter a task description.");
|
||||||
@@ -1209,7 +1213,7 @@ export function ModelOnboardingModal({
|
|||||||
let success = false;
|
let success = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const createdTask = await createTask({ description: trimmedDescription });
|
const createdTask = await createTask({ description: trimmedDescription }, projectId);
|
||||||
setInlineCreatedTask(createdTask);
|
setInlineCreatedTask(createdTask);
|
||||||
setShowTaskCreated(true);
|
setShowTaskCreated(true);
|
||||||
trackOnboardingEvent("onboarding:first-task-created", { taskId: createdTask?.id });
|
trackOnboardingEvent("onboarding:first-task-created", { taskId: createdTask?.id });
|
||||||
@@ -1229,7 +1233,7 @@ export function ModelOnboardingModal({
|
|||||||
if (success) {
|
if (success) {
|
||||||
void completeOnboarding();
|
void completeOnboarding();
|
||||||
}
|
}
|
||||||
}, [firstTaskDescription, addToast, completeOnboarding]);
|
}, [projectId, firstTaskDescription, addToast, completeOnboarding]);
|
||||||
|
|
||||||
// Handle first task CTA - mark complete, close modal, then open new task
|
// Handle first task CTA - mark complete, close modal, then open new task
|
||||||
const handleOpenNewTask = useCallback(async () => {
|
const handleOpenNewTask = useCallback(async () => {
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ describe("AppModals", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("ModelOnboardingModal wiring", () => {
|
describe("ModelOnboardingModal wiring", () => {
|
||||||
it("passes project context and setup-wizard callback into onboarding modal", () => {
|
it("passes empty project id and setup-wizard callback into onboarding modal when no project is selected", () => {
|
||||||
const handleAddProject = vi.fn();
|
const handleAddProject = vi.fn();
|
||||||
const manager = { ...mockModalManager, modelOnboardingOpen: true };
|
const manager = { ...mockModalManager, modelOnboardingOpen: true };
|
||||||
|
|
||||||
@@ -240,6 +240,32 @@ describe("AppModals", () => {
|
|||||||
expect(props.projectId).toBe("");
|
expect(props.projectId).toBe("");
|
||||||
expect(props.onOpenSetupWizard).toBe(handleAddProject);
|
expect(props.onOpenSetupWizard).toBe(handleAddProject);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("passes active project id into onboarding modal when a project is selected", () => {
|
||||||
|
const manager = { ...mockModalManager, modelOnboardingOpen: true };
|
||||||
|
|
||||||
|
render(
|
||||||
|
<AppModals
|
||||||
|
projectId="proj_123"
|
||||||
|
tasks={[]}
|
||||||
|
projects={[]}
|
||||||
|
currentProject={null}
|
||||||
|
addToast={vi.fn()}
|
||||||
|
toasts={mockToasts}
|
||||||
|
removeToast={vi.fn()}
|
||||||
|
modalManager={manager}
|
||||||
|
projectActions={{ handleAddProject: vi.fn(), handleSetupComplete: vi.fn(), handleModelOnboardingComplete: vi.fn() }}
|
||||||
|
taskHandlers={{ handleModalCreate: vi.fn(), handlePlanningTaskCreated: vi.fn(), handlePlanningTasksCreated: vi.fn(), handleSubtaskTasksCreated: vi.fn(), handleGitHubImport: vi.fn() }}
|
||||||
|
taskOperations={{ moveTask: vi.fn(), deleteTask: vi.fn(), mergeTask: vi.fn(), retryTask: vi.fn(), duplicateTask: vi.fn() }}
|
||||||
|
deepLink={{ handleDetailClose: vi.fn() }}
|
||||||
|
settings={mockSettings}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mockModelOnboardingModalProps).toHaveBeenCalledTimes(1);
|
||||||
|
const props = mockModelOnboardingModalProps.mock.calls[0][0];
|
||||||
|
expect(props.projectId).toBe("proj_123");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("ScheduledTasksModal projectId forwarding", () => {
|
describe("ScheduledTasksModal projectId forwarding", () => {
|
||||||
|
|||||||
@@ -1476,6 +1476,16 @@ describe("ModelOnboardingModal", () => {
|
|||||||
|
|
||||||
expect(screen.queryByTestId("onboarding-task-error")).toBeNull();
|
expect(screen.queryByTestId("onboarding-task-error")).toBeNull();
|
||||||
expect(mockCreateTask).toHaveBeenCalledTimes(2);
|
expect(mockCreateTask).toHaveBeenCalledTimes(2);
|
||||||
|
expect(mockCreateTask).toHaveBeenNthCalledWith(
|
||||||
|
1,
|
||||||
|
{ description: "Build auth" },
|
||||||
|
"proj_123",
|
||||||
|
);
|
||||||
|
expect(mockCreateTask).toHaveBeenNthCalledWith(
|
||||||
|
2,
|
||||||
|
{ description: "Build auth" },
|
||||||
|
"proj_123",
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("disables first-task submit button while creating the task", async () => {
|
it("disables first-task submit button while creating the task", async () => {
|
||||||
|
|||||||
@@ -762,7 +762,10 @@ describe("onboarding flow integration", () => {
|
|||||||
await simulateFirstTaskCreation(renderResult, "Ship onboarding telemetry");
|
await simulateFirstTaskCreation(renderResult, "Ship onboarding telemetry");
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(mockCreateTask).toHaveBeenCalledWith({ description: "Ship onboarding telemetry" });
|
expect(mockCreateTask).toHaveBeenCalledWith(
|
||||||
|
{ description: "Ship onboarding telemetry" },
|
||||||
|
"proj_123",
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(mockMarkOnboardingCompleted).toHaveBeenCalled();
|
expect(mockMarkOnboardingCompleted).toHaveBeenCalled();
|
||||||
|
|||||||
Reference in New Issue
Block a user