Compare commits

...

6 Commits

Author SHA1 Message Date
Raunak Bhagat
4ca8050540 fix: remove unused Content import in NonAdminStep 2026-03-03 10:55:05 -08:00
Raunak Bhagat
85186fc58a refactor(fe): move onboarding types to interfaces/onboarding.ts
Move shared onboarding type definitions (OnboardingStep, OnboardingState,
OnboardingActions, etc.) from sections/onboarding/types.ts to
interfaces/onboarding.ts. Update all 20 import sites to use the new path.
2026-03-03 10:51:57 -08:00
Raunak Bhagat
7e37c48fb7 refactor(fe): move useShowOnboarding test to hooks/__tests__
Co-locate the test with the hook it tests. Update the relative import
for OnboardingStep to use the absolute path.
2026-03-03 10:49:34 -08:00
Raunak Bhagat
7122e5a9a7 refactor(fe): consolidate useOnboardingState into useShowOnboarding
Inline useOnboardingState into useShowOnboarding since it was the only
consumer. Delete the now-empty useOnboardingState.ts file. Update test
mocks to target the underlying dependencies instead of the removed module.
2026-03-03 10:47:16 -08:00
Raunak Bhagat
5ad3dd370f Remove dead code 2026-03-03 10:38:52 -08:00
Raunak Bhagat
66c464c29a refactor(fe): migrate onboarding to Content/ContentAction and move to sections
Replace manual icon+title+description flex layouts with opal Content and
ContentAction components in LLMStep, NameStep, and NonAdminStep. Move the
entire onboarding directory from refresh-components/ to sections/ and
update all import paths.
2026-03-03 10:21:33 -08:00
37 changed files with 387 additions and 414 deletions

View File

