295 lines
7.2 KiB
TypeScript
295 lines
7.2 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect, useCallback } from "react";
|
|
import { useSession } from "next-auth/react";
|
|
import { useRouter } from "next/navigation";
|
|
import { Loader2 } from "lucide-react";
|
|
import { OnboardingProgress } from "./OnboardingProgress";
|
|
import { Step0HealthScore } from "./Step0HealthScore";
|
|
import { Step1BrandName } from "./Step1BrandName";
|
|
import { Step2Competitors } from "./Step2Competitors";
|
|
import { Step3Platforms } from "./Step3Platforms";
|
|
import { Step4HealthReport } from "./Step4HealthReport";
|
|
import { Step5ActionSuggestions } from "./Step5ActionSuggestions";
|
|
import { useOnboardingData } from "@/lib/hooks/use-onboarding-data";
|
|
import type { BrandHealthReport, OnboardingState } from "@/types/onboarding";
|
|
import type { HealthScoreResponse } from "@/lib/api/health-score";
|
|
|
|
const initialState: OnboardingState = {
|
|
currentStep: 0,
|
|
brandName: "",
|
|
competitors: [],
|
|
platforms: [],
|
|
frequency: "weekly",
|
|
brandId: null,
|
|
healthReport: null,
|
|
preCheckResult: null,
|
|
};
|
|
|
|
export default function OnboardingPage() {
|
|
const { status } = useSession();
|
|
const router = useRouter();
|
|
const [state, setState] = useState<OnboardingState>(initialState);
|
|
|
|
const {
|
|
isCompleted,
|
|
isLoading: isStatusLoading,
|
|
createBrand: hookCreateBrand,
|
|
isCreatingBrand,
|
|
mutationError,
|
|
createMonitoringTask,
|
|
} = useOnboardingData();
|
|
|
|
const error = mutationError?.message ?? null;
|
|
|
|
useEffect(() => {
|
|
if (isCompleted) {
|
|
router.push("/dashboard");
|
|
}
|
|
}, [isCompleted, router]);
|
|
|
|
const createBrand = useCallback(async () => {
|
|
if (!state.brandName) return null;
|
|
const brandId = await hookCreateBrand({
|
|
name: state.brandName,
|
|
competitors: state.competitors,
|
|
platforms: state.platforms,
|
|
frequency: state.frequency,
|
|
});
|
|
return brandId;
|
|
}, [
|
|
hookCreateBrand,
|
|
state.brandName,
|
|
state.competitors,
|
|
state.platforms,
|
|
state.frequency,
|
|
]);
|
|
|
|
const handleStep0Next = (
|
|
brandName: string,
|
|
healthScore: HealthScoreResponse | null,
|
|
) => {
|
|
setState((prev) => ({
|
|
...prev,
|
|
brandName,
|
|
preCheckResult: healthScore,
|
|
currentStep: 1,
|
|
}));
|
|
};
|
|
|
|
const handleStep1Next = (brandName: string) => {
|
|
setState((prev) => ({
|
|
...prev,
|
|
brandName,
|
|
currentStep: 2,
|
|
}));
|
|
};
|
|
|
|
const handleStep1Skip = () => {
|
|
router.push("/dashboard");
|
|
};
|
|
|
|
const handleStep2Next = (competitors: string[]) => {
|
|
setState((prev) => ({
|
|
...prev,
|
|
competitors,
|
|
currentStep: 3,
|
|
}));
|
|
};
|
|
|
|
const handleStep2Back = () => {
|
|
setState((prev) => ({
|
|
...prev,
|
|
currentStep: 1,
|
|
}));
|
|
};
|
|
|
|
const handleStep2Skip = async () => {
|
|
setState((prev) => ({
|
|
...prev,
|
|
competitors: [],
|
|
currentStep: 3,
|
|
}));
|
|
};
|
|
|
|
const handleStep3Next = async (
|
|
platforms: string[],
|
|
frequency: "daily" | "weekly" | "monthly",
|
|
) => {
|
|
const brandId = await createBrand();
|
|
if (brandId) {
|
|
createMonitoringTask(brandId, platforms, frequency);
|
|
setState((prev) => ({
|
|
...prev,
|
|
platforms,
|
|
frequency,
|
|
brandId,
|
|
currentStep: 4,
|
|
}));
|
|
}
|
|
};
|
|
|
|
const handleStep3Back = () => {
|
|
setState((prev) => ({
|
|
...prev,
|
|
currentStep: 2,
|
|
}));
|
|
};
|
|
|
|
const handleStep3Skip = async () => {
|
|
const defaultPlatforms = [
|
|
"wenxin",
|
|
"kimi",
|
|
"tongyi",
|
|
"baidu_ai",
|
|
"yuanbao",
|
|
"qingyan",
|
|
"doubao",
|
|
];
|
|
const brandId = await createBrand();
|
|
if (brandId) {
|
|
createMonitoringTask(brandId, defaultPlatforms, state.frequency);
|
|
setState((prev) => ({
|
|
...prev,
|
|
platforms: defaultPlatforms,
|
|
brandId,
|
|
currentStep: 4,
|
|
}));
|
|
}
|
|
};
|
|
|
|
const handleStep4Next = (report: BrandHealthReport) => {
|
|
setState((prev) => ({
|
|
...prev,
|
|
healthReport: report,
|
|
currentStep: 5,
|
|
}));
|
|
};
|
|
|
|
const handleStep4Back = () => {
|
|
setState((prev) => ({
|
|
...prev,
|
|
currentStep: 3,
|
|
}));
|
|
};
|
|
|
|
const handleStep5Complete = () => {
|
|
router.push("/dashboard");
|
|
};
|
|
|
|
const handleStep5Back = () => {
|
|
if (state.brandId) {
|
|
setState((prev) => ({
|
|
...prev,
|
|
currentStep: 4,
|
|
}));
|
|
}
|
|
};
|
|
|
|
const handleStep5Skip = () => {
|
|
router.push("/dashboard");
|
|
};
|
|
|
|
if (status === "loading" || isStatusLoading) {
|
|
return (
|
|
<div className="flex min-h-screen items-center justify-center">
|
|
<Loader2 className="h-8 w-8 animate-spin text-primary" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (status === "unauthenticated") {
|
|
if (state.currentStep === 0) {
|
|
// Step0 不需要登录
|
|
} else {
|
|
router.push("/login");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gradient-to-b from-background to-muted/20 px-4 py-8">
|
|
<div className="mx-auto max-w-4xl">
|
|
<div className="mb-8 text-center">
|
|
<h1 className="text-2xl font-bold">GEO 平台</h1>
|
|
<p className="text-muted-foreground">新用户引导</p>
|
|
</div>
|
|
|
|
<div className="mb-8">
|
|
<OnboardingProgress currentStep={state.currentStep} />
|
|
</div>
|
|
|
|
{error && (
|
|
<div className="mb-4 rounded-lg bg-destructive/10 p-4 text-destructive text-center">
|
|
{error}
|
|
</div>
|
|
)}
|
|
|
|
{isCreatingBrand && (
|
|
<div className="mb-4 rounded-lg bg-primary/10 p-4 text-center">
|
|
<Loader2 className="mx-auto h-6 w-6 animate-spin text-primary" />
|
|
<p className="mt-2 text-sm">正在创建品牌...</p>
|
|
</div>
|
|
)}
|
|
|
|
<div className="mt-8">
|
|
{state.currentStep === 0 && (
|
|
<Step0HealthScore
|
|
initialBrandName={state.brandName}
|
|
onNext={handleStep0Next}
|
|
/>
|
|
)}
|
|
|
|
{state.currentStep === 1 && (
|
|
<Step1BrandName
|
|
initialValue={state.brandName}
|
|
onNext={handleStep1Next}
|
|
onSkip={handleStep1Skip}
|
|
/>
|
|
)}
|
|
|
|
{state.currentStep === 2 && (
|
|
<Step2Competitors
|
|
brandName={state.brandName}
|
|
initialCompetitors={state.competitors}
|
|
onNext={handleStep2Next}
|
|
onBack={handleStep2Back}
|
|
onSkip={handleStep2Skip}
|
|
/>
|
|
)}
|
|
|
|
{state.currentStep === 3 && (
|
|
<Step3Platforms
|
|
initialPlatforms={state.platforms}
|
|
onNext={handleStep3Next}
|
|
onBack={handleStep3Back}
|
|
onSkip={handleStep3Skip}
|
|
/>
|
|
)}
|
|
|
|
{state.currentStep === 4 && state.brandId && (
|
|
<Step4HealthReport
|
|
brandId={state.brandId}
|
|
brandName={state.brandName}
|
|
competitors={state.competitors}
|
|
platforms={state.platforms}
|
|
onNext={handleStep4Next}
|
|
onBack={handleStep4Back}
|
|
/>
|
|
)}
|
|
|
|
{state.currentStep === 5 && state.brandId && (
|
|
<Step5ActionSuggestions
|
|
brandId={state.brandId}
|
|
brandName={state.brandName}
|
|
onComplete={handleStep5Complete}
|
|
onBack={handleStep5Back}
|
|
onSkip={handleStep5Skip}
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|