core/apps/webapp/app/components/onboarding/onboarding-modal.tsx
Harshith Mullapudi 00f983079f 1. Added onboarding
2. fix: remove custom tool calls in chat and link directly to mcp
2025-10-21 11:32:36 +05:30

231 lines
6.9 KiB
TypeScript

import { useState } from "react";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog";
import { type Provider, OnboardingStep } from "./types";
import { ProviderSelectionStep } from "./provider-selection-step";
import { IngestionStep } from "./ingestion-step";
import { VerificationStep } from "./verification-step";
import { PROVIDER_CONFIGS } from "./provider-config";
import { Progress } from "../ui/progress";
interface OnboardingModalProps {
isOpen: boolean;
onClose: () => void;
onComplete: () => void;
}
export function OnboardingModal({
isOpen,
onClose,
onComplete,
}: OnboardingModalProps) {
const [currentStep, setCurrentStep] = useState<OnboardingStep>(
OnboardingStep.PROVIDER_SELECTION,
);
const [selectedProvider, setSelectedProvider] = useState<Provider>();
const [ingestionStatus, setIngestionStatus] = useState<
"idle" | "waiting" | "processing" | "complete" | "error"
>("idle");
const [verificationResult, setVerificationResult] = useState<string>();
const [isCheckingRecall, setIsCheckingRecall] = useState(false);
const [error, setError] = useState<string>();
// Calculate progress
const getProgress = () => {
switch (currentStep) {
case OnboardingStep.PROVIDER_SELECTION:
return 33;
case OnboardingStep.FIRST_INGESTION:
return 66;
case OnboardingStep.VERIFICATION:
return 100;
default:
return 0;
}
};
// Poll for ingestion status
const pollIngestion = async () => {
setIngestionStatus("waiting");
try {
const maxAttempts = 30; // 60 seconds (30 * 2s)
let attempts = 0;
// Store the timestamp when polling starts
const startTime = Date.now();
const poll = async (): Promise<boolean> => {
if (attempts >= maxAttempts) {
throw new Error("Ingestion timeout - please try again");
}
// Check for new ingestion logs from the last 5 minutes
const response = await fetch("/api/v1/logs?limit=1");
const data = await response.json();
// Check if there's a recent ingestion (created after we started polling)
if (data.logs && data.logs.length > 0) {
const latestLog = data.logs[0];
const logTime = new Date(latestLog.time).getTime();
// If the log was created after we started polling, we found a new ingestion
if (logTime >= startTime) {
return true;
}
}
await new Promise((resolve) => setTimeout(resolve, 2000));
attempts++;
return poll();
};
const success = await poll();
if (success) {
setIngestionStatus("complete");
// Auto-advance to verification step after 2 seconds
setTimeout(() => {
setCurrentStep(OnboardingStep.VERIFICATION);
}, 2000);
}
} catch (err) {
setError(err instanceof Error ? err.message : "Unknown error occurred");
setIngestionStatus("error");
}
};
const handleProviderSelect = (provider: Provider) => {
setSelectedProvider(provider);
};
const handleContinueFromProvider = () => {
setCurrentStep(OnboardingStep.FIRST_INGESTION);
};
const handleStartWaiting = () => {
pollIngestion();
};
const handleComplete = () => {
setCurrentStep(OnboardingStep.COMPLETE);
onComplete();
onClose();
};
// Poll for recall logs to detect verification
const pollRecallLogs = async () => {
setIsCheckingRecall(true);
try {
const maxAttempts = 30; // 60 seconds
let attempts = 0;
const startTime = Date.now();
const poll = async (): Promise<string | null> => {
if (attempts >= maxAttempts) {
throw new Error("Verification timeout - please try again");
}
// Check for new recall logs
const response = await fetch("/api/v1/recall-logs?limit=1");
const data = await response.json();
// Check if there's a recent recall (created after we started polling)
if (data.recallLogs && data.recallLogs.length > 0) {
const latestRecall = data.recallLogs[0];
const recallTime = new Date(latestRecall.createdAt).getTime();
// If the recall was created after we started polling
if (recallTime >= startTime) {
// Return the query as verification result
return latestRecall.query || "Recall detected successfully";
}
}
await new Promise((resolve) => setTimeout(resolve, 2000));
attempts++;
return poll();
};
const result = await poll();
if (result) {
setVerificationResult(result);
setIsCheckingRecall(false);
}
} catch (err) {
setError(err instanceof Error ? err.message : "Unknown error occurred");
setIsCheckingRecall(false);
}
};
const getStepTitle = () => {
switch (currentStep) {
case OnboardingStep.PROVIDER_SELECTION:
return "Step 1 of 3";
case OnboardingStep.FIRST_INGESTION:
return "Step 2 of 3";
case OnboardingStep.VERIFICATION:
return "Step 3 of 3";
default:
return "";
}
};
return (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="max-h-[90vh] max-w-3xl overflow-y-auto p-4">
<DialogHeader>
<div className="space-y-3">
<DialogTitle className="text-2xl">Welcome to Core</DialogTitle>
<div className="space-y-2">
<div className="flex items-center justify-between">
<p className="text-muted-foreground text-sm">
{getStepTitle()}
</p>
</div>
<Progress
segments={[{ value: getProgress() }]}
className="mb-2"
color="#c15e50"
/>
</div>
</div>
</DialogHeader>
<div>
{currentStep === OnboardingStep.PROVIDER_SELECTION && (
<ProviderSelectionStep
selectedProvider={selectedProvider}
onSelectProvider={handleProviderSelect}
onContinue={handleContinueFromProvider}
/>
)}
{currentStep === OnboardingStep.FIRST_INGESTION &&
selectedProvider && (
<IngestionStep
providerName={PROVIDER_CONFIGS[selectedProvider].name}
ingestionStatus={ingestionStatus}
onStartWaiting={handleStartWaiting}
error={error}
/>
)}
{currentStep === OnboardingStep.VERIFICATION && selectedProvider && (
<VerificationStep
providerName={PROVIDER_CONFIGS[selectedProvider].name}
verificationResult={verificationResult}
isCheckingRecall={isCheckingRecall}
onStartChecking={pollRecallLogs}
onComplete={handleComplete}
/>
)}
</div>
</DialogContent>
</Dialog>
);
}