Files
users/app/ai-assistant/page.tsx
v0 2408d50cb0 refactor: overhaul UI for streamlined user experience
Redesign navigation, home overview, user portrait, and valuation pages
with improved functionality and responsive design.

Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
2025-07-18 13:47:12 +00:00

787 lines
34 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import { useState } from "react"
import { Card, CardContent, CardDescription, CardHeader, CardTitle, CardFooter } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Textarea } from "@/components/ui/textarea"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Switch } from "@/components/ui/switch"
import { Badge } from "@/components/ui/badge"
import { Separator } from "@/components/ui/separator"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
import { Sparkles, BarChart, TrendingUp, Users, Mail, Clock, Download, ChevronRight } from "lucide-react"
export default function AIAssistantPage() {
const [activeTab, setActiveTab] = useState("data-analysis")
const [selectedUserGroup, setSelectedUserGroup] = useState("")
const [isUserGroupDialogOpen, setIsUserGroupDialogOpen] = useState(false)
const [emailAddress, setEmailAddress] = useState("")
const [scheduleReport, setScheduleReport] = useState(false)
// 分析流程步骤
const [analysisStep, setAnalysisStep] = useState(1)
const [analysisType, setAnalysisType] = useState("")
// 营销策略步骤
const [strategyStep, setStrategyStep] = useState(1)
const [strategyType, setStrategyType] = useState("")
return (
<div className="container mx-auto py-6 space-y-8">
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
<div>
<h1 className="text-3xl font-bold tracking-tight">AI </h1>
<p className="text-muted-foreground">AI技术分析数据并提供营销策略</p>
</div>
</div>
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
<TabsList className="grid w-full max-w-md grid-cols-2">
<TabsTrigger value="data-analysis"></TabsTrigger>
<TabsTrigger value="marketing-strategy"></TabsTrigger>
</TabsList>
<TabsContent value="data-analysis" className="space-y-6">
<Card>
<CardHeader>
<CardTitle>AI </CardTitle>
<CardDescription>使AI分析用户数据并生成洞察报告</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
{/* 第1步选择用户分群 */}
<div className={`space-y-4 ${analysisStep !== 1 ? "opacity-60" : ""}`}>
<div className="flex items-center justify-between">
<h3 className="text-lg font-medium flex items-center">
<div className="flex items-center justify-center w-6 h-6 rounded-full bg-primary text-white text-sm mr-2">
1
</div>
</h3>
{analysisStep > 1 ? (
<Button variant="outline" size="sm" onClick={() => setAnalysisStep(1)}>
</Button>
) : (
<Button variant="outline" onClick={() => setIsUserGroupDialogOpen(true)}>
<Users className="mr-2 h-4 w-4" />
</Button>
)}
</div>
{selectedUserGroup && (
<div className="p-4 border rounded-lg bg-muted/50">
<div className="flex justify-between items-center">
<div className="flex items-center gap-2">
<Users className="h-5 w-5 text-muted-foreground" />
<span className="font-medium"></span>
<Badge className="bg-blue-100 text-blue-800">{selectedUserGroup}</Badge>
</div>
{analysisStep === 1 && (
<Button
onClick={() => {
setAnalysisStep(2)
}}
disabled={!selectedUserGroup}
>
<ChevronRight className="ml-1 h-4 w-4" />
</Button>
)}
</div>
</div>
)}
</div>
<Separator />
{/* 第2步选择分析类型 */}
<div
className={`space-y-4 ${analysisStep !== 2 ? (analysisStep > 2 ? "opacity-60" : "opacity-40") : ""}`}
>
<div className="flex items-center justify-between">
<h3 className="text-lg font-medium flex items-center">
<div className="flex items-center justify-center w-6 h-6 rounded-full bg-primary text-white text-sm mr-2">
2
</div>
</h3>
{analysisStep > 2 && (
<Button variant="outline" size="sm" onClick={() => setAnalysisStep(2)}>
</Button>
)}
</div>
{analysisStep >= 2 && (
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<Card
className={`border-2 cursor-pointer hover:shadow-md transition-all ${analysisType === "behavior" ? "border-primary" : "border-transparent"}`}
onClick={() => setAnalysisType("behavior")}
>
<CardHeader className="pb-2">
<CardTitle className="text-base"></CardTitle>
</CardHeader>
<CardContent className="text-sm text-muted-foreground">
</CardContent>
<CardFooter>
{analysisType === "behavior" && <Badge className="bg-primary/10 text-primary"></Badge>}
</CardFooter>
</Card>
<Card
className={`border-2 cursor-pointer hover:shadow-md transition-all ${analysisType === "value" ? "border-primary" : "border-transparent"}`}
onClick={() => setAnalysisType("value")}
>
<CardHeader className="pb-2">
<CardTitle className="text-base"></CardTitle>
</CardHeader>
<CardContent className="text-sm text-muted-foreground"></CardContent>
<CardFooter>
{analysisType === "value" && <Badge className="bg-primary/10 text-primary"></Badge>}
</CardFooter>
</Card>
<Card
className={`border-2 cursor-pointer hover:shadow-md transition-all ${analysisType === "churn" ? "border-primary" : "border-transparent"}`}
onClick={() => setAnalysisType("churn")}
>
<CardHeader className="pb-2">
<CardTitle className="text-base"></CardTitle>
</CardHeader>
<CardContent className="text-sm text-muted-foreground">
</CardContent>
<CardFooter>
{analysisType === "churn" && <Badge className="bg-primary/10 text-primary"></Badge>}
</CardFooter>
</Card>
</div>
)}
{analysisStep === 2 && analysisType && (
<div className="flex justify-end">
<Button onClick={() => setAnalysisStep(3)}>
<ChevronRight className="ml-1 h-4 w-4" />
</Button>
</div>
)}
</div>
<Separator />
{/* 第3步分析参数设置 */}
<div
className={`space-y-4 ${analysisStep !== 3 ? (analysisStep > 3 ? "opacity-60" : "opacity-40") : ""}`}
>
<div className="flex items-center justify-between">
<h3 className="text-lg font-medium flex items-center">
<div className="flex items-center justify-center w-6 h-6 rounded-full bg-primary text-white text-sm mr-2">
3
</div>
</h3>
{analysisStep > 3 && (
<Button variant="outline" size="sm" onClick={() => setAnalysisStep(3)}>
</Button>
)}
</div>
{analysisStep >= 3 && (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="time-range"></Label>
<Select defaultValue="30days">
<SelectTrigger id="time-range">
<SelectValue placeholder="选择时间范围" />
</SelectTrigger>
<SelectContent>
<SelectItem value="7days">7</SelectItem>
<SelectItem value="30days">30</SelectItem>
<SelectItem value="90days">90</SelectItem>
<SelectItem value="custom"></SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="analysis-depth"></Label>
<Select defaultValue="medium">
<SelectTrigger id="analysis-depth">
<SelectValue placeholder="选择分析深度" />
</SelectTrigger>
<SelectContent>
<SelectItem value="basic"></SelectItem>
<SelectItem value="medium"></SelectItem>
<SelectItem value="deep"></SelectItem>
</SelectContent>
</Select>
</div>
</div>
)}
{analysisStep === 3 && (
<div className="flex justify-end">
<Button onClick={() => setAnalysisStep(4)}>
<ChevronRight className="ml-1 h-4 w-4" />
</Button>
</div>
)}
</div>
<Separator />
{/* 第4步报告设置 */}
<div className={`space-y-4 ${analysisStep !== 4 ? "opacity-40" : ""}`}>
<div className="flex items-center justify-between">
<h3 className="text-lg font-medium flex items-center">
<div className="flex items-center justify-center w-6 h-6 rounded-full bg-primary text-white text-sm mr-2">
4
</div>
</h3>
</div>
{analysisStep >= 4 && (
<div className="space-y-4">
<div className="flex flex-col space-y-2">
<Label htmlFor="email"></Label>
<Input
id="email"
type="email"
placeholder="输入邮箱地址"
value={emailAddress}
onChange={(e) => setEmailAddress(e.target.value)}
/>
</div>
<div className="flex items-center space-x-2">
<Switch id="schedule-report" checked={scheduleReport} onCheckedChange={setScheduleReport} />
<Label htmlFor="schedule-report"></Label>
</div>
{scheduleReport && (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 pl-6">
<div className="space-y-2">
<Label htmlFor="schedule-frequency"></Label>
<Select defaultValue="weekly">
<SelectTrigger id="schedule-frequency">
<SelectValue placeholder="选择发送频率" />
</SelectTrigger>
<SelectContent>
<SelectItem value="daily"></SelectItem>
<SelectItem value="weekly"></SelectItem>
<SelectItem value="monthly"></SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="schedule-time"></Label>
<Select defaultValue="morning">
<SelectTrigger id="schedule-time">
<SelectValue placeholder="选择发送时间" />
</SelectTrigger>
<SelectContent>
<SelectItem value="morning"> 9:00</SelectItem>
<SelectItem value="noon"> 12:00</SelectItem>
<SelectItem value="evening"> 6:00</SelectItem>
</SelectContent>
</Select>
</div>
</div>
)}
</div>
)}
</div>
</CardContent>
<CardFooter className="flex justify-between">
<Button
variant="outline"
onClick={() => {
setAnalysisStep(1)
setAnalysisType("")
}}
>
</Button>
<Button disabled={analysisStep < 4 || !emailAddress}>
<Sparkles className="mr-2 h-4 w-4" />
</Button>
</CardFooter>
</Card>
<Card>
<CardHeader>
<CardTitle></CardTitle>
<CardDescription>AI生成的数据分析结果</CardDescription>
</CardHeader>
<CardContent className="h-96 flex items-center justify-center">
{selectedUserGroup && analysisStep === 4 ? (
<div className="text-center space-y-4">
<BarChart className="h-16 w-16 mx-auto text-primary" />
<p className="text-lg font-medium"></p>
<p className="text-muted-foreground">"开始分析"</p>
<div className="flex justify-center gap-2 pt-4">
<Button variant="outline">
<Download className="mr-2 h-4 w-4" />
</Button>
<Button>
<Mail className="mr-2 h-4 w-4" />
</Button>
</div>
</div>
) : (
<div className="text-center text-muted-foreground">
<Users className="h-16 w-16 mx-auto mb-4" />
<p className="text-lg"></p>
<Button variant="outline" className="mt-4" onClick={() => setIsUserGroupDialogOpen(true)}>
<Users className="mr-2 h-4 w-4" />
</Button>
</div>
)}
</CardContent>
</Card>
</TabsContent>
<TabsContent value="marketing-strategy" className="space-y-6">
<Card>
<CardHeader>
<CardTitle>AI </CardTitle>
<CardDescription>使AI生成针对性的营销策略和建议</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
{/* 第1步选择目标用户分群 */}
<div className={`space-y-4 ${strategyStep !== 1 ? "opacity-60" : ""}`}>
<div className="flex items-center justify-between">
<h3 className="text-lg font-medium flex items-center">
<div className="flex items-center justify-center w-6 h-6 rounded-full bg-primary text-white text-sm mr-2">
1
</div>
</h3>
{strategyStep > 1 ? (
<Button variant="outline" size="sm" onClick={() => setStrategyStep(1)}>
</Button>
) : (
<Button variant="outline" onClick={() => setIsUserGroupDialogOpen(true)}>
<Users className="mr-2 h-4 w-4" />
</Button>
)}
</div>
{selectedUserGroup && (
<div className="p-4 border rounded-lg bg-muted/50">
<div className="flex justify-between items-center">
<div className="flex items-center gap-2">
<Users className="h-5 w-5 text-muted-foreground" />
<span className="font-medium"></span>
<Badge className="bg-blue-100 text-blue-800">{selectedUserGroup}</Badge>
</div>
{strategyStep === 1 && (
<Button
onClick={() => {
setStrategyStep(2)
}}
disabled={!selectedUserGroup}
>
<ChevronRight className="ml-1 h-4 w-4" />
</Button>
)}
</div>
</div>
)}
</div>
<Separator />
{/* 第2步选择策略类型 */}
<div
className={`space-y-4 ${strategyStep !== 2 ? (strategyStep > 2 ? "opacity-60" : "opacity-40") : ""}`}
>
<div className="flex items-center justify-between">
<h3 className="text-lg font-medium flex items-center">
<div className="flex items-center justify-center w-6 h-6 rounded-full bg-primary text-white text-sm mr-2">
2
</div>
</h3>
{strategyStep > 2 && (
<Button variant="outline" size="sm" onClick={() => setStrategyStep(2)}>
</Button>
)}
</div>
{strategyStep >= 2 && (
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<Card
className={`border-2 cursor-pointer hover:shadow-md transition-all ${strategyType === "sales" ? "border-primary" : "border-transparent"}`}
onClick={() => setStrategyType("sales")}
>
<CardHeader className="pb-2">
<CardTitle className="text-base"></CardTitle>
</CardHeader>
<CardContent className="text-sm text-muted-foreground">
</CardContent>
<CardFooter>
{strategyType === "sales" && <Badge className="bg-primary/10 text-primary"></Badge>}
</CardFooter>
</Card>
<Card
className={`border-2 cursor-pointer hover:shadow-md transition-all ${strategyType === "reach" ? "border-primary" : "border-transparent"}`}
onClick={() => setStrategyType("reach")}
>
<CardHeader className="pb-2">
<CardTitle className="text-base"></CardTitle>
</CardHeader>
<CardContent className="text-sm text-muted-foreground">
</CardContent>
<CardFooter>
{strategyType === "reach" && <Badge className="bg-primary/10 text-primary"></Badge>}
</CardFooter>
</Card>
<Card
className={`border-2 cursor-pointer hover:shadow-md transition-all ${strategyType === "content" ? "border-primary" : "border-transparent"}`}
onClick={() => setStrategyType("content")}
>
<CardHeader className="pb-2">
<CardTitle className="text-base"></CardTitle>
</CardHeader>
<CardContent className="text-sm text-muted-foreground">
</CardContent>
<CardFooter>
{strategyType === "content" && <Badge className="bg-primary/10 text-primary"></Badge>}
</CardFooter>
</Card>
</div>
)}
{strategyStep === 2 && strategyType && (
<div className="flex justify-end">
<Button onClick={() => setStrategyStep(3)}>
<ChevronRight className="ml-1 h-4 w-4" />
</Button>
</div>
)}
</div>
<Separator />
{/* 第3步策略参数设置 */}
<div className={`space-y-4 ${strategyStep !== 3 ? "opacity-40" : ""}`}>
<div className="flex items-center justify-between">
<h3 className="text-lg font-medium flex items-center">
<div className="flex items-center justify-center w-6 h-6 rounded-full bg-primary text-white text-sm mr-2">
3
</div>
</h3>
</div>
{strategyStep >= 3 && (
<>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="strategy-goal"></Label>
<Select defaultValue="conversion">
<SelectTrigger id="strategy-goal">
<SelectValue placeholder="选择营销目标" />
</SelectTrigger>
<SelectContent>
<SelectItem value="awareness"></SelectItem>
<SelectItem value="engagement"></SelectItem>
<SelectItem value="conversion"></SelectItem>
<SelectItem value="retention"></SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="strategy-budget"></Label>
<Select defaultValue="medium">
<SelectTrigger id="strategy-budget">
<SelectValue placeholder="选择预算范围" />
</SelectTrigger>
<SelectContent>
<SelectItem value="low"></SelectItem>
<SelectItem value="medium"></SelectItem>
<SelectItem value="high"></SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="strategy-notes"></Label>
<Textarea
id="strategy-notes"
placeholder="请输入任何补充说明或特殊要求..."
className="min-h-[100px]"
/>
</div>
<div className="space-y-4">
<div className="flex flex-col space-y-2">
<Label htmlFor="email-strategy"></Label>
<Input
id="email-strategy"
type="email"
placeholder="输入邮箱地址"
value={emailAddress}
onChange={(e) => setEmailAddress(e.target.value)}
/>
</div>
<div className="flex items-center space-x-2">
<Switch id="schedule-strategy" checked={scheduleReport} onCheckedChange={setScheduleReport} />
<Label htmlFor="schedule-strategy"></Label>
</div>
{scheduleReport && (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 pl-6">
<div className="space-y-2">
<Label htmlFor="update-frequency"></Label>
<Select defaultValue="monthly">
<SelectTrigger id="update-frequency">
<SelectValue placeholder="选择更新频率" />
</SelectTrigger>
<SelectContent>
<SelectItem value="weekly"></SelectItem>
<SelectItem value="biweekly"></SelectItem>
<SelectItem value="monthly"></SelectItem>
</SelectContent>
</Select>
</div>
</div>
)}
</div>
</>
)}
</div>
</CardContent>
<CardFooter className="flex justify-between">
<Button
variant="outline"
onClick={() => {
setStrategyStep(1)
setStrategyType("")
}}
>
</Button>
<Button disabled={strategyStep < 3 || !emailAddress}>
<Sparkles className="mr-2 h-4 w-4" />
</Button>
</CardFooter>
</Card>
<Card>
<CardHeader>
<CardTitle></CardTitle>
<CardDescription>AI生成的营销策略建议</CardDescription>
</CardHeader>
<CardContent className="h-96 flex items-center justify-center">
{selectedUserGroup && strategyStep === 3 ? (
<div className="text-center space-y-4">
<TrendingUp className="h-16 w-16 mx-auto text-primary" />
<p className="text-lg font-medium"></p>
<p className="text-muted-foreground">"生成策略"</p>
<div className="flex justify-center gap-2 pt-4">
<Button variant="outline">
<Download className="mr-2 h-4 w-4" />
</Button>
<Button>
<Mail className="mr-2 h-4 w-4" />
</Button>
</div>
</div>
) : (
<div className="text-center text-muted-foreground">
<Users className="h-16 w-16 mx-auto mb-4" />
<p className="text-lg"></p>
<Button variant="outline" className="mt-4" onClick={() => setIsUserGroupDialogOpen(true)}>
<Users className="mr-2 h-4 w-4" />
</Button>
</div>
)}
</CardContent>
</Card>
</TabsContent>
</Tabs>
<UserGroupSelectionDialog
isOpen={isUserGroupDialogOpen}
setIsOpen={setIsUserGroupDialogOpen}
onSelect={setSelectedUserGroup}
/>
</div>
)
}
// 用户分群选择对话框
function UserGroupSelectionDialog({
isOpen,
setIsOpen,
onSelect,
}: {
isOpen: boolean
setIsOpen: (open: boolean) => void
onSelect: (group: string) => void
}) {
// 模拟用户分群数据
const userGroups = [
{
id: "1",
name: "高价值用户",
description: "消费能力强,购买频率高的用户",
count: 12456,
category: "用户资产",
lastUpdated: "2023-07-20",
},
{
id: "2",
name: "活跃用户",
description: "近30天内有活动的用户",
count: 45678,
category: "用户资产",
lastUpdated: "2023-07-20",
},
{
id: "3",
name: "地摊商户",
description: "从事地摊相关业务的用户",
count: 8765,
category: "地摊相关",
lastUpdated: "2023-07-19",
},
{
id: "4",
name: "樊登读书会员",
description: "樊登读书平台的会员用户",
count: 15678,
category: "樊登读书",
lastUpdated: "2023-07-18",
},
{
id: "5",
name: "魔兽玩家",
description: "魔兽世界游戏的活跃玩家",
count: 23456,
category: "魔兽世界",
lastUpdated: "2023-07-17",
},
{
id: "6",
name: "流失风险用户",
description: "有流失风险的高价值用户",
count: 5678,
category: "用户资产",
lastUpdated: "2023-07-16",
},
]
const [searchQuery, setSearchQuery] = useState("")
const [selectedCategory, setSelectedCategory] = useState("all")
// 过滤用户分群
const filteredGroups = userGroups.filter((group) => {
const matchesSearch =
group.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
group.description.toLowerCase().includes(searchQuery.toLowerCase())
const matchesCategory = selectedCategory === "all" || group.category === selectedCategory
return matchesSearch && matchesCategory
})
// 获取所有分类
const categories = ["all", ...new Set(userGroups.map((group) => group.category))]
const handleSelect = (group: string) => {
onSelect(group)
setIsOpen(false)
}
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogContent className="sm:max-w-[600px]">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
<div className="py-4 space-y-4">
<div className="flex flex-col md:flex-row gap-4">
<div className="relative flex-1">
<Input
placeholder="搜索用户分群..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</div>
<Select value={selectedCategory} onValueChange={setSelectedCategory}>
<SelectTrigger className="w-full md:w-[180px]">
<SelectValue placeholder="选择场景分类" />
</SelectTrigger>
<SelectContent>
{categories.map((category) => (
<SelectItem key={category} value={category}>
{category === "all" ? "所有场景" : category}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="max-h-[300px] overflow-y-auto space-y-2">
{filteredGroups.map((group) => (
<div
key={group.id}
className="p-3 border rounded-lg hover:bg-muted/50 cursor-pointer"
onClick={() => handleSelect(group.name)}
>
<div className="flex justify-between items-start">
<div>
<div className="font-medium">{group.name}</div>
<div className="text-sm text-muted-foreground">{group.description}</div>
</div>
<Badge className="bg-blue-100 text-blue-800">{group.category}</Badge>
</div>
<div className="flex justify-between items-center mt-2 text-sm text-muted-foreground">
<div className="flex items-center">
<Users className="h-3 w-3 mr-1" />
{group.count.toLocaleString()}
</div>
<div className="flex items-center">
<Clock className="h-3 w-3 mr-1" />
{group.lastUpdated}
</div>
</div>
</div>
))}
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setIsOpen(false)}>
</Button>
<Button onClick={() => setIsOpen(false)}></Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}