feat: enhance user profile with detailed tags and asset evaluation

Optimize user detail page for asset assessment and tag info.

#VERCEL_SKIP

Co-authored-by: null <4804959+fnvtk@users.noreply.github.com>
This commit is contained in:
v0
2025-08-21 05:32:37 +00:00
parent 9bb0ee2758
commit afc77439bb
25 changed files with 2421 additions and 1645 deletions

View File

@@ -1,340 +1,246 @@
"use client"
import { useState } from "react"
import { Database, Plus, Settings, Play, Pause, RotateCcw, Brain, Zap, CheckCircle, AlertCircle } from "lucide-react"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"
import { Progress } from "@/components/ui/progress"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Textarea } from "@/components/ui/textarea"
import { Switch } from "@/components/ui/switch"
import { Database, Plus, RefreshCw, Settings, Brain, Play } from "lucide-react"
import BottomTabs from "@/components/nav/bottom-tabs"
interface DataSource {
id: string
name: string
type: string
status: "connected" | "disconnected" | "syncing"
lastSync: string
recordCount: number
description: string
type: string
records: string
lastSync: string
status: "connected" | "disconnected" | "syncing"
}
interface AIModel {
id: string
name: string
type: string
status: "training" | "ready" | "error"
accuracy: number
accuracy: string
algorithm: string
features: number
lastTrained: string
parameters: Record<string, any>
status: "ready" | "training" | "error"
}
const mockDataSources: DataSource[] = [
{
id: "wechat-db",
name: "微信用户数据库",
description: "存储微信用户基础信息和行为数据",
type: "MySQL",
records: "2.5B",
lastSync: "2024/1/15 18:30:00",
status: "connected",
},
{
id: "traffic-keywords",
name: "流量关键词库",
description: "搜索引擎关键词和流量数据",
type: "PostgreSQL",
records: "150.0K",
lastSync: "2024/1/15 17:45:00",
status: "connected",
},
{
id: "user-behavior",
name: "用户行为日志",
description: "用户操作行为和交互记录",
type: "MongoDB",
records: "1.5B",
lastSync: "2024/1/15 19:00:00",
status: "syncing",
},
]
const mockAIModels: AIModel[] = [
{
id: "user-value-prediction",
name: "用户价值预测模型",
type: "Classification",
accuracy: "92.0%",
algorithm: "RandomForest",
features: 25,
lastTrained: "2024/1/14 23:30:00",
status: "ready",
},
{
id: "traffic-trend-analysis",
name: "流量趋势分析模型",
type: "Regression",
accuracy: "87.0%",
algorithm: "LSTM",
features: 15,
lastTrained: "2024/1/15 16:00:00",
status: "training",
},
{
id: "user-clustering",
name: "用户聚类模型",
type: "Clustering",
accuracy: "89.0%",
algorithm: "KMeans",
features: 20,
lastTrained: "2024/1/13 20:00:00",
status: "ready",
},
]
export default function DataPlatformPage() {
const [dataSources, setDataSources] = useState<DataSource[]>([
{
id: "ds_001",
name: "微信用户数据库",
type: "MySQL",
status: "connected",
lastSync: "2024-01-15T10:30:00Z",
recordCount: 2500000000,
description: "存储微信用户基础信息和行为数据",
},
{
id: "ds_002",
name: "流量关键词库",
type: "PostgreSQL",
status: "connected",
lastSync: "2024-01-15T09:45:00Z",
recordCount: 150000,
description: "搜索引擎关键词和流量数据",
},
{
id: "ds_003",
name: "用户行为日志",
type: "MongoDB",
status: "syncing",
lastSync: "2024-01-15T11:00:00Z",
recordCount: 1500000000,
description: "用户操作行为和交互记录",
},
])
const [aiModels, setAiModels] = useState<AIModel[]>([
{
id: "model_001",
name: "用户价值预测模型",
type: "Classification",
status: "ready",
accuracy: 0.92,
lastTrained: "2024-01-14T15:30:00Z",
parameters: {
algorithm: "RandomForest",
features: 25,
epochs: 100,
learningRate: 0.01,
},
},
{
id: "model_002",
name: "流量趋势分析模型",
type: "Regression",
status: "training",
accuracy: 0.87,
lastTrained: "2024-01-15T08:00:00Z",
parameters: {
algorithm: "LSTM",
features: 15,
epochs: 200,
learningRate: 0.001,
},
},
{
id: "model_003",
name: "用户聚类模型",
type: "Clustering",
status: "ready",
accuracy: 0.89,
lastTrained: "2024-01-13T12:00:00Z",
parameters: {
algorithm: "KMeans",
clusters: 8,
features: 20,
iterations: 300,
},
},
])
const [isAddingDataSource, setIsAddingDataSource] = useState(false)
const [isTrainingModel, setIsTrainingModel] = useState(false)
const [activeTab, setActiveTab] = useState("datasource")
const [dataSources, setDataSources] = useState<DataSource[]>(mockDataSources)
const [aiModels, setAIModels] = useState<AIModel[]>(mockAIModels)
const [syncing, setSyncing] = useState<string | null>(null)
const [training, setTraining] = useState<string | null>(null)
const [showAddDialog, setShowAddDialog] = useState(false)
const [newDataSource, setNewDataSource] = useState({
name: "",
description: "",
type: "MySQL",
host: "",
port: "",
database: "",
username: "",
password: "",
description: "",
})
const [modelTrainingConfig, setModelTrainingConfig] = useState({
modelId: "",
algorithm: "RandomForest",
features: 25,
epochs: 100,
learningRate: 0.01,
validationSplit: 0.2,
autoTune: true,
})
// 添加数据源
const handleAddDataSource = async () => {
try {
const newSource: DataSource = {
id: `ds_${Date.now()}`,
name: newDataSource.name,
type: newDataSource.type,
status: "connected",
lastSync: new Date().toISOString(),
recordCount: 0,
description: newDataSource.description,
}
setDataSources((prev) => [...prev, newSource])
setIsAddingDataSource(false)
setNewDataSource({
name: "",
type: "MySQL",
host: "",
port: "",
database: "",
username: "",
password: "",
description: "",
})
// 模拟数据导入
setTimeout(() => {
setDataSources((prev) =>
prev.map((ds) =>
ds.id === newSource.id
? { ...ds, recordCount: Math.floor(Math.random() * 1000000) + 10000, status: "connected" as const }
: ds,
),
)
}, 2000)
} catch (error) {
console.error("添加数据源失败:", error)
}
const handleSync = async (sourceId: string) => {
setSyncing(sourceId)
setTimeout(() => {
setSyncing(null)
setDataSources((prev) =>
prev.map((source) =>
source.id === sourceId ? { ...source, lastSync: new Date().toLocaleString("zh-CN") } : source,
),
)
}, 2000)
}
// 同步数据源
const handleSyncDataSource = (id: string) => {
setDataSources((prev) =>
prev.map((ds) => (ds.id === id ? { ...ds, status: "syncing" as const, lastSync: new Date().toISOString() } : ds)),
)
// 模拟同步完成
const handleRetrain = async (modelId: string) => {
setTraining(modelId)
setTimeout(() => {
setDataSources((prev) =>
prev.map((ds) =>
ds.id === id
? {
...ds,
status: "connected" as const,
recordCount: ds.recordCount + Math.floor(Math.random() * 10000),
lastSync: new Date().toISOString(),
}
: ds,
setTraining(null)
setAIModels((prev) =>
prev.map((model) =>
model.id === modelId ? { ...model, lastTrained: new Date().toLocaleString("zh-CN") } : model,
),
)
}, 3000)
}
// 训练AI模型
const handleTrainModel = async () => {
if (!modelTrainingConfig.modelId) return
setIsTrainingModel(true)
// 更新模型状态为训练中
setAiModels((prev) =>
prev.map((model) =>
model.id === modelTrainingConfig.modelId ? { ...model, status: "training" as const } : model,
),
)
// 模拟训练过程
setTimeout(() => {
setAiModels((prev) =>
prev.map((model) =>
model.id === modelTrainingConfig.modelId
? {
...model,
status: "ready" as const,
accuracy: Math.random() * 0.1 + 0.85,
lastTrained: new Date().toISOString(),
parameters: {
algorithm: modelTrainingConfig.algorithm,
features: modelTrainingConfig.features,
epochs: modelTrainingConfig.epochs,
learningRate: modelTrainingConfig.learningRate,
},
}
: model,
),
)
setIsTrainingModel(false)
}, 5000)
const handleAddDataSource = () => {
const newSource: DataSource = {
id: `datasource-${Date.now()}`,
name: newDataSource.name,
description: newDataSource.description,
type: newDataSource.type,
records: "0",
lastSync: "从未同步",
status: "disconnected",
}
setDataSources((prev) => [...prev, newSource])
setNewDataSource({
name: "",
description: "",
type: "MySQL",
host: "",
port: "",
database: "",
username: "",
password: "",
})
setShowAddDialog(false)
}
// 格式化数字
const formatNumber = (num: number): string => {
if (num >= 1000000000) {
return `${(num / 1000000000).toFixed(1)}B`
}
if (num >= 1000000) {
return `${(num / 1000000).toFixed(1)}M`
}
if (num >= 1000) {
return `${(num / 1000).toFixed(1)}K`
}
return num.toString()
}
// 获取状态颜色
const getStatusColor = (status: string) => {
const getStatusBadge = (status: DataSource["status"]) => {
switch (status) {
case "connected":
case "ready":
return "text-green-600 bg-green-50 border-green-200"
case "syncing":
case "training":
return "text-yellow-600 bg-yellow-50 border-yellow-200"
return <Badge className="bg-green-100 text-green-800 border-green-200">Connected</Badge>
case "disconnected":
case "error":
return "text-red-600 bg-red-50 border-red-200"
return <Badge variant="destructive">Disconnected</Badge>
case "syncing":
return <Badge className="bg-blue-100 text-blue-800 border-blue-200">Syncing</Badge>
default:
return "text-gray-600 bg-gray-50 border-gray-200"
return <Badge variant="secondary">Unknown</Badge>
}
}
// 获取状态图标
const getStatusIcon = (status: string) => {
const getModelStatusBadge = (status: AIModel["status"]) => {
switch (status) {
case "connected":
case "ready":
return <CheckCircle className="w-4 h-4" />
case "syncing":
return <Badge className="bg-green-100 text-green-800 border-green-200">Ready</Badge>
case "training":
return <Zap className="w-4 h-4 animate-pulse" />
case "disconnected":
return <Badge className="bg-yellow-100 text-yellow-800 border-yellow-200">Training</Badge>
case "error":
return <AlertCircle className="w-4 h-4" />
return <Badge variant="destructive">Error</Badge>
default:
return <Database className="w-4 h-4" />
return <Badge variant="secondary">Unknown</Badge>
}
}
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-blue-50">
<div className="container mx-auto px-4 py-8">
{/* 页面标题 */}
<div className="mb-8">
<h1 className="text-4xl font-bold text-gray-900 mb-2"></h1>
<p className="text-gray-600">AI模型训练平</p>
</div>
<Tabs defaultValue="datasources" className="space-y-6">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="datasources" className="flex items-center gap-2">
<Database className="w-4 h-4" />
</TabsTrigger>
<TabsTrigger value="aimodels" className="flex items-center gap-2">
<Brain className="w-4 h-4" />
AI模型
</TabsTrigger>
</TabsList>
{/* 数据源管理 */}
<TabsContent value="datasources" className="space-y-6">
<div className="flex justify-between items-center">
<h2 className="text-2xl font-semibold"></h2>
<Dialog open={isAddingDataSource} onOpenChange={setIsAddingDataSource}>
<div className="min-h-screen bg-gray-50">
<div className="bg-white border-b">
<div className="container mx-auto px-4 py-6">
<div className="flex items-center justify-between mb-4">
<div>
<h1 className="text-2xl font-bold text-gray-900"></h1>
<p className="text-gray-600 mt-1">AI模型训练平台</p>
</div>
{activeTab === "datasource" ? (
<Dialog open={showAddDialog} onOpenChange={setShowAddDialog}>
<DialogTrigger asChild>
<Button className="flex items-center gap-2">
<Plus className="w-4 h-4" />
</Button>
</DialogTrigger>
<DialogContent className="max-w-2xl">
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle></DialogTitle>
</DialogHeader>
<div className="grid grid-cols-2 gap-4 py-4">
<div className="space-y-2">
<Label htmlFor="name"></Label>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="name" className="text-right">
</Label>
<Input
id="name"
value={newDataSource.name}
onChange={(e) => setNewDataSource((prev) => ({ ...prev, name: e.target.value }))}
placeholder="输入数据源名称"
onChange={(e) => setNewDataSource({ ...newDataSource, name: e.target.value })}
className="col-span-3"
/>
</div>
<div className="space-y-2">
<Label htmlFor="type"></Label>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="description" className="text-right">
</Label>
<Input
id="description"
value={newDataSource.description}
onChange={(e) => setNewDataSource({ ...newDataSource, description: e.target.value })}
className="col-span-3"
/>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="type" className="text-right">
</Label>
<Select
value={newDataSource.type}
onValueChange={(value) => setNewDataSource((prev) => ({ ...prev, type: value }))}
onValueChange={(value) => setNewDataSource({ ...newDataSource, type: value })}
>
<SelectTrigger>
<SelectTrigger className="col-span-3">
<SelectValue />
</SelectTrigger>
<SelectContent>
@@ -342,130 +248,114 @@ export default function DataPlatformPage() {
<SelectItem value="PostgreSQL">PostgreSQL</SelectItem>
<SelectItem value="MongoDB">MongoDB</SelectItem>
<SelectItem value="Redis">Redis</SelectItem>
<SelectItem value="ClickHouse">ClickHouse</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="host"></Label>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="host" className="text-right">
</Label>
<Input
id="host"
value={newDataSource.host}
onChange={(e) => setNewDataSource((prev) => ({ ...prev, host: e.target.value }))}
onChange={(e) => setNewDataSource({ ...newDataSource, host: e.target.value })}
className="col-span-3"
placeholder="localhost"
/>
</div>
<div className="space-y-2">
<Label htmlFor="port"></Label>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="port" className="text-right">
</Label>
<Input
id="port"
value={newDataSource.port}
onChange={(e) => setNewDataSource((prev) => ({ ...prev, port: e.target.value }))}
onChange={(e) => setNewDataSource({ ...newDataSource, port: e.target.value })}
className="col-span-3"
placeholder="3306"
/>
</div>
<div className="space-y-2">
<Label htmlFor="database"></Label>
<Input
id="database"
value={newDataSource.database}
onChange={(e) => setNewDataSource((prev) => ({ ...prev, database: e.target.value }))}
placeholder="database_name"
/>
</div>
<div className="space-y-2">
<Label htmlFor="username"></Label>
<Input
id="username"
value={newDataSource.username}
onChange={(e) => setNewDataSource((prev) => ({ ...prev, username: e.target.value }))}
placeholder="username"
/>
</div>
<div className="col-span-2 space-y-2">
<Label htmlFor="password"></Label>
<Input
id="password"
type="password"
value={newDataSource.password}
onChange={(e) => setNewDataSource((prev) => ({ ...prev, password: e.target.value }))}
placeholder="password"
/>
</div>
<div className="col-span-2 space-y-2">
<Label htmlFor="description"></Label>
<Textarea
id="description"
value={newDataSource.description}
onChange={(e) => setNewDataSource((prev) => ({ ...prev, description: e.target.value }))}
placeholder="数据源描述信息"
rows={3}
/>
</div>
</div>
<div className="flex justify-end gap-2">
<Button variant="outline" onClick={() => setIsAddingDataSource(false)}>
<Button variant="outline" onClick={() => setShowAddDialog(false)}>
</Button>
<Button onClick={handleAddDataSource}></Button>
<Button onClick={handleAddDataSource} disabled={!newDataSource.name || !newDataSource.type}>
</Button>
</div>
</DialogContent>
</Dialog>
) : (
<Button className="flex items-center gap-2" onClick={() => handleRetrain("all")}>
<RefreshCw className="w-4 h-4" />
</Button>
)}
</div>
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="datasource" className="flex items-center gap-2">
<Database className="w-4 h-4" />
</TabsTrigger>
<TabsTrigger value="aimodel" className="flex items-center gap-2">
<Brain className="w-4 h-4" />
AI模型
</TabsTrigger>
</TabsList>
</Tabs>
</div>
</div>
<div className="container mx-auto px-4 py-6">
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsContent value="datasource" className="space-y-4">
<div className="flex items-center justify-between mb-6">
<h2 className="text-xl font-semibold"></h2>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-6">
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{dataSources.map((source) => (
<Card key={source.id} className="border-2 hover:shadow-lg transition-all duration-200">
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<CardTitle className="text-lg">{source.name}</CardTitle>
<Badge className={`${getStatusColor(source.status)} border`}>
{getStatusIcon(source.status)}
<span className="ml-1 capitalize">{source.status}</span>
</Badge>
<Card key={source.id} className="bg-white">
<CardHeader className="pb-4">
<div className="flex items-start justify-between">
<div className="flex-1">
<CardTitle className="text-lg font-semibold mb-2">{source.name}</CardTitle>
<p className="text-gray-600 text-sm mb-3">{source.description}</p>
<div className="space-y-1 text-sm text-gray-500">
<div>
<span className="font-medium">{source.type}</span>
</div>
<div>
<span className="font-medium">{source.records}</span>
</div>
</div>
</div>
<div className="flex items-center gap-2">
{getStatusBadge(syncing === source.id ? "syncing" : source.status)}
</div>
</div>
<p className="text-sm text-gray-600">{source.description}</p>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-2 gap-4 text-sm">
<div>
<span className="text-gray-500">:</span>
<span className="ml-2 font-medium">{source.type}</span>
<CardContent className="pt-0">
<div className="space-y-3">
<div className="text-sm text-gray-500">{source.lastSync}</div>
<div className="flex items-center gap-2">
<Button
variant="outline"
size="sm"
onClick={() => handleSync(source.id)}
disabled={syncing === source.id}
className="flex items-center gap-2 flex-1"
>
<RefreshCw className={`w-4 h-4 ${syncing === source.id ? "animate-spin" : ""}`} />
{syncing === source.id ? "同步中" : "同步数据"}
</Button>
<Button variant="ghost" size="sm">
<Settings className="w-4 h-4" />
</Button>
</div>
<div>
<span className="text-gray-500">:</span>
<span className="ml-2 font-medium">{formatNumber(source.recordCount)}</span>
</div>
</div>
<div className="text-sm">
<span className="text-gray-500">:</span>
<span className="ml-2">{new Date(source.lastSync).toLocaleString()}</span>
</div>
<div className="flex gap-2">
<Button
size="sm"
variant="outline"
onClick={() => handleSyncDataSource(source.id)}
disabled={source.status === "syncing"}
className="flex-1"
>
{source.status === "syncing" ? (
<>
<RotateCcw className="w-3 h-3 mr-1 animate-spin" />
</>
) : (
<>
<RotateCcw className="w-3 h-3 mr-1" />
</>
)}
</Button>
<Button size="sm" variant="outline">
<Settings className="w-3 h-3" />
</Button>
</div>
</CardContent>
</Card>
@@ -473,201 +363,79 @@ export default function DataPlatformPage() {
</div>
</TabsContent>
{/* AI模型管理 */}
<TabsContent value="aimodels" className="space-y-6">
<div className="flex justify-between items-center">
<h2 className="text-2xl font-semibold">AI模型管理</h2>
<Dialog open={isTrainingModel} onOpenChange={setIsTrainingModel}>
<DialogTrigger asChild>
<Button className="flex items-center gap-2">
<Brain className="w-4 h-4" />
</Button>
</DialogTrigger>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle></DialogTitle>
</DialogHeader>
<div className="grid grid-cols-2 gap-4 py-4">
<div className="col-span-2 space-y-2">
<Label htmlFor="model"></Label>
<Select
value={modelTrainingConfig.modelId}
onValueChange={(value) => setModelTrainingConfig((prev) => ({ ...prev, modelId: value }))}
>
<SelectTrigger>
<SelectValue placeholder="选择要训练的模型" />
</SelectTrigger>
<SelectContent>
{aiModels.map((model) => (
<SelectItem key={model.id} value={model.id}>
{model.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="algorithm"></Label>
<Select
value={modelTrainingConfig.algorithm}
onValueChange={(value) => setModelTrainingConfig((prev) => ({ ...prev, algorithm: value }))}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="RandomForest">Random Forest</SelectItem>
<SelectItem value="XGBoost">XGBoost</SelectItem>
<SelectItem value="LSTM">LSTM</SelectItem>
<SelectItem value="KMeans">K-Means</SelectItem>
<SelectItem value="SVM">SVM</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="features"></Label>
<Input
id="features"
type="number"
value={modelTrainingConfig.features}
onChange={(e) =>
setModelTrainingConfig((prev) => ({ ...prev, features: Number.parseInt(e.target.value) }))
}
/>
</div>
<div className="space-y-2">
<Label htmlFor="epochs"></Label>
<Input
id="epochs"
type="number"
value={modelTrainingConfig.epochs}
onChange={(e) =>
setModelTrainingConfig((prev) => ({ ...prev, epochs: Number.parseInt(e.target.value) }))
}
/>
</div>
<div className="space-y-2">
<Label htmlFor="learningRate"></Label>
<Input
id="learningRate"
type="number"
step="0.001"
value={modelTrainingConfig.learningRate}
onChange={(e) =>
setModelTrainingConfig((prev) => ({
...prev,
learningRate: Number.parseFloat(e.target.value),
}))
}
/>
</div>
<div className="space-y-2">
<Label htmlFor="validationSplit"></Label>
<Input
id="validationSplit"
type="number"
step="0.1"
min="0.1"
max="0.5"
value={modelTrainingConfig.validationSplit}
onChange={(e) =>
setModelTrainingConfig((prev) => ({
...prev,
validationSplit: Number.parseFloat(e.target.value),
}))
}
/>
</div>
<div className="col-span-2 flex items-center space-x-2">
<Switch
id="autoTune"
checked={modelTrainingConfig.autoTune}
onCheckedChange={(checked) =>
setModelTrainingConfig((prev) => ({ ...prev, autoTune: checked }))
}
/>
<Label htmlFor="autoTune"></Label>
</div>
</div>
<div className="flex justify-end gap-2">
<Button variant="outline" onClick={() => setIsTrainingModel(false)}>
</Button>
<Button onClick={handleTrainModel} disabled={!modelTrainingConfig.modelId}>
</Button>
</div>
</DialogContent>
</Dialog>
<TabsContent value="aimodel" className="space-y-4">
<div className="flex items-center justify-between mb-6">
<h2 className="text-xl font-semibold">AI模型管理</h2>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-6">
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{aiModels.map((model) => (
<Card key={model.id} className="border-2 hover:shadow-lg transition-all duration-200">
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<CardTitle className="text-lg">{model.name}</CardTitle>
<Badge className={`${getStatusColor(model.status)} border`}>
{getStatusIcon(model.status)}
<span className="ml-1 capitalize">{model.status}</span>
</Badge>
<Card key={model.id} className="bg-white">
<CardHeader className="pb-4">
<div className="flex items-start justify-between">
<div className="flex-1">
<CardTitle className="text-lg font-semibold mb-2">{model.name}</CardTitle>
<p className="text-gray-600 text-sm mb-3">{model.type} </p>
<div className="mb-3">
<div className="flex justify-between text-sm mb-1">
<span></span>
<span className="font-medium">{model.accuracy}</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2">
<div className="bg-gray-900 h-2 rounded-full" style={{ width: model.accuracy }}></div>
</div>
</div>
<div className="space-y-1 text-sm text-gray-500">
<div>
<span className="font-medium">{model.algorithm}</span>
</div>
<div>
<span className="font-medium">{model.features}</span>
</div>
</div>
</div>
<div className="flex items-center gap-2">
{getModelStatusBadge(training === model.id ? "training" : model.status)}
</div>
</div>
<p className="text-sm text-gray-600">{model.type} </p>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<div className="flex justify-between text-sm">
<span className="text-gray-500"></span>
<span className="font-medium">{(model.accuracy * 100).toFixed(1)}%</span>
</div>
<Progress value={model.accuracy * 100} className="h-2" />
</div>
<div className="grid grid-cols-2 gap-4 text-sm">
<div>
<span className="text-gray-500">:</span>
<span className="ml-2 font-medium">{model.parameters.algorithm}</span>
</div>
<div>
<span className="text-gray-500">:</span>
<span className="ml-2 font-medium">{model.parameters.features}</span>
</div>
</div>
<div className="text-sm">
<span className="text-gray-500">:</span>
<span className="ml-2">{new Date(model.lastTrained).toLocaleString()}</span>
</div>
<div className="flex gap-2">
<Button
size="sm"
variant="outline"
disabled={model.status === "training"}
className="flex-1 bg-transparent"
>
{model.status === "training" ? (
<>
<Pause className="w-3 h-3 mr-1" />
</>
) : (
<>
<Play className="w-3 h-3 mr-1" />
<CardContent className="pt-0">
<div className="space-y-3">
<div className="text-sm text-gray-500">{model.lastTrained}</div>
<div className="flex items-center gap-2">
{model.status === "ready" ? (
<Button variant="outline" size="sm" className="flex items-center gap-2 flex-1 bg-transparent">
<Play className="w-4 h-4" />
</>
</Button>
) : model.status === "training" ? (
<Button
variant="outline"
size="sm"
disabled
className="flex items-center gap-2 flex-1 bg-transparent"
>
<RefreshCw className="w-4 h-4 animate-spin" />
</Button>
) : (
<Button
variant="outline"
size="sm"
onClick={() => handleRetrain(model.id)}
disabled={training === model.id}
className="flex items-center gap-2 flex-1"
>
<RefreshCw className={`w-4 h-4 ${training === model.id ? "animate-spin" : ""}`} />
</Button>
)}
</Button>
<Button size="sm" variant="outline">
<Settings className="w-3 h-3" />
</Button>
<Button variant="ghost" size="sm">
<Settings className="w-4 h-4" />
</Button>
</div>
</div>
</CardContent>
</Card>
@@ -676,6 +444,8 @@ export default function DataPlatformPage() {
</TabsContent>
</Tabs>
</div>
<BottomTabs />
</div>
)
}