create new onboarding experience
This commit is contained in:
parent
391d2671a0
commit
71edefc710
12 changed files with 349 additions and 85 deletions
|
|
@ -1,6 +1,12 @@
|
|||
const DogIcon = (props: React.SVGProps<SVGSVGElement>) => {
|
||||
interface DogIconProps extends React.SVGProps<SVGSVGElement> {
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const DogIcon = ({ disabled = false, stroke, ...props }: DogIconProps) => {
|
||||
const strokeColor = disabled ? "#71717A" : (stroke || "#0F62FE");
|
||||
|
||||
return (
|
||||
<svg
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
|
|
@ -10,7 +16,7 @@ const DogIcon = (props: React.SVGProps<SVGSVGElement>) => {
|
|||
>
|
||||
<path
|
||||
d="M19.9049 23H17.907C17.907 23 15.4096 20.5 16.908 16C17.3753 14.2544 17.3813 12.4181 17.2439 11C17.161 10.1434 17.0256 9.43934 16.908 9C16.7416 8.33333 16.8081 7 18.4065 7C19.5457 7 20.9571 6.92944 21.4034 6.5C22.3268 5.61145 21.9029 4 21.9029 4C21.9029 4 20.9039 3 18.906 3C18.7395 2.33333 17.7072 1 14.9101 1C12.113 1 11.5835 2.16589 10.9143 4C10.4155 5.36686 10.423 6.99637 11.1692 7.71747M14.4106 4C14.2441 5.33333 14.4106 8 11.9132 8C11.5968 8 11.3534 7.89548 11.1692 7.71747M14.9101 23H12.4127M7.91738 23H12.4127M10.4148 15.5C11.5715 16.1667 13.5905 18.6 12.4127 23M3.42204 15C1.02177 18.5 1.64205 23 5.41997 23C5.41997 22 5.71966 19.2 6.91841 16C8.41686 12 11.1692 11.4349 11.1692 7.71747M16.908 4V4.5"
|
||||
stroke="#0F62FE"
|
||||
stroke={strokeColor}
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { Bot, GitBranch } from "lucide-react";
|
||||
import { MarkdownRenderer } from "@/components/markdown-renderer";
|
||||
import { FunctionCalls } from "./function-calls";
|
||||
import { Message } from "./message";
|
||||
import type { FunctionCall } from "../types";
|
||||
import DogIcon from "@/components/logo/dog-icon";
|
||||
|
||||
|
|
@ -29,24 +30,14 @@ export function AssistantMessage({
|
|||
const IconComponent = updatedOnboarding ? DogIcon : Bot;
|
||||
|
||||
return (
|
||||
<div className="flex gap-3">
|
||||
<div className="w-8 h-8 rounded-lg bg-accent/20 flex items-center justify-center flex-shrink-0 select-none">
|
||||
<IconComponent className="h-4 w-4 text-accent-foreground" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<FunctionCalls
|
||||
functionCalls={functionCalls}
|
||||
messageIndex={messageIndex}
|
||||
expandedFunctionCalls={expandedFunctionCalls}
|
||||
onToggle={onToggle}
|
||||
/>
|
||||
<MarkdownRenderer chatMessage={content} />
|
||||
{isStreaming && (
|
||||
<span className="inline-block w-2 h-4 bg-blue-400 ml-1 animate-pulse"></span>
|
||||
)}
|
||||
</div>
|
||||
{showForkButton && onFork && (
|
||||
<div className="flex-shrink-0 ml-2">
|
||||
<Message
|
||||
icon={
|
||||
<div className="w-8 h-8 rounded-lg bg-accent/20 flex items-center justify-center flex-shrink-0 select-none">
|
||||
<IconComponent className="h-4 w-4 text-accent-foreground" />
|
||||
</div>
|
||||
}
|
||||
actions={
|
||||
showForkButton && onFork ? (
|
||||
<button
|
||||
onClick={onFork}
|
||||
className="opacity-0 group-hover:opacity-100 transition-opacity p-1 hover:bg-accent rounded text-muted-foreground hover:text-foreground"
|
||||
|
|
@ -54,8 +45,19 @@ export function AssistantMessage({
|
|||
>
|
||||
<GitBranch className="h-3 w-3" />
|
||||
</button>
|
||||
</div>
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
<FunctionCalls
|
||||
functionCalls={functionCalls}
|
||||
messageIndex={messageIndex}
|
||||
expandedFunctionCalls={expandedFunctionCalls}
|
||||
onToggle={onToggle}
|
||||
/>
|
||||
<MarkdownRenderer chatMessage={content} />
|
||||
{isStreaming && (
|
||||
<span className="inline-block w-2 h-4 bg-blue-400 ml-1 animate-pulse"></span>
|
||||
)}
|
||||
</div>
|
||||
</Message>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
17
frontend/src/app/chat/components/message.tsx
Normal file
17
frontend/src/app/chat/components/message.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { ReactNode } from "react";
|
||||
|
||||
interface MessageProps {
|
||||
icon: ReactNode;
|
||||
children: ReactNode;
|
||||
actions?: ReactNode;
|
||||
}
|
||||
|
||||
export function Message({ icon, children, actions }: MessageProps) {
|
||||
return (
|
||||
<div className="flex gap-3">
|
||||
{icon}
|
||||
<div className="flex-1 min-w-0">{children}</div>
|
||||
{actions && <div className="flex-shrink-0 ml-2">{actions}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import { User } from "lucide-react";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||
import { useAuth } from "@/contexts/auth-context";
|
||||
import { Message } from "./message";
|
||||
|
||||
interface UserMessageProps {
|
||||
content: string;
|
||||
|
|
@ -10,22 +11,23 @@ export function UserMessage({ content }: UserMessageProps) {
|
|||
const { user } = useAuth();
|
||||
|
||||
return (
|
||||
<div className="flex gap-3">
|
||||
<Avatar className="w-8 h-8 flex-shrink-0 select-none">
|
||||
<AvatarImage draggable={false} src={user?.picture} alt={user?.name} />
|
||||
<AvatarFallback className="text-sm bg-primary/20 text-primary">
|
||||
{user?.name ? (
|
||||
user.name.charAt(0).toUpperCase()
|
||||
) : (
|
||||
<User className="h-4 w-4" />
|
||||
)}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="flex-1">
|
||||
<p className="text-foreground whitespace-pre-wrap break-words overflow-wrap-anywhere">
|
||||
{content}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Message
|
||||
icon={
|
||||
<Avatar className="w-8 h-8 flex-shrink-0 select-none">
|
||||
<AvatarImage draggable={false} src={user?.picture} alt={user?.name} />
|
||||
<AvatarFallback className="text-sm bg-primary/20 text-primary">
|
||||
{user?.name ? (
|
||||
user.name.charAt(0).toUpperCase()
|
||||
) : (
|
||||
<User className="h-4 w-4" />
|
||||
)}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
}
|
||||
>
|
||||
<p className="text-foreground whitespace-pre-wrap break-words overflow-wrap-anywhere">
|
||||
{content}
|
||||
</p>
|
||||
</Message>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
import { ReactNode, useEffect, useState } from "react";
|
||||
import { motion, AnimatePresence } from "motion/react";
|
||||
import { Message } from "@/app/chat/components/message";
|
||||
import DogIcon from "@/components/logo/dog-icon";
|
||||
|
||||
interface OnboardingStepProps {
|
||||
text: string;
|
||||
children: ReactNode;
|
||||
isVisible: boolean;
|
||||
isCompleted?: boolean;
|
||||
}
|
||||
|
||||
export function OnboardingStep({ text, children, isVisible, isCompleted = false }: OnboardingStepProps) {
|
||||
const [displayedText, setDisplayedText] = useState("");
|
||||
const [showChildren, setShowChildren] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isVisible) {
|
||||
setDisplayedText("");
|
||||
setShowChildren(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let currentIndex = 0;
|
||||
setDisplayedText("");
|
||||
setShowChildren(false);
|
||||
|
||||
const interval = setInterval(() => {
|
||||
if (currentIndex < text.length) {
|
||||
setDisplayedText(text.slice(0, currentIndex + 1));
|
||||
currentIndex++;
|
||||
} else {
|
||||
clearInterval(interval);
|
||||
setShowChildren(true);
|
||||
}
|
||||
}, 10); // 10ms per character
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [text, isVisible]);
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, ease: "easeOut" }}
|
||||
className={isCompleted ? "opacity-50" : ""}
|
||||
>
|
||||
<Message
|
||||
icon={
|
||||
<div className="w-8 h-8 rounded-lg bg-accent/20 flex items-center justify-center flex-shrink-0 select-none">
|
||||
<DogIcon className="h-6 w-6 text-accent-foreground" disabled={isCompleted} />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<p className={`text-foreground text-sm py-1.5 ${isCompleted ? "text-placeholder-foreground" : ""}`}>
|
||||
{displayedText}
|
||||
{!showChildren && !isCompleted && <span className="inline-block w-1 h-4 bg-primary ml-1 animate-pulse" />}
|
||||
</p>
|
||||
<AnimatePresence>
|
||||
{showChildren && !isCompleted && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, height: 0 }}
|
||||
transition={{ duration: 0.3, ease: "easeOut" }}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</Message>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
27
frontend/src/app/new-onboarding/components/progress-bar.tsx
Normal file
27
frontend/src/app/new-onboarding/components/progress-bar.tsx
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
interface ProgressBarProps {
|
||||
currentStep: number;
|
||||
totalSteps: number;
|
||||
}
|
||||
|
||||
export function ProgressBar({ currentStep, totalSteps }: ProgressBarProps) {
|
||||
const progressPercentage = ((currentStep + 1) / totalSteps) * 100;
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="flex items-center max-w-md mx-auto gap-3">
|
||||
<div className="flex-1 h-2 bg-background rounded-full overflow-hidden">
|
||||
<div
|
||||
className="h-full transition-all duration-300 ease-in-out"
|
||||
style={{
|
||||
width: `${progressPercentage}%`,
|
||||
background: 'linear-gradient(to right, #818CF8, #F472B6)'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<span className="text-sm text-muted-foreground whitespace-nowrap">
|
||||
{currentStep + 1}/{totalSteps}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
109
frontend/src/app/new-onboarding/page.tsx
Normal file
109
frontend/src/app/new-onboarding/page.tsx
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
"use client";
|
||||
|
||||
import { Suspense, useState } from "react";
|
||||
import { ProtectedRoute } from "@/components/protected-route";
|
||||
import { DoclingHealthBanner } from "@/components/docling-health-banner";
|
||||
import { DotPattern } from "@/components/ui/dot-pattern";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { OnboardingStep } from "./components/onboarding-step";
|
||||
import { ProgressBar } from "./components/progress-bar";
|
||||
import OnboardingCard from "../onboarding/components/onboarding-card";
|
||||
|
||||
const TOTAL_STEPS = 4;
|
||||
|
||||
function NewOnboardingPage() {
|
||||
const [currentStep, setCurrentStep] = useState(0);
|
||||
|
||||
const handleStepComplete = () => {
|
||||
if (currentStep < TOTAL_STEPS - 1) {
|
||||
setCurrentStep(currentStep + 1);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-dvh w-full flex gap-5 flex-col items-center justify-center bg-primary-foreground relative p-4">
|
||||
<DoclingHealthBanner className="absolute top-0 left-0 right-0 w-full z-20" />
|
||||
|
||||
{/* Chat-like content area */}
|
||||
<div className="flex flex-col items-center gap-5 w-full max-w-3xl z-10">
|
||||
<div className="w-full h-[872px] bg-background border rounded-lg p-4 shadow-sm overflow-y-auto">
|
||||
<div className="space-y-6">
|
||||
<OnboardingStep
|
||||
isVisible={currentStep >= 0}
|
||||
isCompleted={currentStep > 0}
|
||||
text="Let's get started by setting up your model provider."
|
||||
>
|
||||
<OnboardingCard onComplete={handleStepComplete} />
|
||||
</OnboardingStep>
|
||||
|
||||
<OnboardingStep
|
||||
isVisible={currentStep >= 1}
|
||||
isCompleted={currentStep > 1}
|
||||
text="Step 1: Configure your settings"
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<p className="text-muted-foreground">
|
||||
Let's configure some basic settings for your account.
|
||||
</p>
|
||||
<button
|
||||
onClick={handleStepComplete}
|
||||
className="px-4 py-2 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90"
|
||||
>
|
||||
Continue
|
||||
</button>
|
||||
</div>
|
||||
</OnboardingStep>
|
||||
|
||||
<OnboardingStep
|
||||
isVisible={currentStep >= 2}
|
||||
isCompleted={currentStep > 2}
|
||||
text="Step 2: Connect your model"
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<p className="text-muted-foreground">
|
||||
Choose and connect your preferred AI model provider.
|
||||
</p>
|
||||
<button
|
||||
onClick={handleStepComplete}
|
||||
className="px-4 py-2 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90"
|
||||
>
|
||||
Continue
|
||||
</button>
|
||||
</div>
|
||||
</OnboardingStep>
|
||||
|
||||
<OnboardingStep
|
||||
isVisible={currentStep >= 3}
|
||||
isCompleted={currentStep > 3}
|
||||
text="Step 3: You're all set!"
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<p className="text-muted-foreground">
|
||||
Your account is ready to use. Let's start chatting!
|
||||
</p>
|
||||
<button
|
||||
onClick={() => window.location.href = "/chat"}
|
||||
className="px-4 py-2 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90"
|
||||
>
|
||||
Go to Chat
|
||||
</button>
|
||||
</div>
|
||||
</OnboardingStep>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ProgressBar currentStep={currentStep} totalSteps={TOTAL_STEPS} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ProtectedNewOnboardingPage() {
|
||||
return (
|
||||
<ProtectedRoute>
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<NewOnboardingPage />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import { LabelWrapper } from "@/components/label-wrapper";
|
||||
import OllamaLogo from "@/components/logo/ollama-logo";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
|
|
@ -39,6 +38,8 @@ export function AdvancedOnboarding({
|
|||
languageModels !== undefined &&
|
||||
languageModel !== undefined &&
|
||||
setLanguageModel !== undefined;
|
||||
|
||||
const updatedOnboarding = process.env.UPDATED_ONBOARDING === "true";
|
||||
return (
|
||||
<Accordion type="single" collapsible>
|
||||
<AccordionItem value="item-1">
|
||||
|
|
@ -74,18 +75,20 @@ export function AdvancedOnboarding({
|
|||
/>
|
||||
</LabelWrapper>
|
||||
)}
|
||||
{(hasLanguageModels || hasEmbeddingModels) && <Separator />}
|
||||
<LabelWrapper
|
||||
label="Sample dataset"
|
||||
description="Load sample data to chat with immediately."
|
||||
id="sample-dataset"
|
||||
flex
|
||||
>
|
||||
<Switch
|
||||
checked={sampleDataset}
|
||||
onCheckedChange={setSampleDataset}
|
||||
/>
|
||||
</LabelWrapper>
|
||||
{(hasLanguageModels || hasEmbeddingModels) && !updatedOnboarding && <Separator />}
|
||||
{!updatedOnboarding && (
|
||||
<LabelWrapper
|
||||
label="Sample dataset"
|
||||
description="Load sample data to chat with immediately."
|
||||
id="sample-dataset"
|
||||
flex
|
||||
>
|
||||
<Switch
|
||||
checked={sampleDataset}
|
||||
onCheckedChange={setSampleDataset}
|
||||
/>
|
||||
</LabelWrapper>
|
||||
)}
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
"use client";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import {
|
||||
type OnboardingVariables,
|
||||
useOnboardingMutation,
|
||||
} from "@/app/api/mutations/useOnboardingMutation";
|
||||
import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery";
|
||||
import { useDoclingHealth } from "@/components/docling-health-banner";
|
||||
import IBMLogo from "@/components/logo/ibm-logo";
|
||||
import OllamaLogo from "@/components/logo/ollama-logo";
|
||||
import OpenAILogo from "@/components/logo/openai-logo";
|
||||
|
|
@ -28,24 +27,13 @@ import { IBMOnboarding } from "./ibm-onboarding";
|
|||
import { OllamaOnboarding } from "./ollama-onboarding";
|
||||
import { OpenAIOnboarding } from "./openai-onboarding";
|
||||
|
||||
const OnboardingCard = ({
|
||||
isDoclingHealthy,
|
||||
}: {
|
||||
isDoclingHealthy: boolean;
|
||||
}) => {
|
||||
interface OnboardingCardProps {
|
||||
onComplete: () => void;
|
||||
}
|
||||
|
||||
const OnboardingCard = ({ onComplete }: OnboardingCardProps) => {
|
||||
const updatedOnboarding = process.env.UPDATED_ONBOARDING === "true";
|
||||
const { data: settingsDb, isLoading: isSettingsLoading } =
|
||||
useGetSettingsQuery();
|
||||
|
||||
const redirect = "/";
|
||||
const router = useRouter();
|
||||
|
||||
// Redirect if already authenticated or in no-auth mode
|
||||
useEffect(() => {
|
||||
if (!isSettingsLoading && settingsDb && settingsDb.edited) {
|
||||
router.push(redirect);
|
||||
}
|
||||
}, [isSettingsLoading, settingsDb, router]);
|
||||
const { isHealthy: isDoclingHealthy } = useDoclingHealth();
|
||||
|
||||
|
||||
const [modelProvider, setModelProvider] = useState<string>("openai");
|
||||
|
|
@ -71,7 +59,7 @@ const OnboardingCard = ({
|
|||
const onboardingMutation = useOnboardingMutation({
|
||||
onSuccess: (data) => {
|
||||
console.log("Onboarding completed successfully", data);
|
||||
router.push(redirect);
|
||||
onComplete();
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error("Failed to complete onboarding", {
|
||||
|
|
@ -124,7 +112,7 @@ const OnboardingCard = ({
|
|||
defaultValue={modelProvider}
|
||||
onValueChange={handleSetModelProvider}
|
||||
>
|
||||
<CardHeader>
|
||||
<CardHeader className={`${updatedOnboarding ? "px-0" : ""}`}>
|
||||
<TabsList>
|
||||
<TabsTrigger value="openai">
|
||||
<OpenAILogo className="w-4 h-4" />
|
||||
|
|
@ -140,7 +128,7 @@ const OnboardingCard = ({
|
|||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<CardContent className={`${updatedOnboarding ? "px-0" : ""}`}>
|
||||
<TabsContent value="openai">
|
||||
<OpenAIOnboarding
|
||||
setSettings={setSettings}
|
||||
|
|
@ -164,7 +152,7 @@ const OnboardingCard = ({
|
|||
</TabsContent>
|
||||
</CardContent>
|
||||
</Tabs>
|
||||
<CardFooter className={`flex ${updatedOnboarding ? "" : "justify-end"}`}>
|
||||
<CardFooter className={`flex ${updatedOnboarding ? "px-0" : "justify-end"}`}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,28 @@
|
|||
"use client";
|
||||
|
||||
import { Suspense } from "react";
|
||||
import { DoclingHealthBanner, useDoclingHealth } from "@/components/docling-health-banner";
|
||||
import { Suspense, useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { DoclingHealthBanner } from "@/components/docling-health-banner";
|
||||
import { ProtectedRoute } from "@/components/protected-route";
|
||||
import { DotPattern } from "@/components/ui/dot-pattern";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery";
|
||||
import OnboardingCard from "./components/onboarding-card";
|
||||
|
||||
function OnboardingPage() {
|
||||
const { isHealthy: isDoclingHealthy } = useDoclingHealth();
|
||||
function LegacyOnboardingPage() {
|
||||
const router = useRouter();
|
||||
const { data: settingsDb, isLoading: isSettingsLoading } = useGetSettingsQuery();
|
||||
|
||||
// Redirect if already completed onboarding
|
||||
useEffect(() => {
|
||||
if (!isSettingsLoading && settingsDb && settingsDb.edited) {
|
||||
router.push("/");
|
||||
}
|
||||
}, [isSettingsLoading, settingsDb, router]);
|
||||
|
||||
const handleComplete = () => {
|
||||
router.push("/");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-dvh w-full flex gap-5 flex-col items-center justify-center bg-background relative p-4">
|
||||
|
|
@ -32,17 +46,34 @@ function OnboardingPage() {
|
|||
Connect a model provider
|
||||
</h1>
|
||||
</div>
|
||||
<OnboardingCard isDoclingHealthy={isDoclingHealthy} />
|
||||
<OnboardingCard onComplete={handleComplete} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function OnboardingRouter() {
|
||||
const updatedOnboarding = process.env.UPDATED_ONBOARDING === "true";
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
if (updatedOnboarding) {
|
||||
router.push("/new-onboarding");
|
||||
}
|
||||
}, [updatedOnboarding, router]);
|
||||
|
||||
if (updatedOnboarding) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <LegacyOnboardingPage />;
|
||||
}
|
||||
|
||||
export default function ProtectedOnboardingPage() {
|
||||
return (
|
||||
<ProtectedRoute>
|
||||
<Suspense fallback={<div>Loading onboarding...</div>}>
|
||||
<OnboardingPage />
|
||||
<OnboardingRouter />
|
||||
</Suspense>
|
||||
</ProtectedRoute>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ export function LayoutWrapper({ children }: { children: React.ReactNode }) {
|
|||
};
|
||||
|
||||
// List of paths that should not show navigation
|
||||
const authPaths = ["/login", "/auth/callback", "/onboarding"];
|
||||
const authPaths = ["/login", "/auth/callback", "/onboarding", "/new-onboarding"];
|
||||
const isAuthPage = authPaths.includes(pathname);
|
||||
const isOnKnowledgePage = pathname.startsWith("/knowledge");
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@ export function ProtectedRoute({ children }: ProtectedRouteProps) {
|
|||
}
|
||||
|
||||
if (!isLoading && !isSettingsLoading && !settings.edited) {
|
||||
router.push("/onboarding");
|
||||
const updatedOnboarding = process.env.UPDATED_ONBOARDING === "true";
|
||||
router.push(updatedOnboarding ? "/new-onboarding" : "/onboarding");
|
||||
}
|
||||
}, [
|
||||
isLoading,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue