From 527bc7f67e5e2440e361c18ffc9603c059f64553 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 23 Dec 2025 13:22:48 -0300 Subject: [PATCH] Changed frontend to not reference local storage --- frontend/app/chat/page.tsx | 30 ++--- .../_components/animated-provider-steps.tsx | 19 +-- .../_components/onboarding-card.tsx | 16 +-- .../_components/onboarding-content.tsx | 109 ++++++++---------- .../_components/onboarding-upload.tsx | 20 ++-- frontend/components/chat-renderer.tsx | 109 +++++++++--------- frontend/contexts/chat-context.tsx | 30 ++--- frontend/contexts/task-context.tsx | 15 ++- frontend/lib/constants.ts | 11 -- 9 files changed, 157 insertions(+), 202 deletions(-) diff --git a/frontend/app/chat/page.tsx b/frontend/app/chat/page.tsx index f15cf788..663bc36b 100644 --- a/frontend/app/chat/page.tsx +++ b/frontend/app/chat/page.tsx @@ -12,6 +12,7 @@ import { FILE_CONFIRMATION, FILES_REGEX } from "@/lib/constants"; import { useLoadingStore } from "@/stores/loadingStore"; import { useGetConversationsQuery } from "../api/queries/useGetConversationsQuery"; import { useGetNudgesQuery } from "../api/queries/useGetNudgesQuery"; +import { useGetSettingsQuery } from "../api/queries/useGetSettingsQuery"; import { AssistantMessage } from "./_components/assistant-message"; import { ChatInput, type ChatInputHandle } from "./_components/chat-input"; import Nudges from "./_components/nudges"; @@ -638,27 +639,14 @@ function ChatPage() { }; }, [endpoint, setPreviousResponseIds, setLoading]); - // Check if onboarding is complete by looking at local storage - const [isOnboardingComplete, setIsOnboardingComplete] = useState(() => { - if (typeof window === "undefined") return false; - return localStorage.getItem("onboarding-step") === null; - }); - - // Listen for storage changes to detect when onboarding completes - useEffect(() => { - const checkOnboarding = () => { - if (typeof window !== "undefined") { - setIsOnboardingComplete( - localStorage.getItem("onboarding-step") === null, - ); - } - }; - - // Check periodically since storage events don't fire in the same tab - const interval = setInterval(checkOnboarding, 500); - - return () => clearInterval(interval); - }, []); + // Get settings to check onboarding completion + const { data: settings } = useGetSettingsQuery(); + + // Check if onboarding is complete (current_step >= 4 means complete) + const TOTAL_ONBOARDING_STEPS = 4; + const isOnboardingComplete = + settings?.onboarding?.current_step !== undefined && + settings.onboarding.current_step >= TOTAL_ONBOARDING_STEPS; // Prepare filters for nudges (same as chat) const processedFiltersForNudges = parsedFilterData?.filters diff --git a/frontend/app/onboarding/_components/animated-provider-steps.tsx b/frontend/app/onboarding/_components/animated-provider-steps.tsx index 026b330f..27ddde54 100644 --- a/frontend/app/onboarding/_components/animated-provider-steps.tsx +++ b/frontend/app/onboarding/_components/animated-provider-steps.tsx @@ -10,7 +10,6 @@ import { AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; -import { ONBOARDING_CARD_STEPS_KEY } from "@/lib/constants"; import { cn } from "@/lib/utils"; export function AnimatedProviderSteps({ @@ -18,7 +17,6 @@ export function AnimatedProviderSteps({ isCompleted, setCurrentStep, steps, - storageKey = ONBOARDING_CARD_STEPS_KEY, processingStartTime, hasError = false, }: { @@ -26,25 +24,19 @@ export function AnimatedProviderSteps({ isCompleted: boolean; setCurrentStep: (step: number) => void; steps: string[]; - storageKey?: string; processingStartTime?: number | null; hasError?: boolean; }) { const [startTime, setStartTime] = useState(null); const [elapsedTime, setElapsedTime] = useState(0); - // Initialize start time from prop or local storage + // Initialize start time from prop useEffect(() => { - const storedElapsedTime = localStorage.getItem(storageKey); - - if (isCompleted && storedElapsedTime) { - // If completed, use stored elapsed time - setElapsedTime(parseFloat(storedElapsedTime)); - } else if (processingStartTime) { + if (processingStartTime) { // Use the start time passed from parent (when user clicked Complete) setStartTime(processingStartTime); } - }, [storageKey, isCompleted, processingStartTime]); + }, [processingStartTime]); // Progress through steps useEffect(() => { @@ -56,14 +48,13 @@ export function AnimatedProviderSteps({ } }, [currentStep, setCurrentStep, steps, isCompleted]); - // Calculate and store elapsed time when completed + // Calculate elapsed time when completed useEffect(() => { if (isCompleted && startTime) { const elapsed = Date.now() - startTime; setElapsedTime(elapsed); - localStorage.setItem(storageKey, elapsed.toString()); } - }, [isCompleted, startTime, storageKey]); + }, [isCompleted, startTime]); const isDone = currentStep >= steps.length && !isCompleted && !hasError; diff --git a/frontend/app/onboarding/_components/onboarding-card.tsx b/frontend/app/onboarding/_components/onboarding-card.tsx index be7a25ef..5ea6218a 100644 --- a/frontend/app/onboarding/_components/onboarding-card.tsx +++ b/frontend/app/onboarding/_components/onboarding-card.tsx @@ -10,6 +10,7 @@ import { useOnboardingMutation, } from "@/app/api/mutations/useOnboardingMutation"; import { useOnboardingRollbackMutation } from "@/app/api/mutations/useOnboardingRollbackMutation"; +import { useUpdateOnboardingStateMutation } from "@/app/api/mutations/useUpdateOnboardingStateMutation"; import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery"; import { useGetTasksQuery } from "@/app/api/queries/useGetTasksQuery"; import type { ProviderHealthResponse } from "@/app/api/queries/useProviderHealthQuery"; @@ -25,7 +26,6 @@ import { TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; -import { ONBOARDING_CARD_STEPS_KEY } from "@/lib/constants"; import { cn } from "@/lib/utils"; import { AnimatedProviderSteps } from "./animated-provider-steps"; import { AnthropicOnboarding } from "./anthropic-onboarding"; @@ -173,6 +173,8 @@ const OnboardingCard = ({ // Track which tasks we've already handled to prevent infinite loops const handledFailedTasksRef = useRef>(new Set()); + + const updateOnboardingMutation = useUpdateOnboardingStateMutation(); // Query tasks to track completion const { data: tasks } = useGetTasksQuery({ @@ -307,11 +309,12 @@ const OnboardingCard = ({ console.log("Onboarding completed successfully", data); // Save OpenRAG docs filter ID if sample data was ingested - if (data.openrag_docs_filter_id && typeof window !== "undefined") { - localStorage.setItem( - "onboarding_openrag_docs_filter_id", - data.openrag_docs_filter_id - ); + if (data.openrag_docs_filter_id) { + // Save to backend + updateOnboardingMutation.mutateAsync({ + openrag_docs_filter_id: data.openrag_docs_filter_id, + }); + console.log("Saved OpenRAG docs filter ID:", data.openrag_docs_filter_id); } @@ -674,7 +677,6 @@ const OnboardingCard = ({ setCurrentStep={setCurrentStep} steps={isEmbedding ? EMBEDDING_STEP_LIST : STEP_LIST} processingStartTime={processingStartTime} - storageKey={ONBOARDING_CARD_STEPS_KEY} hasError={!!error} /> diff --git a/frontend/app/onboarding/_components/onboarding-content.tsx b/frontend/app/onboarding/_components/onboarding-content.tsx index 1127e725..094c540b 100644 --- a/frontend/app/onboarding/_components/onboarding-content.tsx +++ b/frontend/app/onboarding/_components/onboarding-content.tsx @@ -3,6 +3,8 @@ import { useEffect, useRef, useState } from "react"; import { StickToBottom } from "use-stick-to-bottom"; import { getFilterById } from "@/app/api/queries/useGetFilterByIdQuery"; +import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery"; +import { useUpdateOnboardingStateMutation } from "@/app/api/mutations/useUpdateOnboardingStateMutation"; import { AssistantMessage } from "@/app/chat/_components/assistant-message"; import Nudges from "@/app/chat/_components/nudges"; import { UserMessage } from "@/app/chat/_components/user-message"; @@ -10,11 +12,6 @@ import type { Message, SelectedFilters } from "@/app/chat/_types/types"; import OnboardingCard from "@/app/onboarding/_components/onboarding-card"; import { useChat } from "@/contexts/chat-context"; import { useChatStreaming } from "@/hooks/useChatStreaming"; -import { - ONBOARDING_ASSISTANT_MESSAGE_KEY, - ONBOARDING_OPENRAG_DOCS_FILTER_ID_KEY, - ONBOARDING_SELECTED_NUDGE_KEY, -} from "@/lib/constants"; import { OnboardingStep } from "./onboarding-step"; import OnboardingUpload from "./onboarding-upload"; @@ -36,43 +33,46 @@ export function OnboardingContent({ currentStep: number; }) { const { setConversationFilter, setCurrentConversationId } = useChat(); + const { data: settings } = useGetSettingsQuery(); + const updateOnboardingMutation = useUpdateOnboardingStateMutation(); const parseFailedRef = useRef(false); const [responseId, setResponseId] = useState(null); + + // Initialize from backend settings const [selectedNudge, setSelectedNudge] = useState(() => { - // Retrieve selected nudge from localStorage on mount - if (typeof window === "undefined") return ""; - return localStorage.getItem(ONBOARDING_SELECTED_NUDGE_KEY) || ""; + return settings?.onboarding?.selected_nudge || ""; }); + const [assistantMessage, setAssistantMessage] = useState( () => { - // Retrieve assistant message from localStorage on mount - if (typeof window === "undefined") return null; - const savedMessage = localStorage.getItem( - ONBOARDING_ASSISTANT_MESSAGE_KEY, - ); - if (savedMessage) { - try { - const parsed = JSON.parse(savedMessage); - // Convert timestamp string back to Date object - return { - ...parsed, - timestamp: new Date(parsed.timestamp), - }; - } catch (error) { - console.error("Failed to parse saved assistant message:", error); - parseFailedRef.current = true; - // Clear corrupted data - will go back a step in useEffect - if (typeof window !== "undefined") { - localStorage.removeItem(ONBOARDING_ASSISTANT_MESSAGE_KEY); - localStorage.removeItem(ONBOARDING_SELECTED_NUDGE_KEY); - } - return null; - } + // Get from backend settings + if (settings?.onboarding?.assistant_message) { + const msg = settings.onboarding.assistant_message; + return { + role: msg.role as "user" | "assistant", + content: msg.content, + timestamp: new Date(msg.timestamp), + }; } return null; }, ); + // Sync state when settings change + useEffect(() => { + if (settings?.onboarding?.selected_nudge) { + setSelectedNudge(settings.onboarding.selected_nudge); + } + if (settings?.onboarding?.assistant_message) { + const msg = settings.onboarding.assistant_message; + setAssistantMessage({ + role: msg.role as "user" | "assistant", + content: msg.content, + timestamp: new Date(msg.timestamp), + }); + } + }, [settings?.onboarding]); + // Handle parse errors by going back a step useEffect(() => { if (parseFailedRef.current && currentStep >= 2) { @@ -83,28 +83,23 @@ export function OnboardingContent({ const { streamingMessage, isLoading, sendMessage } = useChatStreaming({ onComplete: async (message, newResponseId) => { setAssistantMessage(message); - // Save assistant message to localStorage when complete - if (typeof window !== "undefined") { - try { - localStorage.setItem( - ONBOARDING_ASSISTANT_MESSAGE_KEY, - JSON.stringify(message), - ); - } catch (error) { - console.error( - "Failed to save assistant message to localStorage:", - error, - ); - } - } + // Save assistant message to backend + await updateOnboardingMutation.mutateAsync({ + assistant_message: { + role: message.role, + content: message.content, + timestamp: message.timestamp.toISOString(), + }, + }); + if (newResponseId) { setResponseId(newResponseId); // Set the current conversation ID setCurrentConversationId(newResponseId); - // Save the filter association for this conversation - const openragDocsFilterId = localStorage.getItem(ONBOARDING_OPENRAG_DOCS_FILTER_ID_KEY); + // Get filter ID from backend settings + const openragDocsFilterId = settings?.onboarding?.openrag_docs_filter_id; if (openragDocsFilterId) { try { // Load the filter and set it in the context with explicit responseId @@ -136,21 +131,17 @@ export function OnboardingContent({ const handleNudgeClick = async (nudge: string) => { setSelectedNudge(nudge); - // Save selected nudge to localStorage - if (typeof window !== "undefined") { - localStorage.setItem(ONBOARDING_SELECTED_NUDGE_KEY, nudge); - } setAssistantMessage(null); - // Clear saved assistant message when starting a new conversation - if (typeof window !== "undefined") { - localStorage.removeItem(ONBOARDING_ASSISTANT_MESSAGE_KEY); - } + + // Save selected nudge to backend and clear assistant message + await updateOnboardingMutation.mutateAsync({ + selected_nudge: nudge, + assistant_message: null, + }); + setTimeout(async () => { // Check if we have the OpenRAG docs filter ID (sample data was ingested) - const openragDocsFilterId = - typeof window !== "undefined" - ? localStorage.getItem(ONBOARDING_OPENRAG_DOCS_FILTER_ID_KEY) - : null; + const openragDocsFilterId = settings?.onboarding?.openrag_docs_filter_id; // Load and set the OpenRAG docs filter if available let filterToUse = null; diff --git a/frontend/app/onboarding/_components/onboarding-upload.tsx b/frontend/app/onboarding/_components/onboarding-upload.tsx index b434cce9..bef2610d 100644 --- a/frontend/app/onboarding/_components/onboarding-upload.tsx +++ b/frontend/app/onboarding/_components/onboarding-upload.tsx @@ -3,14 +3,11 @@ import { AnimatePresence, motion } from "motion/react"; import { type ChangeEvent, useEffect, useRef, useState } from "react"; import { toast } from "sonner"; import { useCreateFilter } from "@/app/api/mutations/useCreateFilter"; +import { useUpdateOnboardingStateMutation } from "@/app/api/mutations/useUpdateOnboardingStateMutation"; import { useGetNudgesQuery } from "@/app/api/queries/useGetNudgesQuery"; import { useGetTasksQuery } from "@/app/api/queries/useGetTasksQuery"; import { AnimatedProviderSteps } from "@/app/onboarding/_components/animated-provider-steps"; import { Button } from "@/components/ui/button"; -import { - ONBOARDING_UPLOAD_STEPS_KEY, - ONBOARDING_USER_DOC_FILTER_ID_KEY, -} from "@/lib/constants"; import { uploadFile } from "@/lib/upload-utils"; interface OnboardingUploadProps { @@ -27,6 +24,7 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => { const [isCreatingFilter, setIsCreatingFilter] = useState(false); const createFilterMutation = useCreateFilter(); + const updateOnboardingMutation = useUpdateOnboardingStateMutation(); const STEP_LIST = [ "Uploading your document", @@ -103,12 +101,13 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => { description: `Filter for ${filename}`, queryData: queryData, }) - .then((result) => { - if (result.filter?.id && typeof window !== "undefined") { - localStorage.setItem( - ONBOARDING_USER_DOC_FILTER_ID_KEY, - result.filter.id, - ); + .then(async (result) => { + if (result.filter?.id) { + // Save to backend + await updateOnboardingMutation.mutateAsync({ + user_doc_filter_id: result.filter.id, + }); + console.log( "Created knowledge filter for uploaded document", result.filter.id, @@ -267,7 +266,6 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => { setCurrentStep={setCurrentStep} isCompleted={false} steps={STEP_LIST} - storageKey={ONBOARDING_UPLOAD_STEPS_KEY} /> )} diff --git a/frontend/components/chat-renderer.tsx b/frontend/components/chat-renderer.tsx index a0e2a9d4..110586ea 100644 --- a/frontend/components/chat-renderer.tsx +++ b/frontend/components/chat-renderer.tsx @@ -9,6 +9,7 @@ import { } from "@/app/api/queries/useGetConversationsQuery"; import { getFilterById } from "@/app/api/queries/useGetFilterByIdQuery"; import type { Settings } from "@/app/api/queries/useGetSettingsQuery"; +import { useUpdateOnboardingStateMutation } from "@/app/api/mutations/useUpdateOnboardingStateMutation"; import { OnboardingContent } from "@/app/onboarding/_components/onboarding-content"; import { ProgressBar } from "@/app/onboarding/_components/progress-bar"; import { AnimatedConditional } from "@/components/animated-conditional"; @@ -19,13 +20,6 @@ import { useChat } from "@/contexts/chat-context"; import { ANIMATION_DURATION, HEADER_HEIGHT, - ONBOARDING_ASSISTANT_MESSAGE_KEY, - ONBOARDING_CARD_STEPS_KEY, - ONBOARDING_OPENRAG_DOCS_FILTER_ID_KEY, - ONBOARDING_SELECTED_NUDGE_KEY, - ONBOARDING_STEP_KEY, - ONBOARDING_UPLOAD_STEPS_KEY, - ONBOARDING_USER_DOC_FILTER_ID_KEY, SIDEBAR_WIDTH, TOTAL_ONBOARDING_STEPS, } from "@/lib/constants"; @@ -50,21 +44,27 @@ export function ChatRenderer({ setOnboardingComplete, } = useChat(); - // Initialize onboarding state based on local storage and settings + // Initialize onboarding state from backend settings const [currentStep, setCurrentStep] = useState(() => { - if (typeof window === "undefined") return 0; - const savedStep = localStorage.getItem(ONBOARDING_STEP_KEY); - return savedStep !== null ? parseInt(savedStep, 10) : 0; + return settings?.onboarding?.current_step ?? 0; }); const [showLayout, setShowLayout] = useState(() => { - if (typeof window === "undefined") return false; - const savedStep = localStorage.getItem(ONBOARDING_STEP_KEY); - // Show layout if settings.edited is true and if no onboarding step is saved - const isEdited = settings?.edited ?? true; - return isEdited ? savedStep === null : false; + // Show layout only if onboarding is complete (current_step >= TOTAL_ONBOARDING_STEPS) + // This means onboarding will show even if edited=true, as long as it's not complete + const onboardingStep = settings?.onboarding?.current_step ?? 0; + return onboardingStep >= TOTAL_ONBOARDING_STEPS; }); + // Update currentStep and showLayout when settings change + useEffect(() => { + if (settings?.onboarding?.current_step !== undefined) { + setCurrentStep(settings.onboarding.current_step); + // Update showLayout based on whether onboarding is complete + setShowLayout(settings.onboarding.current_step >= TOTAL_ONBOARDING_STEPS); + } + }, [settings?.onboarding?.current_step]); + // Only fetch conversations on chat page const isOnChatPage = pathname === "/" || pathname === "/chat"; const { data: conversations = [], isLoading: isConversationsLoading } = @@ -108,18 +108,18 @@ export function ChatRenderer({ } } - // Try to get the appropriate filter ID + // Try to get the appropriate filter ID from settings let filterId: string | null = null; if (preferUserDoc) { // Completed full onboarding - prefer user document filter - filterId = localStorage.getItem(ONBOARDING_USER_DOC_FILTER_ID_KEY); + filterId = settings?.onboarding?.user_doc_filter_id || null; console.log("[FILTER] User doc filter ID:", filterId); } // Fall back to OpenRAG docs filter if (!filterId) { - filterId = localStorage.getItem(ONBOARDING_OPENRAG_DOCS_FILTER_ID_KEY); + filterId = settings?.onboarding?.openrag_docs_filter_id || null; console.log("[FILTER] OpenRAG docs filter ID:", filterId); } @@ -149,25 +149,29 @@ export function ChatRenderer({ [setConversationFilter] ); - // Save current step to local storage whenever it changes - useEffect(() => { - if (typeof window !== "undefined" && !showLayout) { - localStorage.setItem(ONBOARDING_STEP_KEY, currentStep.toString()); - } - }, [currentStep, showLayout]); + // Note: Current step is now saved to backend via handleStepComplete + // No need to save on every change, only on completion + + const updateOnboardingMutation = useUpdateOnboardingStateMutation(); const handleStepComplete = async () => { if (currentStep < TOTAL_ONBOARDING_STEPS - 1) { - setCurrentStep(currentStep + 1); + const nextStep = currentStep + 1; + setCurrentStep(nextStep); + // Save step to backend + await updateOnboardingMutation.mutateAsync({ current_step: nextStep }); } else { - // Onboarding is complete - remove from local storage and show layout - if (typeof window !== "undefined") { - localStorage.removeItem(ONBOARDING_STEP_KEY); - localStorage.removeItem(ONBOARDING_ASSISTANT_MESSAGE_KEY); - localStorage.removeItem(ONBOARDING_SELECTED_NUDGE_KEY); - localStorage.removeItem(ONBOARDING_CARD_STEPS_KEY); - localStorage.removeItem(ONBOARDING_UPLOAD_STEPS_KEY); - } + // Onboarding is complete - set step to TOTAL_ONBOARDING_STEPS to indicate completion + // and clear intermediate state in backend + await updateOnboardingMutation.mutateAsync({ + current_step: TOTAL_ONBOARDING_STEPS, + assistant_message: null, + selected_nudge: null, + card_steps: null, + upload_steps: null, + openrag_docs_filter_id: null, + user_doc_filter_id: null, + }); // Mark onboarding as complete in context setOnboardingComplete(true); @@ -180,36 +184,35 @@ export function ChatRenderer({ // This will pick up the default filter we just set await startNewConversation(); - // Clean up onboarding filter IDs now that we've set the default - if (typeof window !== "undefined") { - localStorage.removeItem(ONBOARDING_OPENRAG_DOCS_FILTER_ID_KEY); - localStorage.removeItem(ONBOARDING_USER_DOC_FILTER_ID_KEY); - console.log("[FILTER] Cleaned up onboarding filter IDs"); - } - setShowLayout(true); } }; - const handleStepBack = () => { + const handleStepBack = async () => { if (currentStep > 0) { - setCurrentStep(currentStep - 1); + const prevStep = currentStep - 1; + setCurrentStep(prevStep); + // Save step to backend + await updateOnboardingMutation.mutateAsync({ current_step: prevStep }); } }; - const handleSkipOnboarding = () => { - // Skip onboarding by marking it as complete - if (typeof window !== "undefined") { - localStorage.removeItem(ONBOARDING_STEP_KEY); - localStorage.removeItem(ONBOARDING_ASSISTANT_MESSAGE_KEY); - localStorage.removeItem(ONBOARDING_SELECTED_NUDGE_KEY); - localStorage.removeItem(ONBOARDING_CARD_STEPS_KEY); - localStorage.removeItem(ONBOARDING_UPLOAD_STEPS_KEY); - } + const handleSkipOnboarding = async () => { + // Skip onboarding by marking it as complete in backend + await updateOnboardingMutation.mutateAsync({ + current_step: TOTAL_ONBOARDING_STEPS, + assistant_message: null, + selected_nudge: null, + card_steps: null, + upload_steps: null, + openrag_docs_filter_id: null, + user_doc_filter_id: null, + }); + // Mark onboarding as complete in context setOnboardingComplete(true); // Store the OpenRAG docs filter as default for new conversations - storeDefaultFilterForNewConversations(false); + await storeDefaultFilterForNewConversations(false); setShowLayout(true); }; diff --git a/frontend/contexts/chat-context.tsx b/frontend/contexts/chat-context.tsx index f6f1a1e4..5803cde4 100644 --- a/frontend/contexts/chat-context.tsx +++ b/frontend/contexts/chat-context.tsx @@ -10,7 +10,6 @@ import { useRef, useState, } from "react"; -import { ONBOARDING_STEP_KEY } from "@/lib/constants"; import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery"; export type EndpointType = "chat" | "langflow"; @@ -129,28 +128,15 @@ export function ChatProvider({ children }: ChatProviderProps) { return false; }); - // Sync onboarding completion state with settings.edited and localStorage + // Sync onboarding completion state with settings from backend useEffect(() => { - const checkOnboarding = () => { - if (typeof window !== "undefined") { - // Onboarding is complete if settings.edited is true AND step key is null - const stepKeyExists = localStorage.getItem(ONBOARDING_STEP_KEY) !== null; - const isEdited = settings?.edited === true; - // Complete if edited is true and step key doesn't exist (onboarding flow finished) - setIsOnboardingComplete(isEdited && !stepKeyExists); - } - }; - - // Check on mount and when settings change - checkOnboarding(); - - // Listen for storage events (for cross-tab sync) - window.addEventListener("storage", checkOnboarding); - - return () => { - window.removeEventListener("storage", checkOnboarding); - }; - }, [settings?.edited]); + const TOTAL_ONBOARDING_STEPS = 4; + // Onboarding is complete if current_step >= 4 + const isComplete = + settings?.onboarding?.current_step !== undefined && + settings.onboarding.current_step >= TOTAL_ONBOARDING_STEPS; + setIsOnboardingComplete(isComplete); + }, [settings?.onboarding?.current_step]); const setOnboardingComplete = useCallback((complete: boolean) => { setIsOnboardingComplete(complete); diff --git a/frontend/contexts/task-context.tsx b/frontend/contexts/task-context.tsx index 780bbc7e..cd70b598 100644 --- a/frontend/contexts/task-context.tsx +++ b/frontend/contexts/task-context.tsx @@ -17,8 +17,8 @@ import { type TaskFileEntry, useGetTasksQuery, } from "@/app/api/queries/useGetTasksQuery"; +import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery"; import { useAuth } from "@/contexts/auth-context"; -import { ONBOARDING_STEP_KEY } from "@/lib/constants"; // Task interface is now imported from useGetTasksQuery export type { Task }; @@ -90,11 +90,18 @@ export function TaskProvider({ children }: { children: React.ReactNode }) { }, }); + // Get settings to check if onboarding is active + const { data: settings } = useGetSettingsQuery(); + // Helper function to check if onboarding is active const isOnboardingActive = useCallback(() => { - if (typeof window === "undefined") return false; - return localStorage.getItem(ONBOARDING_STEP_KEY) !== null; - }, []); + const TOTAL_ONBOARDING_STEPS = 4; + // Onboarding is active if current_step < 4 + return ( + settings?.onboarding?.current_step !== undefined && + settings.onboarding.current_step < TOTAL_ONBOARDING_STEPS + ); + }, [settings?.onboarding?.current_step]); const refetchSearch = useCallback(() => { queryClient.invalidateQueries({ diff --git a/frontend/lib/constants.ts b/frontend/lib/constants.ts index 88baf8d0..f0e1a70e 100644 --- a/frontend/lib/constants.ts +++ b/frontend/lib/constants.ts @@ -37,17 +37,6 @@ export const SIDEBAR_WIDTH = 280; export const HEADER_HEIGHT = 54; export const TOTAL_ONBOARDING_STEPS = 4; -/** - * Local Storage Keys - */ -export const ONBOARDING_STEP_KEY = "onboarding_current_step"; -export const ONBOARDING_ASSISTANT_MESSAGE_KEY = "onboarding_assistant_message"; -export const ONBOARDING_SELECTED_NUDGE_KEY = "onboarding_selected_nudge"; -export const ONBOARDING_CARD_STEPS_KEY = "onboarding_card_steps"; -export const ONBOARDING_UPLOAD_STEPS_KEY = "onboarding_upload_steps"; -export const ONBOARDING_OPENRAG_DOCS_FILTER_ID_KEY = "onboarding_openrag_docs_filter_id"; -export const ONBOARDING_USER_DOC_FILTER_ID_KEY = "onboarding_user_doc_filter_id"; - export const FILES_REGEX = /(?<=I'm uploading a document called ['"])[^'"]+\.[^.]+(?=['"]\. Here is its content:)/;