Changed frontend to not reference local storage
This commit is contained in:
parent
633afde224
commit
527bc7f67e
9 changed files with 157 additions and 202 deletions
|
|
@ -12,6 +12,7 @@ import { FILE_CONFIRMATION, FILES_REGEX } from "@/lib/constants";
|
||||||
import { useLoadingStore } from "@/stores/loadingStore";
|
import { useLoadingStore } from "@/stores/loadingStore";
|
||||||
import { useGetConversationsQuery } from "../api/queries/useGetConversationsQuery";
|
import { useGetConversationsQuery } from "../api/queries/useGetConversationsQuery";
|
||||||
import { useGetNudgesQuery } from "../api/queries/useGetNudgesQuery";
|
import { useGetNudgesQuery } from "../api/queries/useGetNudgesQuery";
|
||||||
|
import { useGetSettingsQuery } from "../api/queries/useGetSettingsQuery";
|
||||||
import { AssistantMessage } from "./_components/assistant-message";
|
import { AssistantMessage } from "./_components/assistant-message";
|
||||||
import { ChatInput, type ChatInputHandle } from "./_components/chat-input";
|
import { ChatInput, type ChatInputHandle } from "./_components/chat-input";
|
||||||
import Nudges from "./_components/nudges";
|
import Nudges from "./_components/nudges";
|
||||||
|
|
@ -638,27 +639,14 @@ function ChatPage() {
|
||||||
};
|
};
|
||||||
}, [endpoint, setPreviousResponseIds, setLoading]);
|
}, [endpoint, setPreviousResponseIds, setLoading]);
|
||||||
|
|
||||||
// Check if onboarding is complete by looking at local storage
|
// Get settings to check onboarding completion
|
||||||
const [isOnboardingComplete, setIsOnboardingComplete] = useState(() => {
|
const { data: settings } = useGetSettingsQuery();
|
||||||
if (typeof window === "undefined") return false;
|
|
||||||
return localStorage.getItem("onboarding-step") === null;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Listen for storage changes to detect when onboarding completes
|
// Check if onboarding is complete (current_step >= 4 means complete)
|
||||||
useEffect(() => {
|
const TOTAL_ONBOARDING_STEPS = 4;
|
||||||
const checkOnboarding = () => {
|
const isOnboardingComplete =
|
||||||
if (typeof window !== "undefined") {
|
settings?.onboarding?.current_step !== undefined &&
|
||||||
setIsOnboardingComplete(
|
settings.onboarding.current_step >= TOTAL_ONBOARDING_STEPS;
|
||||||
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);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Prepare filters for nudges (same as chat)
|
// Prepare filters for nudges (same as chat)
|
||||||
const processedFiltersForNudges = parsedFilterData?.filters
|
const processedFiltersForNudges = parsedFilterData?.filters
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import {
|
||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionTrigger,
|
AccordionTrigger,
|
||||||
} from "@/components/ui/accordion";
|
} from "@/components/ui/accordion";
|
||||||
import { ONBOARDING_CARD_STEPS_KEY } from "@/lib/constants";
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
export function AnimatedProviderSteps({
|
export function AnimatedProviderSteps({
|
||||||
|
|
@ -18,7 +17,6 @@ export function AnimatedProviderSteps({
|
||||||
isCompleted,
|
isCompleted,
|
||||||
setCurrentStep,
|
setCurrentStep,
|
||||||
steps,
|
steps,
|
||||||
storageKey = ONBOARDING_CARD_STEPS_KEY,
|
|
||||||
processingStartTime,
|
processingStartTime,
|
||||||
hasError = false,
|
hasError = false,
|
||||||
}: {
|
}: {
|
||||||
|
|
@ -26,25 +24,19 @@ export function AnimatedProviderSteps({
|
||||||
isCompleted: boolean;
|
isCompleted: boolean;
|
||||||
setCurrentStep: (step: number) => void;
|
setCurrentStep: (step: number) => void;
|
||||||
steps: string[];
|
steps: string[];
|
||||||
storageKey?: string;
|
|
||||||
processingStartTime?: number | null;
|
processingStartTime?: number | null;
|
||||||
hasError?: boolean;
|
hasError?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const [startTime, setStartTime] = useState<number | null>(null);
|
const [startTime, setStartTime] = useState<number | null>(null);
|
||||||
const [elapsedTime, setElapsedTime] = useState<number>(0);
|
const [elapsedTime, setElapsedTime] = useState<number>(0);
|
||||||
|
|
||||||
// Initialize start time from prop or local storage
|
// Initialize start time from prop
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const storedElapsedTime = localStorage.getItem(storageKey);
|
if (processingStartTime) {
|
||||||
|
|
||||||
if (isCompleted && storedElapsedTime) {
|
|
||||||
// If completed, use stored elapsed time
|
|
||||||
setElapsedTime(parseFloat(storedElapsedTime));
|
|
||||||
} else if (processingStartTime) {
|
|
||||||
// Use the start time passed from parent (when user clicked Complete)
|
// Use the start time passed from parent (when user clicked Complete)
|
||||||
setStartTime(processingStartTime);
|
setStartTime(processingStartTime);
|
||||||
}
|
}
|
||||||
}, [storageKey, isCompleted, processingStartTime]);
|
}, [processingStartTime]);
|
||||||
|
|
||||||
// Progress through steps
|
// Progress through steps
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -56,14 +48,13 @@ export function AnimatedProviderSteps({
|
||||||
}
|
}
|
||||||
}, [currentStep, setCurrentStep, steps, isCompleted]);
|
}, [currentStep, setCurrentStep, steps, isCompleted]);
|
||||||
|
|
||||||
// Calculate and store elapsed time when completed
|
// Calculate elapsed time when completed
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isCompleted && startTime) {
|
if (isCompleted && startTime) {
|
||||||
const elapsed = Date.now() - startTime;
|
const elapsed = Date.now() - startTime;
|
||||||
setElapsedTime(elapsed);
|
setElapsedTime(elapsed);
|
||||||
localStorage.setItem(storageKey, elapsed.toString());
|
|
||||||
}
|
}
|
||||||
}, [isCompleted, startTime, storageKey]);
|
}, [isCompleted, startTime]);
|
||||||
|
|
||||||
const isDone = currentStep >= steps.length && !isCompleted && !hasError;
|
const isDone = currentStep >= steps.length && !isCompleted && !hasError;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import {
|
||||||
useOnboardingMutation,
|
useOnboardingMutation,
|
||||||
} from "@/app/api/mutations/useOnboardingMutation";
|
} from "@/app/api/mutations/useOnboardingMutation";
|
||||||
import { useOnboardingRollbackMutation } from "@/app/api/mutations/useOnboardingRollbackMutation";
|
import { useOnboardingRollbackMutation } from "@/app/api/mutations/useOnboardingRollbackMutation";
|
||||||
|
import { useUpdateOnboardingStateMutation } from "@/app/api/mutations/useUpdateOnboardingStateMutation";
|
||||||
import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery";
|
import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery";
|
||||||
import { useGetTasksQuery } from "@/app/api/queries/useGetTasksQuery";
|
import { useGetTasksQuery } from "@/app/api/queries/useGetTasksQuery";
|
||||||
import type { ProviderHealthResponse } from "@/app/api/queries/useProviderHealthQuery";
|
import type { ProviderHealthResponse } from "@/app/api/queries/useProviderHealthQuery";
|
||||||
|
|
@ -25,7 +26,6 @@ import {
|
||||||
TooltipContent,
|
TooltipContent,
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
import { ONBOARDING_CARD_STEPS_KEY } from "@/lib/constants";
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { AnimatedProviderSteps } from "./animated-provider-steps";
|
import { AnimatedProviderSteps } from "./animated-provider-steps";
|
||||||
import { AnthropicOnboarding } from "./anthropic-onboarding";
|
import { AnthropicOnboarding } from "./anthropic-onboarding";
|
||||||
|
|
@ -174,6 +174,8 @@ const OnboardingCard = ({
|
||||||
// Track which tasks we've already handled to prevent infinite loops
|
// Track which tasks we've already handled to prevent infinite loops
|
||||||
const handledFailedTasksRef = useRef<Set<string>>(new Set());
|
const handledFailedTasksRef = useRef<Set<string>>(new Set());
|
||||||
|
|
||||||
|
const updateOnboardingMutation = useUpdateOnboardingStateMutation();
|
||||||
|
|
||||||
// Query tasks to track completion
|
// Query tasks to track completion
|
||||||
const { data: tasks } = useGetTasksQuery({
|
const { data: tasks } = useGetTasksQuery({
|
||||||
enabled: currentStep !== null, // Only poll when onboarding has started
|
enabled: currentStep !== null, // Only poll when onboarding has started
|
||||||
|
|
@ -307,11 +309,12 @@ const OnboardingCard = ({
|
||||||
console.log("Onboarding completed successfully", data);
|
console.log("Onboarding completed successfully", data);
|
||||||
|
|
||||||
// Save OpenRAG docs filter ID if sample data was ingested
|
// Save OpenRAG docs filter ID if sample data was ingested
|
||||||
if (data.openrag_docs_filter_id && typeof window !== "undefined") {
|
if (data.openrag_docs_filter_id) {
|
||||||
localStorage.setItem(
|
// Save to backend
|
||||||
"onboarding_openrag_docs_filter_id",
|
updateOnboardingMutation.mutateAsync({
|
||||||
data.openrag_docs_filter_id
|
openrag_docs_filter_id: data.openrag_docs_filter_id,
|
||||||
);
|
});
|
||||||
|
|
||||||
console.log("Saved 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}
|
setCurrentStep={setCurrentStep}
|
||||||
steps={isEmbedding ? EMBEDDING_STEP_LIST : STEP_LIST}
|
steps={isEmbedding ? EMBEDDING_STEP_LIST : STEP_LIST}
|
||||||
processingStartTime={processingStartTime}
|
processingStartTime={processingStartTime}
|
||||||
storageKey={ONBOARDING_CARD_STEPS_KEY}
|
|
||||||
hasError={!!error}
|
hasError={!!error}
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { StickToBottom } from "use-stick-to-bottom";
|
import { StickToBottom } from "use-stick-to-bottom";
|
||||||
import { getFilterById } from "@/app/api/queries/useGetFilterByIdQuery";
|
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 { AssistantMessage } from "@/app/chat/_components/assistant-message";
|
||||||
import Nudges from "@/app/chat/_components/nudges";
|
import Nudges from "@/app/chat/_components/nudges";
|
||||||
import { UserMessage } from "@/app/chat/_components/user-message";
|
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 OnboardingCard from "@/app/onboarding/_components/onboarding-card";
|
||||||
import { useChat } from "@/contexts/chat-context";
|
import { useChat } from "@/contexts/chat-context";
|
||||||
import { useChatStreaming } from "@/hooks/useChatStreaming";
|
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 { OnboardingStep } from "./onboarding-step";
|
||||||
import OnboardingUpload from "./onboarding-upload";
|
import OnboardingUpload from "./onboarding-upload";
|
||||||
|
|
@ -36,43 +33,46 @@ export function OnboardingContent({
|
||||||
currentStep: number;
|
currentStep: number;
|
||||||
}) {
|
}) {
|
||||||
const { setConversationFilter, setCurrentConversationId } = useChat();
|
const { setConversationFilter, setCurrentConversationId } = useChat();
|
||||||
|
const { data: settings } = useGetSettingsQuery();
|
||||||
|
const updateOnboardingMutation = useUpdateOnboardingStateMutation();
|
||||||
const parseFailedRef = useRef(false);
|
const parseFailedRef = useRef(false);
|
||||||
const [responseId, setResponseId] = useState<string | null>(null);
|
const [responseId, setResponseId] = useState<string | null>(null);
|
||||||
|
|
||||||
|
// Initialize from backend settings
|
||||||
const [selectedNudge, setSelectedNudge] = useState<string>(() => {
|
const [selectedNudge, setSelectedNudge] = useState<string>(() => {
|
||||||
// Retrieve selected nudge from localStorage on mount
|
return settings?.onboarding?.selected_nudge || "";
|
||||||
if (typeof window === "undefined") return "";
|
|
||||||
return localStorage.getItem(ONBOARDING_SELECTED_NUDGE_KEY) || "";
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const [assistantMessage, setAssistantMessage] = useState<Message | null>(
|
const [assistantMessage, setAssistantMessage] = useState<Message | null>(
|
||||||
() => {
|
() => {
|
||||||
// Retrieve assistant message from localStorage on mount
|
// Get from backend settings
|
||||||
if (typeof window === "undefined") return null;
|
if (settings?.onboarding?.assistant_message) {
|
||||||
const savedMessage = localStorage.getItem(
|
const msg = settings.onboarding.assistant_message;
|
||||||
ONBOARDING_ASSISTANT_MESSAGE_KEY,
|
return {
|
||||||
);
|
role: msg.role as "user" | "assistant",
|
||||||
if (savedMessage) {
|
content: msg.content,
|
||||||
try {
|
timestamp: new Date(msg.timestamp),
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
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
|
// Handle parse errors by going back a step
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (parseFailedRef.current && currentStep >= 2) {
|
if (parseFailedRef.current && currentStep >= 2) {
|
||||||
|
|
@ -83,28 +83,23 @@ export function OnboardingContent({
|
||||||
const { streamingMessage, isLoading, sendMessage } = useChatStreaming({
|
const { streamingMessage, isLoading, sendMessage } = useChatStreaming({
|
||||||
onComplete: async (message, newResponseId) => {
|
onComplete: async (message, newResponseId) => {
|
||||||
setAssistantMessage(message);
|
setAssistantMessage(message);
|
||||||
// Save assistant message to localStorage when complete
|
// Save assistant message to backend
|
||||||
if (typeof window !== "undefined") {
|
await updateOnboardingMutation.mutateAsync({
|
||||||
try {
|
assistant_message: {
|
||||||
localStorage.setItem(
|
role: message.role,
|
||||||
ONBOARDING_ASSISTANT_MESSAGE_KEY,
|
content: message.content,
|
||||||
JSON.stringify(message),
|
timestamp: message.timestamp.toISOString(),
|
||||||
);
|
},
|
||||||
} catch (error) {
|
});
|
||||||
console.error(
|
|
||||||
"Failed to save assistant message to localStorage:",
|
|
||||||
error,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (newResponseId) {
|
if (newResponseId) {
|
||||||
setResponseId(newResponseId);
|
setResponseId(newResponseId);
|
||||||
|
|
||||||
// Set the current conversation ID
|
// Set the current conversation ID
|
||||||
setCurrentConversationId(newResponseId);
|
setCurrentConversationId(newResponseId);
|
||||||
|
|
||||||
// Save the filter association for this conversation
|
// Get filter ID from backend settings
|
||||||
const openragDocsFilterId = localStorage.getItem(ONBOARDING_OPENRAG_DOCS_FILTER_ID_KEY);
|
const openragDocsFilterId = settings?.onboarding?.openrag_docs_filter_id;
|
||||||
if (openragDocsFilterId) {
|
if (openragDocsFilterId) {
|
||||||
try {
|
try {
|
||||||
// Load the filter and set it in the context with explicit responseId
|
// Load the filter and set it in the context with explicit responseId
|
||||||
|
|
@ -136,21 +131,17 @@ export function OnboardingContent({
|
||||||
|
|
||||||
const handleNudgeClick = async (nudge: string) => {
|
const handleNudgeClick = async (nudge: string) => {
|
||||||
setSelectedNudge(nudge);
|
setSelectedNudge(nudge);
|
||||||
// Save selected nudge to localStorage
|
|
||||||
if (typeof window !== "undefined") {
|
|
||||||
localStorage.setItem(ONBOARDING_SELECTED_NUDGE_KEY, nudge);
|
|
||||||
}
|
|
||||||
setAssistantMessage(null);
|
setAssistantMessage(null);
|
||||||
// Clear saved assistant message when starting a new conversation
|
|
||||||
if (typeof window !== "undefined") {
|
// Save selected nudge to backend and clear assistant message
|
||||||
localStorage.removeItem(ONBOARDING_ASSISTANT_MESSAGE_KEY);
|
await updateOnboardingMutation.mutateAsync({
|
||||||
}
|
selected_nudge: nudge,
|
||||||
|
assistant_message: null,
|
||||||
|
});
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
// Check if we have the OpenRAG docs filter ID (sample data was ingested)
|
// Check if we have the OpenRAG docs filter ID (sample data was ingested)
|
||||||
const openragDocsFilterId =
|
const openragDocsFilterId = settings?.onboarding?.openrag_docs_filter_id;
|
||||||
typeof window !== "undefined"
|
|
||||||
? localStorage.getItem(ONBOARDING_OPENRAG_DOCS_FILTER_ID_KEY)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
// Load and set the OpenRAG docs filter if available
|
// Load and set the OpenRAG docs filter if available
|
||||||
let filterToUse = null;
|
let filterToUse = null;
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,11 @@ import { AnimatePresence, motion } from "motion/react";
|
||||||
import { type ChangeEvent, useEffect, useRef, useState } from "react";
|
import { type ChangeEvent, useEffect, useRef, useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { useCreateFilter } from "@/app/api/mutations/useCreateFilter";
|
import { useCreateFilter } from "@/app/api/mutations/useCreateFilter";
|
||||||
|
import { useUpdateOnboardingStateMutation } from "@/app/api/mutations/useUpdateOnboardingStateMutation";
|
||||||
import { useGetNudgesQuery } from "@/app/api/queries/useGetNudgesQuery";
|
import { useGetNudgesQuery } from "@/app/api/queries/useGetNudgesQuery";
|
||||||
import { useGetTasksQuery } from "@/app/api/queries/useGetTasksQuery";
|
import { useGetTasksQuery } from "@/app/api/queries/useGetTasksQuery";
|
||||||
import { AnimatedProviderSteps } from "@/app/onboarding/_components/animated-provider-steps";
|
import { AnimatedProviderSteps } from "@/app/onboarding/_components/animated-provider-steps";
|
||||||
import { Button } from "@/components/ui/button";
|
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";
|
import { uploadFile } from "@/lib/upload-utils";
|
||||||
|
|
||||||
interface OnboardingUploadProps {
|
interface OnboardingUploadProps {
|
||||||
|
|
@ -27,6 +24,7 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
|
||||||
const [isCreatingFilter, setIsCreatingFilter] = useState(false);
|
const [isCreatingFilter, setIsCreatingFilter] = useState(false);
|
||||||
|
|
||||||
const createFilterMutation = useCreateFilter();
|
const createFilterMutation = useCreateFilter();
|
||||||
|
const updateOnboardingMutation = useUpdateOnboardingStateMutation();
|
||||||
|
|
||||||
const STEP_LIST = [
|
const STEP_LIST = [
|
||||||
"Uploading your document",
|
"Uploading your document",
|
||||||
|
|
@ -103,12 +101,13 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
|
||||||
description: `Filter for ${filename}`,
|
description: `Filter for ${filename}`,
|
||||||
queryData: queryData,
|
queryData: queryData,
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then(async (result) => {
|
||||||
if (result.filter?.id && typeof window !== "undefined") {
|
if (result.filter?.id) {
|
||||||
localStorage.setItem(
|
// Save to backend
|
||||||
ONBOARDING_USER_DOC_FILTER_ID_KEY,
|
await updateOnboardingMutation.mutateAsync({
|
||||||
result.filter.id,
|
user_doc_filter_id: result.filter.id,
|
||||||
);
|
});
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
"Created knowledge filter for uploaded document",
|
"Created knowledge filter for uploaded document",
|
||||||
result.filter.id,
|
result.filter.id,
|
||||||
|
|
@ -267,7 +266,6 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
|
||||||
setCurrentStep={setCurrentStep}
|
setCurrentStep={setCurrentStep}
|
||||||
isCompleted={false}
|
isCompleted={false}
|
||||||
steps={STEP_LIST}
|
steps={STEP_LIST}
|
||||||
storageKey={ONBOARDING_UPLOAD_STEPS_KEY}
|
|
||||||
/>
|
/>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import {
|
||||||
} from "@/app/api/queries/useGetConversationsQuery";
|
} from "@/app/api/queries/useGetConversationsQuery";
|
||||||
import { getFilterById } from "@/app/api/queries/useGetFilterByIdQuery";
|
import { getFilterById } from "@/app/api/queries/useGetFilterByIdQuery";
|
||||||
import type { Settings } from "@/app/api/queries/useGetSettingsQuery";
|
import type { Settings } from "@/app/api/queries/useGetSettingsQuery";
|
||||||
|
import { useUpdateOnboardingStateMutation } from "@/app/api/mutations/useUpdateOnboardingStateMutation";
|
||||||
import { OnboardingContent } from "@/app/onboarding/_components/onboarding-content";
|
import { OnboardingContent } from "@/app/onboarding/_components/onboarding-content";
|
||||||
import { ProgressBar } from "@/app/onboarding/_components/progress-bar";
|
import { ProgressBar } from "@/app/onboarding/_components/progress-bar";
|
||||||
import { AnimatedConditional } from "@/components/animated-conditional";
|
import { AnimatedConditional } from "@/components/animated-conditional";
|
||||||
|
|
@ -19,13 +20,6 @@ import { useChat } from "@/contexts/chat-context";
|
||||||
import {
|
import {
|
||||||
ANIMATION_DURATION,
|
ANIMATION_DURATION,
|
||||||
HEADER_HEIGHT,
|
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,
|
SIDEBAR_WIDTH,
|
||||||
TOTAL_ONBOARDING_STEPS,
|
TOTAL_ONBOARDING_STEPS,
|
||||||
} from "@/lib/constants";
|
} from "@/lib/constants";
|
||||||
|
|
@ -50,21 +44,27 @@ export function ChatRenderer({
|
||||||
setOnboardingComplete,
|
setOnboardingComplete,
|
||||||
} = useChat();
|
} = useChat();
|
||||||
|
|
||||||
// Initialize onboarding state based on local storage and settings
|
// Initialize onboarding state from backend settings
|
||||||
const [currentStep, setCurrentStep] = useState<number>(() => {
|
const [currentStep, setCurrentStep] = useState<number>(() => {
|
||||||
if (typeof window === "undefined") return 0;
|
return settings?.onboarding?.current_step ?? 0;
|
||||||
const savedStep = localStorage.getItem(ONBOARDING_STEP_KEY);
|
|
||||||
return savedStep !== null ? parseInt(savedStep, 10) : 0;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const [showLayout, setShowLayout] = useState<boolean>(() => {
|
const [showLayout, setShowLayout] = useState<boolean>(() => {
|
||||||
if (typeof window === "undefined") return false;
|
// Show layout only if onboarding is complete (current_step >= TOTAL_ONBOARDING_STEPS)
|
||||||
const savedStep = localStorage.getItem(ONBOARDING_STEP_KEY);
|
// This means onboarding will show even if edited=true, as long as it's not complete
|
||||||
// Show layout if settings.edited is true and if no onboarding step is saved
|
const onboardingStep = settings?.onboarding?.current_step ?? 0;
|
||||||
const isEdited = settings?.edited ?? true;
|
return onboardingStep >= TOTAL_ONBOARDING_STEPS;
|
||||||
return isEdited ? savedStep === null : false;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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
|
// Only fetch conversations on chat page
|
||||||
const isOnChatPage = pathname === "/" || pathname === "/chat";
|
const isOnChatPage = pathname === "/" || pathname === "/chat";
|
||||||
const { data: conversations = [], isLoading: isConversationsLoading } =
|
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;
|
let filterId: string | null = null;
|
||||||
|
|
||||||
if (preferUserDoc) {
|
if (preferUserDoc) {
|
||||||
// Completed full onboarding - prefer user document filter
|
// 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);
|
console.log("[FILTER] User doc filter ID:", filterId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to OpenRAG docs filter
|
// Fall back to OpenRAG docs filter
|
||||||
if (!filterId) {
|
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);
|
console.log("[FILTER] OpenRAG docs filter ID:", filterId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,25 +149,29 @@ export function ChatRenderer({
|
||||||
[setConversationFilter]
|
[setConversationFilter]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Save current step to local storage whenever it changes
|
// Note: Current step is now saved to backend via handleStepComplete
|
||||||
useEffect(() => {
|
// No need to save on every change, only on completion
|
||||||
if (typeof window !== "undefined" && !showLayout) {
|
|
||||||
localStorage.setItem(ONBOARDING_STEP_KEY, currentStep.toString());
|
const updateOnboardingMutation = useUpdateOnboardingStateMutation();
|
||||||
}
|
|
||||||
}, [currentStep, showLayout]);
|
|
||||||
|
|
||||||
const handleStepComplete = async () => {
|
const handleStepComplete = async () => {
|
||||||
if (currentStep < TOTAL_ONBOARDING_STEPS - 1) {
|
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 {
|
} else {
|
||||||
// Onboarding is complete - remove from local storage and show layout
|
// Onboarding is complete - set step to TOTAL_ONBOARDING_STEPS to indicate completion
|
||||||
if (typeof window !== "undefined") {
|
// and clear intermediate state in backend
|
||||||
localStorage.removeItem(ONBOARDING_STEP_KEY);
|
await updateOnboardingMutation.mutateAsync({
|
||||||
localStorage.removeItem(ONBOARDING_ASSISTANT_MESSAGE_KEY);
|
current_step: TOTAL_ONBOARDING_STEPS,
|
||||||
localStorage.removeItem(ONBOARDING_SELECTED_NUDGE_KEY);
|
assistant_message: null,
|
||||||
localStorage.removeItem(ONBOARDING_CARD_STEPS_KEY);
|
selected_nudge: null,
|
||||||
localStorage.removeItem(ONBOARDING_UPLOAD_STEPS_KEY);
|
card_steps: null,
|
||||||
}
|
upload_steps: null,
|
||||||
|
openrag_docs_filter_id: null,
|
||||||
|
user_doc_filter_id: null,
|
||||||
|
});
|
||||||
|
|
||||||
// Mark onboarding as complete in context
|
// Mark onboarding as complete in context
|
||||||
setOnboardingComplete(true);
|
setOnboardingComplete(true);
|
||||||
|
|
@ -180,36 +184,35 @@ export function ChatRenderer({
|
||||||
// This will pick up the default filter we just set
|
// This will pick up the default filter we just set
|
||||||
await startNewConversation();
|
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);
|
setShowLayout(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleStepBack = () => {
|
const handleStepBack = async () => {
|
||||||
if (currentStep > 0) {
|
if (currentStep > 0) {
|
||||||
setCurrentStep(currentStep - 1);
|
const prevStep = currentStep - 1;
|
||||||
|
setCurrentStep(prevStep);
|
||||||
|
// Save step to backend
|
||||||
|
await updateOnboardingMutation.mutateAsync({ current_step: prevStep });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSkipOnboarding = () => {
|
const handleSkipOnboarding = async () => {
|
||||||
// Skip onboarding by marking it as complete
|
// Skip onboarding by marking it as complete in backend
|
||||||
if (typeof window !== "undefined") {
|
await updateOnboardingMutation.mutateAsync({
|
||||||
localStorage.removeItem(ONBOARDING_STEP_KEY);
|
current_step: TOTAL_ONBOARDING_STEPS,
|
||||||
localStorage.removeItem(ONBOARDING_ASSISTANT_MESSAGE_KEY);
|
assistant_message: null,
|
||||||
localStorage.removeItem(ONBOARDING_SELECTED_NUDGE_KEY);
|
selected_nudge: null,
|
||||||
localStorage.removeItem(ONBOARDING_CARD_STEPS_KEY);
|
card_steps: null,
|
||||||
localStorage.removeItem(ONBOARDING_UPLOAD_STEPS_KEY);
|
upload_steps: null,
|
||||||
}
|
openrag_docs_filter_id: null,
|
||||||
|
user_doc_filter_id: null,
|
||||||
|
});
|
||||||
|
|
||||||
// Mark onboarding as complete in context
|
// Mark onboarding as complete in context
|
||||||
setOnboardingComplete(true);
|
setOnboardingComplete(true);
|
||||||
// Store the OpenRAG docs filter as default for new conversations
|
// Store the OpenRAG docs filter as default for new conversations
|
||||||
storeDefaultFilterForNewConversations(false);
|
await storeDefaultFilterForNewConversations(false);
|
||||||
setShowLayout(true);
|
setShowLayout(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import {
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
import { ONBOARDING_STEP_KEY } from "@/lib/constants";
|
|
||||||
import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery";
|
import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery";
|
||||||
|
|
||||||
export type EndpointType = "chat" | "langflow";
|
export type EndpointType = "chat" | "langflow";
|
||||||
|
|
@ -129,28 +128,15 @@ export function ChatProvider({ children }: ChatProviderProps) {
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sync onboarding completion state with settings.edited and localStorage
|
// Sync onboarding completion state with settings from backend
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const checkOnboarding = () => {
|
const TOTAL_ONBOARDING_STEPS = 4;
|
||||||
if (typeof window !== "undefined") {
|
// Onboarding is complete if current_step >= 4
|
||||||
// Onboarding is complete if settings.edited is true AND step key is null
|
const isComplete =
|
||||||
const stepKeyExists = localStorage.getItem(ONBOARDING_STEP_KEY) !== null;
|
settings?.onboarding?.current_step !== undefined &&
|
||||||
const isEdited = settings?.edited === true;
|
settings.onboarding.current_step >= TOTAL_ONBOARDING_STEPS;
|
||||||
// Complete if edited is true and step key doesn't exist (onboarding flow finished)
|
setIsOnboardingComplete(isComplete);
|
||||||
setIsOnboardingComplete(isEdited && !stepKeyExists);
|
}, [settings?.onboarding?.current_step]);
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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 setOnboardingComplete = useCallback((complete: boolean) => {
|
const setOnboardingComplete = useCallback((complete: boolean) => {
|
||||||
setIsOnboardingComplete(complete);
|
setIsOnboardingComplete(complete);
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ import {
|
||||||
type TaskFileEntry,
|
type TaskFileEntry,
|
||||||
useGetTasksQuery,
|
useGetTasksQuery,
|
||||||
} from "@/app/api/queries/useGetTasksQuery";
|
} from "@/app/api/queries/useGetTasksQuery";
|
||||||
|
import { useGetSettingsQuery } from "@/app/api/queries/useGetSettingsQuery";
|
||||||
import { useAuth } from "@/contexts/auth-context";
|
import { useAuth } from "@/contexts/auth-context";
|
||||||
import { ONBOARDING_STEP_KEY } from "@/lib/constants";
|
|
||||||
|
|
||||||
// Task interface is now imported from useGetTasksQuery
|
// Task interface is now imported from useGetTasksQuery
|
||||||
export type { Task };
|
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
|
// Helper function to check if onboarding is active
|
||||||
const isOnboardingActive = useCallback(() => {
|
const isOnboardingActive = useCallback(() => {
|
||||||
if (typeof window === "undefined") return false;
|
const TOTAL_ONBOARDING_STEPS = 4;
|
||||||
return localStorage.getItem(ONBOARDING_STEP_KEY) !== null;
|
// 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(() => {
|
const refetchSearch = useCallback(() => {
|
||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
|
|
|
||||||
|
|
@ -37,17 +37,6 @@ export const SIDEBAR_WIDTH = 280;
|
||||||
export const HEADER_HEIGHT = 54;
|
export const HEADER_HEIGHT = 54;
|
||||||
export const TOTAL_ONBOARDING_STEPS = 4;
|
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 =
|
export const FILES_REGEX =
|
||||||
/(?<=I'm uploading a document called ['"])[^'"]+\.[^.]+(?=['"]\. Here is its content:)/;
|
/(?<=I'm uploading a document called ['"])[^'"]+\.[^.]+(?=['"]\. Here is its content:)/;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue