diff --git a/frontend/components/docling-health-banner.tsx b/frontend/components/docling-health-banner.tsx
index 673d952d..b15a009c 100644
--- a/frontend/components/docling-health-banner.tsx
+++ b/frontend/components/docling-health-banner.tsx
@@ -1,147 +1,148 @@
"use client";
-import { AlertTriangle, ExternalLink, Copy } from "lucide-react";
-import { useDoclingHealthQuery } from "@/src/app/api/queries/useDoclingHealthQuery";
-import { Banner, BannerIcon, BannerTitle, BannerAction } from "@/components/ui/banner";
+import { AlertTriangle, Copy, ExternalLink } from "lucide-react";
+import { useState } from "react";
+import {
+ Banner,
+ BannerAction,
+ BannerIcon,
+ BannerTitle,
+} from "@/components/ui/banner";
import { Button } from "@/components/ui/button";
import {
- Dialog,
- DialogContent,
- DialogHeader,
- DialogTitle,
- DialogDescription,
- DialogFooter
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
} from "@/components/ui/dialog";
+import { HEADER_HEIGHT } from "@/lib/constants";
import { cn } from "@/lib/utils";
-import { useState } from "react";
+import { useDoclingHealthQuery } from "@/src/app/api/queries/useDoclingHealthQuery";
interface DoclingHealthBannerProps {
- className?: string;
+ className?: string;
}
// DoclingSetupDialog component
interface DoclingSetupDialogProps {
- open: boolean;
- onOpenChange: (open: boolean) => void;
- className?: string;
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+ className?: string;
}
function DoclingSetupDialog({
- open,
- onOpenChange,
- className
+ open,
+ onOpenChange,
+ className,
}: DoclingSetupDialogProps) {
- const [copied, setCopied] = useState(false);
+ const [copied, setCopied] = useState(false);
- const handleCopy = async () => {
- await navigator.clipboard.writeText("uv run openrag");
- setCopied(true);
- setTimeout(() => setCopied(false), 2000);
- };
+ const handleCopy = async () => {
+ await navigator.clipboard.writeText("uv run openrag");
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ };
- return (
-
-
-
-
-
- docling-serve is stopped. Knowledge ingest is unavailable.
-
-
- Start docling-serve by running:
-
-
+ return (
+
+
+
+
+
+ docling-serve is stopped. Knowledge ingest is unavailable.
+
+ Start docling-serve by running:
+
-
-
-
- uv run openrag
-
-
-
-
-
+
+
+
+ uv run openrag
+
+
+
+
+
-
- Then, select Start All Services in the TUI. Once docling-serve is running, refresh OpenRAG.
-
-
+
+ Then, select{" "}
+
+ Start All Services
+ {" "}
+ in the TUI. Once docling-serve is running, refresh OpenRAG.
+
+
-
- onOpenChange(false)}
- >
- Close
-
-
-
-
- );
+
+ onOpenChange(false)}>
+ Close
+
+
+
+
+ );
}
// Custom hook to check docling health status
export function useDoclingHealth() {
- const { data: health, isLoading, isError } = useDoclingHealthQuery();
+ const { data: health, isLoading, isError } = useDoclingHealthQuery();
- const isHealthy = health?.status === "healthy" && !isError;
- const isUnhealthy = health?.status === "unhealthy" || isError;
+ const isHealthy = health?.status === "healthy" && !isError;
+ const isUnhealthy = health?.status === "unhealthy" || isError;
- return {
- health,
- isLoading,
- isError,
- isHealthy,
- isUnhealthy,
- };
+ return {
+ health,
+ isLoading,
+ isError,
+ isHealthy,
+ isUnhealthy,
+ };
}
export function DoclingHealthBanner({ className }: DoclingHealthBannerProps) {
- const { isLoading, isHealthy, isUnhealthy } = useDoclingHealth();
- const [showDialog, setShowDialog] = useState(false);
+ const { isLoading, isHealthy, isUnhealthy } = useDoclingHealth();
+ const [showDialog, setShowDialog] = useState(false);
- // Only show banner when service is unhealthy
- if (isLoading || isHealthy) {
- return null;
- }
+ // Only show banner when service is unhealthy
+ if (isLoading || isHealthy) {
+ return null;
+ }
- if (isUnhealthy) {
- return (
- <>
-
-
-
- docling-serve native service is stopped. Knowledge ingest is unavailable.
-
- setShowDialog(true)}
- className="bg-foreground text-background hover:bg-primary/90"
- >
- Setup Docling Serve
-
-
-
+ if (isUnhealthy) {
+ return (
+ <>
+
+
+
+ docling-serve native service is stopped. Knowledge ingest is
+ unavailable.
+
+ setShowDialog(true)}
+ className="bg-foreground text-background hover:bg-primary/90"
+ >
+ Setup Docling Serve
+
+
+
-
- >
- );
- }
+
+ >
+ );
+ }
- return null;
-}
\ No newline at end of file
+ return null;
+}
diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css
index d8460fd6..317879a3 100644
--- a/frontend/src/app/globals.css
+++ b/frontend/src/app/globals.css
@@ -109,15 +109,12 @@
@layer components {
.app-grid-arrangement {
- --sidebar-width: 0px;
--notifications-width: 0px;
--filters-width: 0px;
- --app-header-height: 53px;
--top-banner-height: 0px;
+ --header-height: 54px;
+ --sidebar-width: 280px;
- @media (width >= 48rem) {
- --sidebar-width: 288px;
- }
&.notifications-open {
--notifications-width: 320px;
}
@@ -132,7 +129,7 @@
width: 100%;
grid-template-rows:
var(--top-banner-height)
- var(--app-header-height)
+ var(--header-height)
1fr;
grid-template-columns:
var(--sidebar-width)
@@ -147,10 +144,6 @@
grid-template-rows 0.25s ease-in-out;
}
- .header-arrangement {
- @apply flex w-full items-center justify-between border-b border-border;
- }
-
.header-start-display {
@apply flex items-center gap-2;
}
diff --git a/frontend/src/app/login/page.tsx b/frontend/src/app/login/page.tsx
index 837706d7..c9f55e22 100644
--- a/frontend/src/app/login/page.tsx
+++ b/frontend/src/app/login/page.tsx
@@ -9,37 +9,28 @@ import { Button } from "@/components/ui/button";
import { DotPattern } from "@/components/ui/dot-pattern";
import { useAuth } from "@/contexts/auth-context";
import { cn } from "@/lib/utils";
-import { useGetSettingsQuery } from "../api/queries/useGetSettingsQuery";
function LoginPageContent() {
const { isLoading, isAuthenticated, isNoAuthMode, login } = useAuth();
const router = useRouter();
const searchParams = useSearchParams();
- const { data: settings, isLoading: isSettingsLoading } = useGetSettingsQuery({
- enabled: isAuthenticated || isNoAuthMode,
- });
-
- const redirect =
- settings && !settings.edited
- ? "/onboarding"
- : searchParams.get("redirect") || "/chat";
+ const redirect = searchParams.get("redirect") || "/chat";
// Redirect if already authenticated or in no-auth mode
useEffect(() => {
- if (!isLoading && !isSettingsLoading && (isAuthenticated || isNoAuthMode)) {
+ if (!isLoading && (isAuthenticated || isNoAuthMode)) {
router.push(redirect);
}
}, [
isLoading,
- isSettingsLoading,
isAuthenticated,
isNoAuthMode,
router,
redirect,
]);
- if (isLoading || isSettingsLoading) {
+ if (isLoading) {
return (
diff --git a/frontend/src/app/new-onboarding/components/onboarding-content.tsx b/frontend/src/app/new-onboarding/components/onboarding-content.tsx
new file mode 100644
index 00000000..1e5e7c03
--- /dev/null
+++ b/frontend/src/app/new-onboarding/components/onboarding-content.tsx
@@ -0,0 +1,78 @@
+"use client";
+
+import OnboardingCard from "@/app/onboarding/components/onboarding-card";
+import { OnboardingStep } from "./onboarding-step";
+
+export function OnboardingContent({
+ handleStepComplete,
+ currentStep,
+}: {
+ handleStepComplete: () => void;
+ currentStep: number;
+}) {
+ return (
+
+
= 0}
+ isCompleted={currentStep > 0}
+ text="Let's get started by setting up your model provider."
+ >
+
+
+
+
= 1}
+ isCompleted={currentStep > 1}
+ text="Step 1: Configure your settings"
+ >
+
+
+ Let's configure some basic settings for your account.
+
+
+ Continue
+
+
+
+
+
= 2}
+ isCompleted={currentStep > 2}
+ text="Step 2: Connect your model"
+ >
+
+
+ Choose and connect your preferred AI model provider.
+
+
+ Continue
+
+
+
+
+
= 3}
+ isCompleted={currentStep > 3}
+ text="Step 3: You're all set!"
+ >
+
+
+ Your account is ready to use. Let's start chatting!
+
+
+ Go to Chat
+
+
+
+
+ );
+}
diff --git a/frontend/src/app/new-onboarding/components/onboarding-step.tsx b/frontend/src/app/new-onboarding/components/onboarding-step.tsx
index bda65101..16b62010 100644
--- a/frontend/src/app/new-onboarding/components/onboarding-step.tsx
+++ b/frontend/src/app/new-onboarding/components/onboarding-step.tsx
@@ -1,78 +1,90 @@
-import { ReactNode, useEffect, useState } from "react";
-import { motion, AnimatePresence } from "motion/react";
+import { AnimatePresence, motion } from "motion/react";
+import { type ReactNode, useEffect, useState } from "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;
+ 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);
+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;
- }
+ useEffect(() => {
+ if (!isVisible) {
+ setDisplayedText("");
+ setShowChildren(false);
+ return;
+ }
- let currentIndex = 0;
- setDisplayedText("");
- setShowChildren(false);
+ 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
+ const interval = setInterval(() => {
+ if (currentIndex < text.length) {
+ setDisplayedText(text.slice(0, currentIndex + 1));
+ currentIndex++;
+ } else {
+ clearInterval(interval);
+ setShowChildren(true);
+ }
+ }, 20); // 20ms per character
- return () => clearInterval(interval);
- }, [text, isVisible]);
+ return () => clearInterval(interval);
+ }, [text, isVisible]);
- if (!isVisible) return null;
+ if (!isVisible) return null;
- return (
-
-
-
-
- }
- >
-
-
- {displayedText}
- {!showChildren && !isCompleted && }
-
-
- {showChildren && !isCompleted && (
-
- {children}
-
- )}
-
-
-
-
- );
+ return (
+
+
+
+
+ }
+ >
+
+
+ {displayedText}
+ {!showChildren && !isCompleted && (
+
+ )}
+
+
+ {showChildren && !isCompleted && (
+
+ {children}
+
+ )}
+
+
+
+
+ );
}
diff --git a/frontend/src/app/new-onboarding/components/progress-bar.tsx b/frontend/src/app/new-onboarding/components/progress-bar.tsx
index e0e12e45..adb0ffea 100644
--- a/frontend/src/app/new-onboarding/components/progress-bar.tsx
+++ b/frontend/src/app/new-onboarding/components/progress-bar.tsx
@@ -8,8 +8,8 @@ export function ProgressBar({ currentStep, totalSteps }: ProgressBarProps) {
return (
-
-
+
+
-
+
{currentStep + 1}/{totalSteps}
diff --git a/frontend/src/app/new-onboarding/page.tsx b/frontend/src/app/new-onboarding/page.tsx
index efc50bf0..d7e122d5 100644
--- a/frontend/src/app/new-onboarding/page.tsx
+++ b/frontend/src/app/new-onboarding/page.tsx
@@ -1,109 +1,48 @@
"use client";
import { Suspense, useState } from "react";
-import { ProtectedRoute } from "@/components/protected-route";
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 OnboardingCard from "../onboarding/components/onboarding-card";
+import { OnboardingContent } from "./components/onboarding-content";
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 [currentStep, setCurrentStep] = useState(0);
- const handleStepComplete = () => {
- if (currentStep < TOTAL_STEPS - 1) {
- setCurrentStep(currentStep + 1);
- }
- };
+ const handleStepComplete = () => {
+ if (currentStep < TOTAL_STEPS - 1) {
+ setCurrentStep(currentStep + 1);
+ }
+ };
- return (
-
-
+ return (
+
+
- {/* Chat-like content area */}
-
-
-
-
= 0}
- isCompleted={currentStep > 0}
- text="Let's get started by setting up your model provider."
- >
-
-
+ {/* Chat-like content area */}
+
+
+
+
-
= 1}
- isCompleted={currentStep > 1}
- text="Step 1: Configure your settings"
- >
-
-
- Let's configure some basic settings for your account.
-
-
- Continue
-
-
-
-
-
= 2}
- isCompleted={currentStep > 2}
- text="Step 2: Connect your model"
- >
-
-
- Choose and connect your preferred AI model provider.
-
-
- Continue
-
-
-
-
-
= 3}
- isCompleted={currentStep > 3}
- text="Step 3: You're all set!"
- >
-
-
- Your account is ready to use. Let's start chatting!
-
-
window.location.href = "/chat"}
- className="px-4 py-2 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90"
- >
- Go to Chat
-
-
-
-
-
-
-
-
-
- );
+
+
+
+ );
}
export default function ProtectedNewOnboardingPage() {
- return (
-
- Loading... }>
-
-
-
- );
+ return (
+
+ Loading... }>
+
+
+
+ );
}
diff --git a/frontend/src/components/animated-conditional.tsx b/frontend/src/components/animated-conditional.tsx
new file mode 100644
index 00000000..e2499890
--- /dev/null
+++ b/frontend/src/components/animated-conditional.tsx
@@ -0,0 +1,53 @@
+import { motion } from "framer-motion";
+import { ANIMATION_DURATION } from "@/lib/constants";
+
+export const AnimatedConditional = ({
+ children,
+ isOpen,
+ className,
+ slide = false,
+ delay,
+ vertical = false,
+}: {
+ children: React.ReactNode;
+ isOpen: boolean;
+ className?: string;
+ delay?: number;
+ vertical?: boolean;
+ slide?: boolean;
+}) => {
+ const animationProperty = slide
+ ? vertical
+ ? "translateY"
+ : "translateX"
+ : vertical
+ ? "height"
+ : "width";
+ const animationValue = isOpen
+ ? slide
+ ? "0px"
+ : "auto"
+ : slide
+ ? "-100%"
+ : "0px";
+
+ return (
+
+ {children}
+
+ );
+};
diff --git a/frontend/src/components/chat-renderer.tsx b/frontend/src/components/chat-renderer.tsx
new file mode 100644
index 00000000..88174cf2
--- /dev/null
+++ b/frontend/src/components/chat-renderer.tsx
@@ -0,0 +1,180 @@
+"use client";
+
+import { motion } from "framer-motion";
+import { usePathname } from "next/navigation";
+import { useState } from "react";
+import {
+ type ChatConversation,
+ useGetConversationsQuery,
+} from "@/app/api/queries/useGetConversationsQuery";
+import type { Settings } from "@/app/api/queries/useGetSettingsQuery";
+import { OnboardingContent } from "@/app/new-onboarding/components/onboarding-content";
+import { ProgressBar } from "@/app/new-onboarding/components/progress-bar";
+import { AnimatedConditional } from "@/components/animated-conditional";
+import { Header } from "@/components/header";
+import { Navigation } from "@/components/navigation";
+import { useAuth } from "@/contexts/auth-context";
+import { useChat } from "@/contexts/chat-context";
+import {
+ ANIMATION_DURATION,
+ HEADER_HEIGHT,
+ SIDEBAR_WIDTH,
+ TOTAL_ONBOARDING_STEPS,
+} from "@/lib/constants";
+import { cn } from "@/lib/utils";
+
+export function ChatRenderer({
+ settings,
+ children,
+}: {
+ settings: Settings;
+ children: React.ReactNode;
+}) {
+ const pathname = usePathname();
+ const { isAuthenticated, isNoAuthMode } = useAuth();
+ const {
+ endpoint,
+ refreshTrigger,
+ refreshConversations,
+ startNewConversation,
+ } = useChat();
+
+ // Onboarding animation state
+ const [showLayout, setShowLayout] = useState(!!settings?.edited);
+ // Only fetch conversations on chat page
+ const isOnChatPage = pathname === "/" || pathname === "/chat";
+ const { data: conversations = [], isLoading: isConversationsLoading } =
+ useGetConversationsQuery(endpoint, refreshTrigger, {
+ enabled: isOnChatPage && (isAuthenticated || isNoAuthMode),
+ }) as { data: ChatConversation[]; isLoading: boolean };
+
+ const handleNewConversation = () => {
+ refreshConversations();
+ startNewConversation();
+ };
+
+ const [currentStep, setCurrentStep] = useState(0);
+
+ const handleStepComplete = () => {
+ if (currentStep < TOTAL_ONBOARDING_STEPS - 1) {
+ setCurrentStep(currentStep + 1);
+ } else {
+ setShowLayout(true);
+ }
+ };
+
+ // List of paths with smaller max-width
+ const smallWidthPaths = ["/settings/connector/new"];
+ const isSmallWidthPath = smallWidthPaths.includes(pathname);
+
+ const x = showLayout ? "0px" : `calc(-${SIDEBAR_WIDTH / 2}px + 50vw)`;
+ const y = showLayout ? "0px" : `calc(-${HEADER_HEIGHT / 2}px + 50vh)`;
+ const translateY = showLayout ? "0px" : `-50vh`;
+ const translateX = showLayout ? "0px" : `-50vw`;
+
+ // For all other pages, render with Langflow-styled navigation and task menu
+ return (
+ <>
+
+
+
+
+ {/* Sidebar Navigation */}
+
+
+
+
+ {/* Main Content */}
+
+
+
+
+
+ {children}
+
+ {!showLayout && (
+
+ )}
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/frontend/src/components/header.tsx b/frontend/src/components/header.tsx
new file mode 100644
index 00000000..e542b53e
--- /dev/null
+++ b/frontend/src/components/header.tsx
@@ -0,0 +1,60 @@
+"use client";
+
+import { Bell } from "lucide-react";
+import Logo from "@/components/logo/logo";
+import { UserNav } from "@/components/user-nav";
+import { useTask } from "@/contexts/task-context";
+import { cn } from "@/lib/utils";
+
+export function Header() {
+ const { tasks, toggleMenu } = useTask();
+
+ // Calculate active tasks for the bell icon
+ const activeTasks = tasks.filter(
+ (task) =>
+ task.status === "pending" ||
+ task.status === "running" ||
+ task.status === "processing",
+ );
+
+ return (
+
+
+ {/* Logo/Title */}
+
+
+ OpenRAG
+
+
+
+
+ {/* Knowledge Filter Dropdown */}
+ {/*
*/}
+
+ {/* GitHub Star Button */}
+ {/*
*/}
+
+ {/* Discord Link */}
+ {/*
*/}
+
+ {/* Task Notification Bell */}
+
+
+ {activeTasks.length > 0 &&
}
+
+
+ {/* Separator */}
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/components/layout-wrapper.tsx b/frontend/src/components/layout-wrapper.tsx
index 130ad3f0..17339f60 100644
--- a/frontend/src/components/layout-wrapper.tsx
+++ b/frontend/src/components/layout-wrapper.tsx
@@ -1,39 +1,25 @@
"use client";
-import { Bell, Loader2 } from "lucide-react";
+import { Loader2 } from "lucide-react";
import { usePathname } from "next/navigation";
-import {
- useGetConversationsQuery,
- type ChatConversation,
-} from "@/app/api/queries/useGetConversationsQuery";
import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery";
import { DoclingHealthBanner } from "@/components/docling-health-banner";
import { KnowledgeFilterPanel } from "@/components/knowledge-filter-panel";
-import Logo from "@/components/logo/logo";
-import { Navigation } from "@/components/navigation";
import { TaskNotificationMenu } from "@/components/task-notification-menu";
-import { UserNav } from "@/components/user-nav";
import { useAuth } from "@/contexts/auth-context";
-import { useChat } from "@/contexts/chat-context";
import { useKnowledgeFilter } from "@/contexts/knowledge-filter-context";
-// import { GitHubStarButton } from "@/components/github-star-button"
-// import { DiscordLink } from "@/components/discord-link"
import { useTask } from "@/contexts/task-context";
-import { useDoclingHealthQuery } from "@/src/app/api/queries/useDoclingHealthQuery";
import { cn } from "@/lib/utils";
+import { useDoclingHealthQuery } from "@/src/app/api/queries/useDoclingHealthQuery";
+import { ChatRenderer } from "./chat-renderer";
export function LayoutWrapper({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
- const { tasks, isMenuOpen, toggleMenu } = useTask();
+ const { isMenuOpen } = useTask();
const { isPanelOpen } = useKnowledgeFilter();
const { isLoading, isAuthenticated, isNoAuthMode } = useAuth();
- const {
- endpoint,
- refreshTrigger,
- refreshConversations,
- startNewConversation,
- } = useChat();
- const { isLoading: isSettingsLoading } = useGetSettingsQuery({
+
+ const { data: settings, isLoading: isSettingsLoading } = useGetSettingsQuery({
enabled: isAuthenticated || isNoAuthMode,
});
const {
@@ -42,40 +28,16 @@ export function LayoutWrapper({ children }: { children: React.ReactNode }) {
isError,
} = useDoclingHealthQuery();
- // Only fetch conversations on chat page
- const isOnChatPage = pathname === "/" || pathname === "/chat";
- const { data: conversations = [], isLoading: isConversationsLoading } =
- useGetConversationsQuery(endpoint, refreshTrigger, {
- enabled: isOnChatPage && (isAuthenticated || isNoAuthMode),
- }) as { data: ChatConversation[]; isLoading: boolean };
-
- const handleNewConversation = () => {
- refreshConversations();
- startNewConversation();
- };
-
// List of paths that should not show navigation
- const authPaths = ["/login", "/auth/callback", "/onboarding", "/new-onboarding"];
+ const authPaths = ["/login", "/auth/callback"];
const isAuthPage = authPaths.includes(pathname);
const isOnKnowledgePage = pathname.startsWith("/knowledge");
- // List of paths with smaller max-width
- const smallWidthPaths = ["/settings/connector/new"];
- const isSmallWidthPath = smallWidthPaths.includes(pathname);
-
- // Calculate active tasks for the bell icon
- const activeTasks = tasks.filter(
- task =>
- task.status === "pending" ||
- task.status === "running" ||
- task.status === "processing"
- );
-
const isUnhealthy = health?.status === "unhealthy" || isError;
const isBannerVisible = !isHealthLoading && isUnhealthy;
// Show loading state when backend isn't ready
- if (isLoading || isSettingsLoading) {
+ if (isLoading || isSettingsLoading || !settings) {
return (
@@ -93,88 +55,31 @@ export function LayoutWrapper({ children }: { children: React.ReactNode }) {
// For all other pages, render with Langflow-styled navigation and task menu
return (
-
-
-
+
+
+
+
+
+
+
{children}
+
+ {/* Task Notifications Panel */}
+
+
+ {/* Knowledge Filter Panel */}
+
-
-
- {/* Logo/Title */}
-
-
- OpenRAG
-
-
-
-
- {/* Knowledge Filter Dropdown */}
- {/*
*/}
-
- {/* GitHub Star Button */}
- {/*
*/}
-
- {/* Discord Link */}
- {/*
*/}
-
- {/* Task Notification Bell */}
-
-
- {activeTasks.length > 0 && (
-
- )}
-
-
- {/* Separator */}
-
-
-
-
-
-
-
- {/* Sidebar Navigation */}
-
-
- {/* Main Content */}
-
-
- {children}
-
-
-
- {/* Task Notifications Panel */}
-
-
- {/* Knowledge Filter Panel */}
-
);
}
diff --git a/frontend/src/components/protected-route.tsx b/frontend/src/components/protected-route.tsx
index a5403e1a..a6f8bf62 100644
--- a/frontend/src/components/protected-route.tsx
+++ b/frontend/src/components/protected-route.tsx
@@ -3,7 +3,6 @@
import { Loader2 } from "lucide-react";
import { usePathname, useRouter } from "next/navigation";
import { useEffect } from "react";
-import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery";
import { useAuth } from "@/contexts/auth-context";
interface ProtectedRouteProps {
@@ -12,10 +11,6 @@ interface ProtectedRouteProps {
export function ProtectedRoute({ children }: ProtectedRouteProps) {
const { isLoading, isAuthenticated, isNoAuthMode } = useAuth();
- const { data: settings = {}, isLoading: isSettingsLoading } =
- useGetSettingsQuery({
- enabled: isAuthenticated || isNoAuthMode,
- });
const router = useRouter();
const pathname = usePathname();
@@ -31,30 +26,22 @@ export function ProtectedRoute({ children }: ProtectedRouteProps) {
);
useEffect(() => {
- if (!isLoading && !isSettingsLoading && !isAuthenticated && !isNoAuthMode) {
+ if (!isLoading && !isAuthenticated && !isNoAuthMode) {
// Redirect to login with current path as redirect parameter
const redirectUrl = `/login?redirect=${encodeURIComponent(pathname)}`;
router.push(redirectUrl);
return;
}
-
- if (!isLoading && !isSettingsLoading && !settings.edited) {
- const updatedOnboarding = process.env.UPDATED_ONBOARDING === "true";
- router.push(updatedOnboarding ? "/new-onboarding" : "/onboarding");
- }
}, [
isLoading,
- isSettingsLoading,
isAuthenticated,
isNoAuthMode,
router,
pathname,
- isSettingsLoading,
- settings.edited,
]);
// Show loading state while checking authentication
- if (isLoading || isSettingsLoading) {
+ if (isLoading) {
return (
diff --git a/frontend/src/lib/constants.ts b/frontend/src/lib/constants.ts
index 9ce34634..1cd8eb90 100644
--- a/frontend/src/lib/constants.ts
+++ b/frontend/src/lib/constants.ts
@@ -22,4 +22,9 @@ export const DEFAULT_KNOWLEDGE_SETTINGS = {
*/
export const UI_CONSTANTS = {
MAX_SYSTEM_PROMPT_CHARS: 2000,
-} as const;
\ No newline at end of file
+} as const;
+
+export const ANIMATION_DURATION = 0.4;
+export const SIDEBAR_WIDTH = 280;
+export const HEADER_HEIGHT = 54;
+export const TOTAL_ONBOARDING_STEPS = 4;
\ No newline at end of file