@@ -23,7 +23,7 @@ import { LLM_PROVIDERS_ADMIN_URL } from "@/lib/llmConfig/constants";
import {
buildInitialValues,
testApiKeyHelper,
} from "@/refresh-components/onboarding/components/llmConnectionHelpers";
} from "@/sections/onboarding/components/llmConnectionHelpers";
import OnboardingInfoPages from "@/app/craft/onboarding/components/OnboardingInfoPages";
import OnboardingUserInfo from "@/app/craft/onboarding/components/OnboardingUserInfo";
import OnboardingLlmSetup, {

View File

@@ -2,39 +2,36 @@ import React from "react";
import { renderHook, act } from "@testing-library/react";
import "@testing-library/jest-dom";
import { useShowOnboarding } from "@/hooks/useShowOnboarding";
import { OnboardingStep } from "../types";
import { OnboardingStep } from "@/interfaces/onboarding";
// Mock useOnboardingState to isolate useShowOnboarding logic
const mockActions = {
nextStep: jest.fn(),
prevStep: jest.fn(),
goToStep: jest.fn(),
setButtonActive: jest.fn(),
updateName: jest.fn(),
updateData: jest.fn(),
setLoading: jest.fn(),
setError: jest.fn(),
reset: jest.fn(),
};
let mockStepIndex = 0;
jest.mock("@/refresh-components/onboarding/useOnboardingState", () => ({
useOnboardingState: () => ({
state: {
currentStep: OnboardingStep.Welcome,
stepIndex: mockStepIndex,
totalSteps: 3,
data: {},
isButtonActive: true,
isLoading: false,
},
llmDescriptors: [],
actions: mockActions,
isLoading: false,
// Mock the underlying dependencies that useOnboardingState relies on
jest.mock("@/providers/UserProvider", () => ({
useUser: () => ({
user: null,
refreshUser: jest.fn(),
}),
}));
jest.mock("@/components/chat/ProviderContext", () => ({
useProviderStatus: () => ({
llmProviders: [],
isLoadingProviders: false,
hasProviders: false,
providerOptions: [],
refreshProviderInfo: jest.fn(),
}),
}));
jest.mock("@/hooks/useLLMProviders", () => ({
useLLMProviders: () => ({
refetch: jest.fn(),
}),
}));
jest.mock("@/lib/userSettings", () => ({
updateUserPersonalization: jest.fn().mockResolvedValue(undefined),
}));
function renderUseShowOnboarding(
overrides: {
isLoadingProviders?: boolean;
@@ -63,7 +60,6 @@ describe("useShowOnboarding", () => {
beforeEach(() => {
jest.clearAllMocks();
localStorage.clear();
mockStepIndex = 0;
});
it("returns showOnboarding=false while providers are loading", () => {
@@ -133,31 +129,6 @@ describe("useShowOnboarding", () => {
expect(result.current.showOnboarding).toBe(false);
});
it("does not self-correct when user has advanced past Welcome step", () => {
const { result, rerender } = renderUseShowOnboarding({
hasAnyProvider: false,
chatSessionsCount: 0,
userId: "user-1",
});
expect(result.current.showOnboarding).toBe(true);
// Simulate user advancing past Welcome (e.g. they configured an LLM provider)
mockStepIndex = 1;
// Re-render with same userId but provider data now available
rerender({
liveAgent: undefined,
isLoadingProviders: false,
hasAnyProvider: true,
isLoadingChatSessions: false,
chatSessionsCount: 0,
userId: "user-1",
});
// Should stay true — user is actively using onboarding
expect(result.current.showOnboarding).toBe(true);
});
it("re-evaluates when userId changes", () => {
const { result, rerender } = renderUseShowOnboarding({
hasAnyProvider: false,
@@ -207,7 +178,7 @@ describe("useShowOnboarding", () => {
expect(result.current.showOnboarding).toBe(false);
});
it("returns onboardingState and actions from useOnboardingState", () => {
it("returns onboardingState and actions", () => {
const { result } = renderUseShowOnboarding();
expect(result.current.onboardingState.currentStep).toBe(
OnboardingStep.Welcome

View File

@@ -1,13 +1,250 @@
"use client";
import { useCallback, useEffect, useRef, useState } from "react";
import { useReducer, useCallback, useEffect, useRef, useState } from "react";
import { onboardingReducer, initialState } from "@/sections/onboarding/reducer";
import {
OnboardingActions,
OnboardingActionType,
OnboardingData,
OnboardingState,
OnboardingStep,
} from "@/interfaces/onboarding";
import { WellKnownLLMProviderDescriptor } from "@/interfaces/llm";
import { updateUserPersonalization } from "@/lib/userSettings";
import { useUser } from "@/providers/UserProvider";
import { MinimalPersonaSnapshot } from "@/app/admin/agents/interfaces";
import { useOnboardingState } from "@/refresh-components/onboarding/useOnboardingState";
import { useLLMProviders } from "@/hooks/useLLMProviders";
import { useProviderStatus } from "@/components/chat/ProviderContext";
function getOnboardingCompletedKey(userId: string): string {
return `onyx:onboardingCompleted:${userId}`;
}
function useOnboardingState(liveAgent?: MinimalPersonaSnapshot): {
state: OnboardingState;
llmDescriptors: WellKnownLLMProviderDescriptor[];
actions: OnboardingActions;
isLoading: boolean;
} {
const [state, dispatch] = useReducer(onboardingReducer, initialState);
const { user, refreshUser } = useUser();
// Get provider data from ProviderContext instead of duplicating the call
const {
llmProviders,
isLoadingProviders,
hasProviders: hasLlmProviders,
providerOptions,
refreshProviderInfo,
} = useProviderStatus();
// Only fetch persona-specific providers (different endpoint)
const { refetch: refreshPersonaProviders } = useLLMProviders(liveAgent?.id);
const userName = user?.personalization?.name;
const llmDescriptors = providerOptions;
const nameUpdateTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(
null
);
// Navigate to the earliest incomplete step in the onboarding flow.
// Step order: Welcome -> Name -> LlmSetup -> Complete
// We check steps in order and stop at the first incomplete one.
useEffect(() => {
// Don't run logic until data has loaded
if (isLoadingProviders) {
return;
}
// Pre-populate state with existing data
if (userName) {
dispatch({
type: OnboardingActionType.UPDATE_DATA,
payload: { userName },
});
}
if (hasLlmProviders) {
dispatch({
type: OnboardingActionType.UPDATE_DATA,
payload: { llmProviders: (llmProviders ?? []).map((p) => p.provider) },
});
}
// Determine the earliest incomplete step
// Name step is incomplete if userName is not set
if (!userName) {
// Stay at Welcome/Name step (no dispatch needed, this is the initial state)
return;
}
// LlmSetup step is incomplete if no LLM providers are configured
if (!hasLlmProviders) {
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: false,
});
dispatch({
type: OnboardingActionType.GO_TO_STEP,
step: OnboardingStep.LlmSetup,
});
return;
}
// All steps complete - go to Complete step
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: true,
});
dispatch({
type: OnboardingActionType.GO_TO_STEP,
step: OnboardingStep.Complete,
});
}, [llmProviders, isLoadingProviders]);
const nextStep = useCallback(() => {
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: false,
});
if (state.currentStep === OnboardingStep.Name) {
const hasProviders = (state.data.llmProviders?.length ?? 0) > 0;
if (hasProviders) {
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: true,
});
} else {
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: false,
});
}
}
if (state.currentStep === OnboardingStep.LlmSetup) {
refreshProviderInfo();
if (liveAgent) {
refreshPersonaProviders();
}
}
dispatch({ type: OnboardingActionType.NEXT_STEP });
}, [state, refreshProviderInfo, llmProviders, refreshPersonaProviders]);
const prevStep = useCallback(() => {
dispatch({ type: OnboardingActionType.PREV_STEP });
}, []);
const goToStep = useCallback(
(step: OnboardingStep) => {
const hasProviders = state.data.llmProviders?.length || 0 > 0;
if (step === OnboardingStep.LlmSetup && hasProviders) {
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: true,
});
} else if (step === OnboardingStep.LlmSetup) {
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: false,
});
}
dispatch({ type: OnboardingActionType.GO_TO_STEP, step });
},
[llmProviders]
);
const updateName = useCallback(
(name: string) => {
dispatch({
type: OnboardingActionType.UPDATE_DATA,
payload: { userName: name },
});
if (nameUpdateTimeoutRef.current) {
clearTimeout(nameUpdateTimeoutRef.current);
}
if (name === "") {
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: false,
});
} else {
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: true,
});
}
nameUpdateTimeoutRef.current = setTimeout(async () => {
try {
await updateUserPersonalization({ name });
await refreshUser();
} catch (_e) {
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: false,
});
console.error("Error updating user name:", _e);
} finally {
nameUpdateTimeoutRef.current = null;
}
}, 500);
},
[refreshUser]
);
const updateData = useCallback((data: Partial<OnboardingData>) => {
dispatch({ type: OnboardingActionType.UPDATE_DATA, payload: data });
}, []);
const setLoading = useCallback((isLoading: boolean) => {
dispatch({ type: OnboardingActionType.SET_LOADING, isLoading });
}, []);
const setButtonActive = useCallback((active: boolean) => {
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: active,
});
}, []);
const setError = useCallback((error: string | undefined) => {
dispatch({ type: OnboardingActionType.SET_ERROR, error });
}, []);
const reset = useCallback(() => {
dispatch({ type: OnboardingActionType.RESET });
}, []);
useEffect(() => {
return () => {
if (nameUpdateTimeoutRef.current) {
clearTimeout(nameUpdateTimeoutRef.current);
}
};
}, []);
return {
state,
llmDescriptors,
actions: {
nextStep,
prevStep,
goToStep,
setButtonActive,
updateName,
updateData,
setLoading,
setError,
reset,
},
isLoading: isLoadingProviders || !!liveAgent,
};
}
interface UseShowOnboardingParams {
liveAgent: MinimalPersonaSnapshot | undefined;
isLoadingProviders: boolean;

View File

@@ -1,240 +0,0 @@
import { useReducer, useCallback, useEffect, useRef } from "react";
import { onboardingReducer, initialState } from "./reducer";
import {
OnboardingActions,
OnboardingActionType,
OnboardingData,
OnboardingState,
OnboardingStep,
} from "./types";
import { WellKnownLLMProviderDescriptor } from "@/interfaces/llm";
import { updateUserPersonalization } from "@/lib/userSettings";
import { useUser } from "@/providers/UserProvider";
import { MinimalPersonaSnapshot } from "@/app/admin/agents/interfaces";
import { useLLMProviders } from "@/hooks/useLLMProviders";
import { useProviderStatus } from "@/components/chat/ProviderContext";
export function useOnboardingState(liveAgent?: MinimalPersonaSnapshot): {
state: OnboardingState;
llmDescriptors: WellKnownLLMProviderDescriptor[];
actions: OnboardingActions;
isLoading: boolean;
} {
const [state, dispatch] = useReducer(onboardingReducer, initialState);
const { user, refreshUser } = useUser();
// Get provider data from ProviderContext instead of duplicating the call
const {
llmProviders,
isLoadingProviders,
hasProviders: hasLlmProviders,
providerOptions,
refreshProviderInfo,
} = useProviderStatus();
// Only fetch persona-specific providers (different endpoint)
const { refetch: refreshPersonaProviders } = useLLMProviders(liveAgent?.id);
const userName = user?.personalization?.name;
const llmDescriptors = providerOptions;
const nameUpdateTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(
null
);
// Navigate to the earliest incomplete step in the onboarding flow.
// Step order: Welcome -> Name -> LlmSetup -> Complete
// We check steps in order and stop at the first incomplete one.
useEffect(() => {
// Don't run logic until data has loaded
if (isLoadingProviders) {
return;
}
// Pre-populate state with existing data
if (userName) {
dispatch({
type: OnboardingActionType.UPDATE_DATA,
payload: { userName },
});
}
if (hasLlmProviders) {
dispatch({
type: OnboardingActionType.UPDATE_DATA,
payload: { llmProviders: (llmProviders ?? []).map((p) => p.provider) },
});
}
// Determine the earliest incomplete step
// Name step is incomplete if userName is not set
if (!userName) {
// Stay at Welcome/Name step (no dispatch needed, this is the initial state)
return;
}
// LlmSetup step is incomplete if no LLM providers are configured
if (!hasLlmProviders) {
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: false,
});
dispatch({
type: OnboardingActionType.GO_TO_STEP,
step: OnboardingStep.LlmSetup,
});
return;
}
// All steps complete - go to Complete step
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: true,
});
dispatch({
type: OnboardingActionType.GO_TO_STEP,
step: OnboardingStep.Complete,
});
}, [llmProviders, isLoadingProviders]);
const nextStep = useCallback(() => {
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: false,
});
if (state.currentStep === OnboardingStep.Name) {
const hasProviders = (state.data.llmProviders?.length ?? 0) > 0;
if (hasProviders) {
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: true,
});
} else {
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: false,
});
}
}
if (state.currentStep === OnboardingStep.LlmSetup) {
refreshProviderInfo();
if (liveAgent) {
refreshPersonaProviders();
}
}
dispatch({ type: OnboardingActionType.NEXT_STEP });
}, [state, refreshProviderInfo, llmProviders, refreshPersonaProviders]);
const prevStep = useCallback(() => {
dispatch({ type: OnboardingActionType.PREV_STEP });
}, []);
const goToStep = useCallback(
(step: OnboardingStep) => {
const hasProviders = state.data.llmProviders?.length || 0 > 0;
if (step === OnboardingStep.LlmSetup && hasProviders) {
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: true,
});
} else if (step === OnboardingStep.LlmSetup) {
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: false,
});
}
dispatch({ type: OnboardingActionType.GO_TO_STEP, step });
},
[llmProviders]
);
const updateName = useCallback(
(name: string) => {
dispatch({
type: OnboardingActionType.UPDATE_DATA,
payload: { userName: name },
});
if (nameUpdateTimeoutRef.current) {
clearTimeout(nameUpdateTimeoutRef.current);
}
if (name === "") {
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: false,
});
} else {
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: true,
});
}
nameUpdateTimeoutRef.current = setTimeout(async () => {
try {
await updateUserPersonalization({ name });
await refreshUser();
} catch (_e) {
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: false,
});
console.error("Error updating user name:", _e);
} finally {
nameUpdateTimeoutRef.current = null;
}
}, 500);
},
[refreshUser]
);
const updateData = useCallback((data: Partial<OnboardingData>) => {
dispatch({ type: OnboardingActionType.UPDATE_DATA, payload: data });
}, []);
const setLoading = useCallback((isLoading: boolean) => {
dispatch({ type: OnboardingActionType.SET_LOADING, isLoading });
}, []);
const setButtonActive = useCallback((active: boolean) => {
dispatch({
type: OnboardingActionType.SET_BUTTON_ACTIVE,
isButtonActive: active,
});
}, []);
const setError = useCallback((error: string | undefined) => {
dispatch({ type: OnboardingActionType.SET_ERROR, error });
}, []);
const reset = useCallback(() => {
dispatch({ type: OnboardingActionType.RESET });
}, []);
useEffect(() => {
return () => {
if (nameUpdateTimeoutRef.current) {
clearTimeout(nameUpdateTimeoutRef.current);
}
};
}, []);
return {
state,
llmDescriptors,
actions: {
nextStep,
prevStep,
goToStep,
setButtonActive,
updateName,
updateData,
setLoading,
setError,
reset,
},
isLoading: isLoadingProviders || !!liveAgent,
};
}

View File

@@ -60,8 +60,8 @@ import {
import ProjectChatSessionList from "@/app/app/components/projects/ProjectChatSessionList";
import { cn } from "@/lib/utils";
import Suggestions from "@/sections/Suggestions";
import OnboardingFlow from "@/refresh-components/onboarding/OnboardingFlow";
import { OnboardingStep } from "@/refresh-components/onboarding/types";
import OnboardingFlow from "@/sections/onboarding/OnboardingFlow";
import { OnboardingStep } from "@/interfaces/onboarding";
import { useShowOnboarding } from "@/hooks/useShowOnboarding";
import * as AppLayouts from "@/layouts/app-layouts";
import { SvgChevronDown, SvgFileText } from "@opal/icons";

View File

@@ -51,7 +51,7 @@ import {
SvgStop,
SvgX,
} from "@opal/icons";
import { Button, OpenButton } from "@opal/components";
import { Button } from "@opal/components";
import Popover from "@/refresh-components/Popover";
import SimpleLoader from "@/refresh-components/loaders/SimpleLoader";
import { useQueryController } from "@/providers/QueryControllerProvider";

View File

@@ -3,7 +3,11 @@ import OnboardingHeader from "./components/OnboardingHeader";
import NameStep from "./steps/NameStep";
import LLMStep from "./steps/LLMStep";
import FinalStep from "./steps/FinalStep";
import { OnboardingActions, OnboardingState, OnboardingStep } from "./types";
import {
OnboardingActions,
OnboardingState,
OnboardingStep,
} from "@/interfaces/onboarding";
import { WellKnownLLMProviderDescriptor } from "@/interfaces/llm";
import { useUser } from "@/providers/UserProvider";
import { UserRole } from "@/lib/types";

View File

@@ -3,7 +3,7 @@ import {
OnboardingActionType,
OnboardingStep,
OnboardingState,
} from "../types";
} from "@/interfaces/onboarding";
describe("onboardingReducer", () => {
describe("initial state", () => {

View File

@@ -9,6 +9,7 @@ import { Button as OpalButton } from "@opal/components";
import InputAvatar from "@/refresh-components/inputs/InputAvatar";
import { cn } from "@/lib/utils";
import { SvgCheckCircle, SvgEdit, SvgUser, SvgX } from "@opal/icons";
import { ContentAction } from "@opal/layouts";
export default function NonAdminStep() {
const inputRef = useRef<HTMLInputElement>(null);
@@ -54,17 +55,21 @@ export default function NonAdminStep() {
className="flex items-center justify-between w-full min-h-11 py-1 pl-3 pr-2 bg-background-tint-00 rounded-16 shadow-01 mb-2"
aria-label="non-admin-confirmation"
>
<div className="flex items-center gap-1">
<SvgCheckCircle className="w-4 h-4 stroke-status-success-05" />
<Text as="p" text03 mainUiBody>
You're all set!
</Text>
</div>
<OpalButton
prominence="tertiary"
size="sm"
icon={SvgX}
onClick={handleDismissConfirmation}
<ContentAction
icon={SvgCheckCircle}
title="You're all set!"
sizePreset="main-ui"
variant="body"
prominence="muted"
paddingVariant="fit"
rightChildren={
<OpalButton
prominence="tertiary"
size="sm"
icon={SvgX}
onClick={handleDismissConfirmation}
/>
}
/>
</div>
)}
@@ -75,39 +80,36 @@ export default function NonAdminStep() {
role="group"
aria-label="non-admin-name-prompt"
>
<div className="flex items-center gap-1 h-full">
<div className="h-full p-0.5">
<SvgUser className="w-4 h-4 stroke-text-03" />
</div>
<div>
<Text as="p" text04 mainUiAction>
What should Onyx call you?
</Text>
<Text as="p" text03 secondaryBody>
We will display this name in the app.
</Text>
</div>
</div>
<div className="flex items-center justify-end gap-2">
<InputTypeIn
ref={inputRef}
placeholder="Your name"
value={name || ""}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setName(e.target.value)
}
onKeyDown={(e) => {
if (e.key === "Enter" && name && name.trim().length > 0) {
e.preventDefault();
handleSave();
}
}}
className="w-[26%] min-w-40"
/>
<Button disabled={name === ""} onClick={handleSave}>
Save
</Button>
</div>
<ContentAction
icon={SvgUser}
title="What should Onyx call you?"
description="We will display this name in the app."
sizePreset="main-ui"
variant="section"
paddingVariant="fit"
rightChildren={
<div className="flex items-center justify-end gap-2">
<InputTypeIn
ref={inputRef}
placeholder="Your name"
value={name || ""}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setName(e.target.value)
}
onKeyDown={(e) => {
if (e.key === "Enter" && name && name.trim().length > 0) {
e.preventDefault();
handleSave();
}
}}
className="w-[26%] min-w-40"
/>
<Button disabled={name === ""} onClick={handleSave}>
Save
</Button>
</div>
}
/>
</div>
) : (
<div

View File

@@ -1,10 +1,10 @@
import React from "react";
import { STEP_CONFIG } from "@/refresh-components/onboarding/constants";
import { STEP_CONFIG } from "@/sections/onboarding/constants";
import {
OnboardingActions,
OnboardingState,
OnboardingStep,
} from "@/refresh-components/onboarding/types";
} from "@/interfaces/onboarding";
import Text from "@/refresh-components/texts/Text";
import Button from "@/refresh-components/buttons/Button";
import { Button as OpalButton } from "@opal/components";

View File

@@ -1,7 +1,4 @@
import {
OnboardingStep,
FinalStepItemProps,
} from "@/refresh-components/onboarding/types";
import { OnboardingStep, FinalStepItemProps } from "@/interfaces/onboarding";
import { SvgGlobe, SvgImage, SvgUsers } from "@opal/icons";
type StepConfig = {

View File

@@ -13,7 +13,7 @@ import {
OnboardingFormWrapper,
OnboardingFormChildProps,
} from "./OnboardingFormWrapper";
import { OnboardingActions, OnboardingState } from "../types";
import { OnboardingActions, OnboardingState } from "@/interfaces/onboarding";
import { buildInitialValues } from "../components/llmConnectionHelpers";
import ConnectionProviderIcon from "@/refresh-components/ConnectionProviderIcon";
import InlineExternalLink from "@/refresh-components/InlineExternalLink";

View File

@@ -11,7 +11,7 @@ import {
OnboardingFormWrapper,
OnboardingFormChildProps,
} from "./OnboardingFormWrapper";
import { OnboardingActions, OnboardingState } from "../types";
import { OnboardingActions, OnboardingState } from "@/interfaces/onboarding";
import { buildInitialValues } from "../components/llmConnectionHelpers";
import ConnectionProviderIcon from "@/refresh-components/ConnectionProviderIcon";
import InlineExternalLink from "@/refresh-components/InlineExternalLink";

View File

@@ -16,7 +16,7 @@ import {
OnboardingFormWrapper,
OnboardingFormChildProps,
} from "./OnboardingFormWrapper";
import { OnboardingActions, OnboardingState } from "../types";
import { OnboardingActions, OnboardingState } from "@/interfaces/onboarding";
import { buildInitialValues } from "../components/llmConnectionHelpers";
import ConnectionProviderIcon from "@/refresh-components/ConnectionProviderIcon";
import InlineExternalLink from "@/refresh-components/InlineExternalLink";

View File

@@ -14,7 +14,7 @@ import {
OnboardingFormWrapper,
OnboardingFormChildProps,
} from "./OnboardingFormWrapper";
import { OnboardingActions, OnboardingState } from "../types";
import { OnboardingActions, OnboardingState } from "@/interfaces/onboarding";
import { buildInitialValues } from "../components/llmConnectionHelpers";
import ConnectionProviderIcon from "@/refresh-components/ConnectionProviderIcon";

View File

@@ -17,7 +17,7 @@ import {
OnboardingFormWrapper,
OnboardingFormChildProps,
} from "./OnboardingFormWrapper";
import { OnboardingActions, OnboardingState } from "../types";
import { OnboardingActions, OnboardingState } from "@/interfaces/onboarding";
import { buildInitialValues } from "../components/llmConnectionHelpers";
import ConnectionProviderIcon from "@/refresh-components/ConnectionProviderIcon";
import InlineExternalLink from "@/refresh-components/InlineExternalLink";

View File

@@ -10,7 +10,7 @@ import {
LLM_ADMIN_URL,
LLM_PROVIDERS_ADMIN_URL,
} from "@/lib/llmConfig/constants";
import { OnboardingActions, OnboardingState } from "../types";
import { OnboardingActions, OnboardingState } from "@/interfaces/onboarding";
import { APIFormFieldState } from "@/refresh-components/form/types";
import {
testApiKeyHelper,

View File

@@ -13,7 +13,7 @@ import {
OnboardingFormWrapper,
OnboardingFormChildProps,
} from "./OnboardingFormWrapper";
import { OnboardingActions, OnboardingState } from "../types";
import { OnboardingActions, OnboardingState } from "@/interfaces/onboarding";
import { buildInitialValues } from "../components/llmConnectionHelpers";
import ConnectionProviderIcon from "@/refresh-components/ConnectionProviderIcon";
import InlineExternalLink from "@/refresh-components/InlineExternalLink";

View File

@@ -13,7 +13,7 @@ import {
OnboardingFormWrapper,
OnboardingFormChildProps,
} from "./OnboardingFormWrapper";
import { OnboardingActions, OnboardingState } from "../types";
import { OnboardingActions, OnboardingState } from "@/interfaces/onboarding";
import { buildInitialValues } from "../components/llmConnectionHelpers";
import ConnectionProviderIcon from "@/refresh-components/ConnectionProviderIcon";
import InlineExternalLink from "@/refresh-components/InlineExternalLink";

View File

@@ -15,7 +15,7 @@ import {
OnboardingFormWrapper,
OnboardingFormChildProps,
} from "./OnboardingFormWrapper";
import { OnboardingActions, OnboardingState } from "../types";
import { OnboardingActions, OnboardingState } from "@/interfaces/onboarding";
import {
buildInitialValues,
testApiKeyHelper,

View File

@@ -14,7 +14,7 @@ import {
OnboardingState,
OnboardingActions,
OnboardingStep,
} from "../../types";
} from "@/interfaces/onboarding";
/**
* Creates a mock WellKnownLLMProviderDescriptor for testing

View File

@@ -3,7 +3,7 @@ import {
WellKnownLLMProviderDescriptor,
LLMProviderName,
} from "@/interfaces/llm";
import { OnboardingActions, OnboardingState } from "../types";
import { OnboardingActions, OnboardingState } from "@/interfaces/onboarding";
import { OpenAIOnboardingForm } from "./OpenAIOnboardingForm";
import { AnthropicOnboardingForm } from "./AnthropicOnboardingForm";
import { OllamaOnboardingForm } from "./OllamaOnboardingForm";

View File

@@ -3,7 +3,7 @@ import {
OnboardingAction,
OnboardingActionType,
OnboardingStep,
} from "./types";
} from "@/interfaces/onboarding";
import { STEP_NAVIGATION, STEP_CONFIG, TOTAL_STEPS } from "./constants";
export const initialState: OnboardingState = {

View File

@@ -2,8 +2,8 @@ import React from "react";
import Link from "next/link";
import type { Route } from "next";
import Button from "@/refresh-components/buttons/Button";
import { FINAL_SETUP_CONFIG } from "@/refresh-components/onboarding/constants";
import { FinalStepItemProps } from "@/refresh-components/onboarding/types";
import { FINAL_SETUP_CONFIG } from "@/sections/onboarding/constants";
import { FinalStepItemProps } from "@/interfaces/onboarding";
import { SvgExternalLink } from "@opal/icons";
import { Section } from "@/layouts/general-layouts";
import { ContentAction } from "@opal/layouts";

View File

@@ -3,7 +3,11 @@ import Text from "@/refresh-components/texts/Text";
import Button from "@/refresh-components/buttons/Button";
import Separator from "@/refresh-components/Separator";
import LLMProviderCard from "../components/LLMProviderCard";
import { OnboardingActions, OnboardingState, OnboardingStep } from "../types";
import {
OnboardingActions,
OnboardingState,
OnboardingStep,
} from "@/interfaces/onboarding";
import { WellKnownLLMProviderDescriptor } from "@/interfaces/llm";
import {
getOnboardingForm,
@@ -12,6 +16,7 @@ import {
import { Disabled } from "@/refresh-components/Disabled";
import { ProviderIcon } from "@/app/admin/configuration/llm/ProviderIcon";
import { SvgCheckCircle, SvgCpu, SvgExternalLink } from "@opal/icons";
import { ContentAction } from "@opal/layouts";
type LLMStepProps = {
state: OnboardingState;
@@ -122,31 +127,26 @@ const LLMStepInner = ({
className="flex flex-col items-center justify-between w-full p-1 rounded-16 border border-border-01 bg-background-tint-00"
aria-label="onboarding-llm-step"
>
<div className="flex gap-2 justify-between h-full w-full">
<div className="flex mx-2 mt-2 gap-1">
<div className="h-full p-0.5">
<SvgCpu className="w-4 h-4 stroke-text-03" />
<ContentAction
icon={SvgCpu}
title="Connect your LLM models"
description="Onyx supports both self-hosted models and popular providers."
sizePreset="main-ui"
variant="section"
paddingVariant="lg"
rightChildren={
<div className="p-0.5">
<Button
tertiary
rightIcon={SvgExternalLink}
disabled={disabled}
href="admin/configuration/llm"
>
View in Admin Panel
</Button>
</div>
<div>
<Text as="p" text04 mainUiAction>
Connect your LLM models
</Text>
<Text as="p" text03 secondaryBody>
Onyx supports both self-hosted models and popular providers.
</Text>
</div>
</div>
<div className="p-0.5">
<Button
tertiary
rightIcon={SvgExternalLink}
disabled={disabled}
href="admin/configuration/llm"
>
View in Admin Panel
</Button>
</div>
</div>
}
/>
<Separator />
<div className="flex flex-wrap gap-1 [&>*:last-child:nth-child(odd)]:basis-full">
{isLoading ? (

View File

@@ -3,11 +3,16 @@
import React, { useRef } from "react";
import Text from "@/refresh-components/texts/Text";
import InputTypeIn from "@/refresh-components/inputs/InputTypeIn";
import { OnboardingState, OnboardingActions, OnboardingStep } from "../types";
import {
OnboardingState,
OnboardingActions,
OnboardingStep,
} from "@/interfaces/onboarding";
import InputAvatar from "@/refresh-components/inputs/InputAvatar";
import { cn } from "@/lib/utils";
import IconButton from "@/refresh-components/buttons/IconButton";
import { SvgCheckCircle, SvgEdit, SvgUser } from "@opal/icons";
import { ContentAction } from "@opal/layouts";
export interface NameStepProps {
state: OnboardingState;
@@ -40,26 +45,23 @@ const NameStep = React.memo(
role="group"
aria-label="onboarding-name-step"
>
<div className="flex items-center gap-1 h-full">
<div className="h-full p-0.5">
<SvgUser className="w-4 h-4 stroke-text-03" />
</div>
<div>
<Text as="p" text04 mainUiAction>
What should Onyx call you?
</Text>
<Text as="p" text03 secondaryBody>
We will display this name in the app.
</Text>
</div>
</div>
<InputTypeIn
ref={inputRef}
placeholder="Your name"
value={userName || ""}
onChange={(e) => updateName(e.target.value)}
onKeyDown={handleKeyDown}
className="max-w-60"
<ContentAction
icon={SvgUser}
title="What should Onyx call you?"
description="We will display this name in the app."
sizePreset="main-ui"
variant="section"
paddingVariant="fit"
rightChildren={
<InputTypeIn
ref={inputRef}
placeholder="Your name"
value={userName || ""}
onChange={(e) => updateName(e.target.value)}
onKeyDown={handleKeyDown}
className="max-w-60"
/>
}
/>
</div>
) : (