Finish up loading states

This commit is contained in:
Mike Fortman 2025-10-22 17:05:24 -05:00
parent 3ebb1adac7
commit 8c2d58183c
4 changed files with 79 additions and 32 deletions

View file

@ -1,6 +1,6 @@
"use client"; "use client";
import { useState } from "react"; import { useEffect, useState } from "react";
import { StickToBottom } from "use-stick-to-bottom"; import { StickToBottom } from "use-stick-to-bottom";
import { AssistantMessage } from "@/app/chat/components/assistant-message"; import { AssistantMessage } from "@/app/chat/components/assistant-message";
import { UserMessage } from "@/app/chat/components/user-message"; import { UserMessage } from "@/app/chat/components/user-message";
@ -59,6 +59,12 @@ export function OnboardingContent({
// Determine which message to show (streaming takes precedence) // Determine which message to show (streaming takes precedence)
const displayMessage = streamingMessage || assistantMessage; const displayMessage = streamingMessage || assistantMessage;
useEffect(() => {
if (currentStep === 1 && !isLoading && !!displayMessage) {
handleStepComplete();
}
}, [isLoading, displayMessage, handleStepComplete]);
return ( return (
<StickToBottom <StickToBottom
className="flex h-full flex-1 flex-col" className="flex h-full flex-1 flex-col"
@ -68,6 +74,7 @@ export function OnboardingContent({
> >
<StickToBottom.Content className="flex flex-col min-h-full overflow-x-hidden px-8 py-6"> <StickToBottom.Content className="flex flex-col min-h-full overflow-x-hidden px-8 py-6">
<div className="flex flex-col place-self-center w-full space-y-6"> <div className="flex flex-col place-self-center w-full space-y-6">
{/* Step 1 */}
<OnboardingStep <OnboardingStep
isVisible={currentStep >= 0} isVisible={currentStep >= 0}
isCompleted={currentStep > 0} isCompleted={currentStep > 0}
@ -76,6 +83,7 @@ export function OnboardingContent({
<OnboardingCard onComplete={handleStepComplete} /> <OnboardingCard onComplete={handleStepComplete} />
</OnboardingStep> </OnboardingStep>
{/* Step 2 */}
<OnboardingStep <OnboardingStep
isVisible={currentStep >= 1} isVisible={currentStep >= 1}
isCompleted={currentStep > 1 || !!selectedNudge} isCompleted={currentStep > 1 || !!selectedNudge}
@ -94,7 +102,7 @@ export function OnboardingContent({
{currentStep >= 1 && !!selectedNudge && ( {currentStep >= 1 && !!selectedNudge && (
<UserMessage <UserMessage
content={selectedNudge} content={selectedNudge}
isCompleted={currentStep > 1} isCompleted={currentStep > 2}
/> />
)} )}
@ -109,13 +117,13 @@ export function OnboardingContent({
expandedFunctionCalls={new Set()} expandedFunctionCalls={new Set()}
onToggle={() => {}} onToggle={() => {}}
isStreaming={!!streamingMessage} isStreaming={!!streamingMessage}
isCompleted={currentStep > 1} isCompleted={currentStep > 2}
/> />
)} )}
{/* Still kind of part of step 2 */} {/* Step 3 */}
<OnboardingStep <OnboardingStep
isVisible={currentStep === 1 && !isLoading && !!displayMessage} isVisible={currentStep >= 2 && !isLoading && !!displayMessage}
isCompleted={currentStep > 2} isCompleted={currentStep > 2}
text="Now, let's add your data." text="Now, let's add your data."
hideIcon={true} hideIcon={true}
@ -123,9 +131,10 @@ export function OnboardingContent({
<OnboardingUpload onComplete={handleStepComplete} /> <OnboardingUpload onComplete={handleStepComplete} />
</OnboardingStep> </OnboardingStep>
{/* Step 4 */}
<OnboardingStep <OnboardingStep
isVisible={currentStep >= 2} isVisible={currentStep >= 3}
isCompleted={currentStep > 2} isCompleted={currentStep > 3}
text="Step 3: You're all set!" text="Step 3: You're all set!"
> >
<div className="space-y-4"> <div className="space-y-4">

View file

@ -1,6 +1,8 @@
import { ChangeEvent, useRef, useState } from "react"; import { ChangeEvent, useRef, useState } from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { duplicateCheck, uploadFile } from "@/lib/upload-utils"; import { duplicateCheck, uploadFile } from "@/lib/upload-utils";
import { AnimatePresence, motion } from "motion/react";
import { AnimatedProviderSteps } from "@/app/onboarding/components/animated-provider-steps";
interface OnboardingUploadProps { interface OnboardingUploadProps {
onComplete: () => void; onComplete: () => void;
@ -9,6 +11,12 @@ interface OnboardingUploadProps {
const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => { const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
const fileInputRef = useRef<HTMLInputElement>(null); const fileInputRef = useRef<HTMLInputElement>(null);
const [isUploading, setIsUploading] = useState(false); const [isUploading, setIsUploading] = useState(false);
const [currentStep, setCurrentStep] = useState<number | null>(null);
const STEP_LIST = [
"Analyzing your document",
"Ingesting your document",
];
const resetFileInput = () => { const resetFileInput = () => {
if (fileInputRef.current) { if (fileInputRef.current) {
@ -24,12 +32,14 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
const performUpload = async (file: File, replace = false) => { const performUpload = async (file: File, replace = false) => {
setIsUploading(true); setIsUploading(true);
try { try {
setCurrentStep(1);
await uploadFile(file, replace); await uploadFile(file, replace);
console.log("Document uploaded successfully"); console.log("Document uploaded successfully");
} catch (error) { } catch (error) {
console.error("Upload failed", (error as Error).message); console.error("Upload failed", (error as Error).message);
} finally { } finally {
setIsUploading(false); setIsUploading(false);
setCurrentStep(STEP_LIST.length);
onComplete(); onComplete();
} }
}; };
@ -42,6 +52,7 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
} }
try { try {
setCurrentStep(0);
const duplicateInfo = await duplicateCheck(selectedFile); const duplicateInfo = await duplicateCheck(selectedFile);
if (duplicateInfo.exists) { if (duplicateInfo.exists) {
console.log("Duplicate file detected"); console.log("Duplicate file detected");
@ -58,23 +69,45 @@ const OnboardingUpload = ({ onComplete }: OnboardingUploadProps) => {
return ( return (
<div className="space-y-4"> <AnimatePresence mode="wait">
<Button {currentStep === null ? (
size="sm" <motion.div
variant="outline" key="user-ingest"
onClick={handleUploadClick} initial={{ opacity: 1, y: 0 }}
disabled={isUploading} exit={{ opacity: 0, y: -24 }}
> transition={{ duration: 0.4, ease: "easeInOut" }}
{isUploading ? "Uploading..." : "Add a Document"} >
</Button> <Button
<input size="sm"
ref={fileInputRef} variant="outline"
type="file" onClick={handleUploadClick}
onChange={handleFileChange} disabled={isUploading}
className="hidden" >
accept=".pdf,.doc,.docx,.txt,.md,.rtf,.odt" {isUploading ? "Uploading..." : "Add a Document"}
/> </Button>
</div> <input
ref={fileInputRef}
type="file"
onChange={handleFileChange}
className="hidden"
accept=".pdf,.doc,.docx,.txt,.md,.rtf,.odt"
/>
</motion.div>
) : (
<motion.div
key="ingest-steps"
initial={{ opacity: 0, y: 24 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, ease: "easeInOut" }}
>
<AnimatedProviderSteps
currentStep={currentStep}
setCurrentStep={setCurrentStep}
steps={STEP_LIST}
/>
</motion.div>
)}
</AnimatePresence>
) )
} }

View file

@ -9,16 +9,12 @@ import { cn } from "@/lib/utils";
export function AnimatedProviderSteps({ export function AnimatedProviderSteps({
currentStep, currentStep,
setCurrentStep, setCurrentStep,
steps,
}: { }: {
currentStep: number; currentStep: number;
setCurrentStep: (step: number) => void; setCurrentStep: (step: number) => void;
steps: string[];
}) { }) {
const steps = [
"Setting up your model provider",
"Defining schema",
"Configuring Langflow",
"Ingesting sample data",
];
useEffect(() => { useEffect(() => {
if (currentStep < steps.length - 1) { if (currentStep < steps.length - 1) {
@ -27,7 +23,7 @@ export function AnimatedProviderSteps({
}, 1000); }, 1000);
return () => clearInterval(interval); return () => clearInterval(interval);
} }
}, [currentStep, setCurrentStep]); }, [currentStep, setCurrentStep, steps]);
const isDone = currentStep >= steps.length; const isDone = currentStep >= steps.length;

View file

@ -34,7 +34,15 @@ interface OnboardingCardProps {
onComplete: () => void; onComplete: () => void;
} }
const TOTAL_PROVIDER_STEPS = 4;
const STEP_LIST = [
"Setting up your model provider",
"Defining schema",
"Configuring Langflow",
"Ingesting sample data",
];
const TOTAL_PROVIDER_STEPS = STEP_LIST.length;
const OnboardingCard = ({ onComplete }: OnboardingCardProps) => { const OnboardingCard = ({ onComplete }: OnboardingCardProps) => {
const updatedOnboarding = process.env.UPDATED_ONBOARDING === "true"; const updatedOnboarding = process.env.UPDATED_ONBOARDING === "true";
@ -245,6 +253,7 @@ const OnboardingCard = ({ onComplete }: OnboardingCardProps) => {
<AnimatedProviderSteps <AnimatedProviderSteps
currentStep={currentStep} currentStep={currentStep}
setCurrentStep={setCurrentStep} setCurrentStep={setCurrentStep}
steps={STEP_LIST}
/> />
</motion.div> </motion.div>
)} )}