From 6a971d45d63c06df3369b973c3996be017c3f06b Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62335616+lucaseduoli@users.noreply.github.com> Date: Thu, 16 Oct 2025 16:38:08 -0300 Subject: [PATCH] feat: add new onboarding animation, adds onboarding into layout wrapper (#267) * modularized header * removed onboarding redirection * added animations * Made animations slide instead of grow * change name of grow to slide * Added constants * Fixed animations, added onboarding content to card, switch display depending on current state of onboarding * fixed initial animation * fixed animation glitch * removed debug settimeout * fixed width * Format --- frontend/components/docling-health-banner.tsx | 227 +++++++++--------- frontend/src/app/globals.css | 13 +- frontend/src/app/login/page.tsx | 15 +- .../components/onboarding-content.tsx | 78 ++++++ .../components/onboarding-step.tsx | 142 ++++++----- .../components/progress-bar.tsx | 6 +- frontend/src/app/new-onboarding/page.tsx | 117 +++------ .../src/components/animated-conditional.tsx | 53 ++++ frontend/src/components/chat-renderer.tsx | 180 ++++++++++++++ frontend/src/components/header.tsx | 60 +++++ frontend/src/components/layout-wrapper.tsx | 159 +++--------- frontend/src/components/protected-route.tsx | 17 +- frontend/src/lib/constants.ts | 7 +- 13 files changed, 639 insertions(+), 435 deletions(-) create mode 100644 frontend/src/app/new-onboarding/components/onboarding-content.tsx create mode 100644 frontend/src/components/animated-conditional.tsx create mode 100644 frontend/src/components/chat-renderer.tsx create mode 100644 frontend/src/components/header.tsx 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. + +
- - - -
-
- ); + + + +
+
+ ); } // 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. +

+ +
+
+ + = 2} + isCompleted={currentStep > 2} + text="Step 2: Connect your model" + > +
+

+ Choose and connect your preferred AI model provider. +

+ +
+
+ + = 3} + isCompleted={currentStep > 3} + text="Step 3: You're all set!" + > +
+

+ Your account is ready to use. Let's start chatting! +

+ +
+
+
+ ); +} 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. -

- -
-
- - = 2} - isCompleted={currentStep > 2} - text="Step 2: Connect your model" - > -
-

- Choose and connect your preferred AI model provider. -

- -
-
- - = 3} - isCompleted={currentStep > 3} - text="Step 3: You're all set!" - > -
-

- Your account is ready to use. Let's start chatting! -

- -
-
-
-
- - -
-
- ); + +
+
+ ); } 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 */} + + + {/* 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 */} - - - {/* 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