Files
users/components/data-integration/data-collection-settings.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

699 lines
29 KiB
TypeScript
Raw Permalink 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, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
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 { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Badge } from "@/components/ui/badge"
import { Separator } from "@/components/ui/separator"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Plus, Trash2, Clock, RefreshCw, Database, Server, FileText, Settings, HelpCircle } from "lucide-react"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
interface CollectionTask {
id: string
name: string
source: string
type: string
schedule: string
lastRun: string | null
nextRun: string | null
status: "active" | "paused" | "error" | "completed"
recordsCollected: number
}
export function DataCollectionSettings() {
const [activeTab, setActiveTab] = useState("tasks")
const [isAddingTask, setIsAddingTask] = useState(false)
const [selectedTaskId, setSelectedTaskId] = useState<string | null>(null)
// 模拟数据采集任务
const [collectionTasks, setCollectionTasks] = useState<CollectionTask[]>([
{
id: "1",
name: "用户基本信息采集",
source: "CRM系统",
type: "数据库",
schedule: "每日 00:00",
lastRun: "2023-07-20 00:00",
nextRun: "2023-07-21 00:00",
status: "active",
recordsCollected: 125678,
},
{
id: "2",
name: "用户行为数据采集",
source: "行为分析平台",
type: "API",
schedule: "每小时",
lastRun: "2023-07-20 15:00",
nextRun: "2023-07-20 16:00",
status: "active",
recordsCollected: 458921,
},
{
id: "3",
name: "社交媒体数据采集",
source: "MCP SERVER",
type: "AI工具",
schedule: "每日 02:00",
lastRun: "2023-07-20 02:00",
nextRun: "2023-07-21 02:00",
status: "active",
recordsCollected: 87452,
},
{
id: "4",
name: "历史交易数据导入",
source: "交易系统",
type: "文件",
schedule: "手动",
lastRun: "2023-07-15 10:30",
nextRun: null,
status: "completed",
recordsCollected: 254789,
},
{
id: "5",
name: "设备信息采集",
source: "设备管理系统",
type: "API",
schedule: "每日 03:00",
lastRun: "2023-07-20 03:00",
nextRun: "2023-07-21 03:00",
status: "error",
recordsCollected: 12547,
},
])
// 模拟数据源
const dataSources = [
{ id: "crm", name: "CRM系统", type: "数据库" },
{ id: "behavior", name: "行为分析平台", type: "API" },
{ id: "mcp", name: "MCP SERVER", type: "AI工具" },
{ id: "transaction", name: "交易系统", type: "文件" },
{ id: "device", name: "设备管理系统", type: "API" },
{ id: "social", name: "社交媒体平台", type: "API" },
{ id: "ecommerce", name: "电商平台", type: "API" },
]
// 模拟采集类型
const collectionTypes = [
{ id: "full", name: "全量采集", description: "采集所有数据" },
{ id: "incremental", name: "增量采集", description: "仅采集新增或变更的数据" },
{ id: "scheduled", name: "定时采集", description: "按照设定的时间计划采集数据" },
{ id: "event", name: "事件触发采集", description: "在特定事件发生时采集数据" },
{ id: "manual", name: "手动采集", description: "手动触发数据采集" },
]
const getStatusBadge = (status: CollectionTask["status"]) => {
switch (status) {
case "active":
return <Badge className="bg-green-100 text-green-800"></Badge>
case "paused":
return <Badge className="bg-yellow-100 text-yellow-800"></Badge>
case "error":
return <Badge className="bg-red-100 text-red-800"></Badge>
case "completed":
return <Badge className="bg-blue-100 text-blue-800"></Badge>
}
}
const getSourceIcon = (type: string) => {
switch (type) {
case "数据库":
return <Database className="h-4 w-4 text-blue-500" />
case "API":
return <Server className="h-4 w-4 text-purple-500" />
case "文件":
return <FileText className="h-4 w-4 text-green-500" />
case "AI工具":
return <Settings className="h-4 w-4 text-orange-500" />
default:
return <Database className="h-4 w-4" />
}
}
const addTask = (task: Omit<CollectionTask, "id" | "lastRun" | "recordsCollected">) => {
const newTask: CollectionTask = {
id: `task-${Date.now()}`,
...task,
lastRun: null,
recordsCollected: 0,
}
setCollectionTasks([...collectionTasks, newTask])
setIsAddingTask(false)
}
const deleteTask = (id: string) => {
setCollectionTasks(collectionTasks.filter((task) => task.id !== id))
}
const toggleTaskStatus = (id: string) => {
setCollectionTasks(
collectionTasks.map((task) => {
if (task.id === id) {
return {
...task,
status: task.status === "active" ? "paused" : "active",
}
}
return task
}),
)
}
const selectedTask = selectedTaskId ? collectionTasks.find((task) => task.id === selectedTaskId) : null
return (
<div className="space-y-4">
<Card>
<CardHeader>
<CardTitle></CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent>
<Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-4">
<TabsList>
<TabsTrigger value="tasks"></TabsTrigger>
<TabsTrigger value="sources"></TabsTrigger>
<TabsTrigger value="settings"></TabsTrigger>
</TabsList>
<TabsContent value="tasks" className="space-y-4">
<div className="flex justify-between items-center">
<div className="flex items-center gap-2">
<Badge className="bg-blue-100 text-blue-800"> {collectionTasks.length} </Badge>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">
<HelpCircle className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p className="max-w-xs">
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<Button onClick={() => setIsAddingTask(true)}>
<Plus className="mr-2 h-4 w-4" />
</Button>
</div>
<div className="rounded-md border">
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead className="text-right"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{collectionTasks.map((task) => (
<TableRow key={task.id}>
<TableCell className="font-medium">{task.name}</TableCell>
<TableCell>
<div className="flex items-center gap-1">
{getSourceIcon(task.type)}
<span>{task.source}</span>
</div>
</TableCell>
<TableCell>{task.schedule}</TableCell>
<TableCell>{task.lastRun || "从未运行"}</TableCell>
<TableCell>{task.nextRun || "无计划"}</TableCell>
<TableCell>{getStatusBadge(task.status)}</TableCell>
<TableCell>{task.recordsCollected.toLocaleString()}</TableCell>
<TableCell className="text-right">
<div className="flex justify-end space-x-2">
<Button
variant="ghost"
size="icon"
onClick={() => toggleTaskStatus(task.id)}
disabled={task.status === "completed"}
>
{task.status === "active" ? (
<Clock className="h-4 w-4 text-yellow-500" />
) : (
<RefreshCw className="h-4 w-4 text-green-500" />
)}
</Button>
<Button variant="ghost" size="icon" onClick={() => setSelectedTaskId(task.id)}>
<Settings className="h-4 w-4" />
</Button>
<Button variant="ghost" size="icon" onClick={() => deleteTask(task.id)}>
<Trash2 className="h-4 w-4 text-red-500" />
</Button>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</TabsContent>
<TabsContent value="sources" className="space-y-4">
<div className="flex justify-between items-center">
<h3 className="text-lg font-medium"></h3>
<Button variant="outline"></Button>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{dataSources.map((source) => (
<Card key={source.id} className="border shadow-sm">
<CardContent className="p-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
{getSourceIcon(source.type)}
<div>
<h4 className="font-medium">{source.name}</h4>
<p className="text-sm text-muted-foreground">{source.type}</p>
</div>
</div>
<Button variant="ghost" size="icon">
<Settings className="h-4 w-4" />
</Button>
</div>
</CardContent>
</Card>
))}
</div>
<Card className="mt-4">
<CardHeader>
<CardTitle>MCP SERVER </CardTitle>
<CardDescription>MCP SERVER AI工具的采集参数</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="mcp-server-url"></Label>
<Input id="mcp-server-url" defaultValue="https://mcp.example.com/api" />
</div>
<div className="space-y-2">
<Label htmlFor="mcp-api-key">API密钥</Label>
<Input id="mcp-api-key" type="password" defaultValue="••••••••••••••••" />
</div>
</div>
<div className="space-y-2">
<Label htmlFor="mcp-model">AI模型选择</Label>
<Select defaultValue="gpt-4">
<SelectTrigger id="mcp-model">
<SelectValue placeholder="选择AI模型" />
</SelectTrigger>
<SelectContent>
<SelectItem value="gpt-4">GPT-4</SelectItem>
<SelectItem value="gpt-3.5-turbo">GPT-3.5 Turbo</SelectItem>
<SelectItem value="claude-2">Claude 2</SelectItem>
<SelectItem value="llama-2">Llama 2</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label htmlFor="mcp-auto-process"></Label>
<Switch id="mcp-auto-process" defaultChecked />
</div>
<p className="text-sm text-muted-foreground">
使AI处理采集的数据
</p>
</div>
<Separator />
<div className="space-y-2">
<Label></Label>
<div className="grid grid-cols-2 gap-2">
<div className="flex items-center space-x-2">
<Switch id="collect-profile" defaultChecked />
<Label htmlFor="collect-profile"></Label>
</div>
<div className="flex items-center space-x-2">
<Switch id="collect-posts" defaultChecked />
<Label htmlFor="collect-posts"></Label>
</div>
<div className="flex items-center space-x-2">
<Switch id="collect-interactions" defaultChecked />
<Label htmlFor="collect-interactions"></Label>
</div>
<div className="flex items-center space-x-2">
<Switch id="collect-followers" defaultChecked />
<Label htmlFor="collect-followers"></Label>
</div>
</div>
</div>
<div className="flex justify-end">
<Button>MCP配置</Button>
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="settings" className="space-y-4">
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label htmlFor="enable-collection"></Label>
<Switch id="enable-collection" defaultChecked />
</div>
<p className="text-sm text-muted-foreground"></p>
</div>
<div className="space-y-2">
<Label htmlFor="collection-threads">线</Label>
<Select defaultValue="5">
<SelectTrigger id="collection-threads">
<SelectValue placeholder="选择并发线程数" />
</SelectTrigger>
<SelectContent>
<SelectItem value="1">1</SelectItem>
<SelectItem value="3">3</SelectItem>
<SelectItem value="5">5</SelectItem>
<SelectItem value="10">10</SelectItem>
<SelectItem value="20">20</SelectItem>
</SelectContent>
</Select>
<p className="text-sm text-muted-foreground">
</p>
</div>
<div className="space-y-2">
<Label htmlFor="retry-attempts"></Label>
<Select defaultValue="3">
<SelectTrigger id="retry-attempts">
<SelectValue placeholder="选择重试次数" />
</SelectTrigger>
<SelectContent>
<SelectItem value="0"></SelectItem>
<SelectItem value="1">1</SelectItem>
<SelectItem value="3">3</SelectItem>
<SelectItem value="5">5</SelectItem>
<SelectItem value="10">10</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="retry-delay"></Label>
<Input id="retry-delay" type="number" defaultValue="60" />
</div>
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label htmlFor="data-deduplication"></Label>
<Switch id="data-deduplication" defaultChecked />
</div>
<p className="text-sm text-muted-foreground"></p>
</div>
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label htmlFor="data-validation"></Label>
<Switch id="data-validation" defaultChecked />
</div>
<p className="text-sm text-muted-foreground"></p>
</div>
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label htmlFor="notification"></Label>
<Switch id="notification" defaultChecked />
</div>
<p className="text-sm text-muted-foreground"></p>
</div>
<div className="flex justify-end">
<Button></Button>
</div>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</CardContent>
</Card>
{/* 添加采集任务对话框 */}
<Dialog open={isAddingTask} onOpenChange={setIsAddingTask}>
<DialogContent className="sm:max-w-[500px]">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="task-name" className="text-right">
</Label>
<Input id="task-name" className="col-span-3" placeholder="例如:用户基本信息采集" />
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="data-source" className="text-right">
</Label>
<Select>
<SelectTrigger id="data-source" className="col-span-3">
<SelectValue placeholder="选择数据源" />
</SelectTrigger>
<SelectContent>
{dataSources.map((source) => (
<SelectItem key={source.id} value={source.id}>
{source.name} ({source.type})
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="collection-type" className="text-right">
</Label>
<Select>
<SelectTrigger id="collection-type" className="col-span-3">
<SelectValue placeholder="选择采集类型" />
</SelectTrigger>
<SelectContent>
{collectionTypes.map((type) => (
<SelectItem key={type.id} value={type.id}>
{type.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="schedule" className="text-right">
</Label>
<Select>
<SelectTrigger id="schedule" className="col-span-3">
<SelectValue placeholder="选择采集频率" />
</SelectTrigger>
<SelectContent>
<SelectItem value="hourly"></SelectItem>
<SelectItem value="daily"></SelectItem>
<SelectItem value="weekly"></SelectItem>
<SelectItem value="monthly"></SelectItem>
<SelectItem value="manual"></SelectItem>
</SelectContent>
</Select>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label htmlFor="time" className="text-right">
</Label>
<Input id="time" type="time" className="col-span-3" />
</div>
<div className="grid grid-cols-4 items-center gap-4">
<div className="text-right">
<Label htmlFor="auto-process"></Label>
</div>
<div className="flex items-center space-x-2 col-span-3">
<Switch id="auto-process" />
<Label htmlFor="auto-process"></Label>
</div>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setIsAddingTask(false)}>
</Button>
<Button
onClick={() =>
addTask({
name: "新建采集任务",
source: "CRM系统",
type: "数据库",
schedule: "每日 00:00",
nextRun: "2023-07-21 00:00",
status: "active",
})
}
>
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* 任务详情对话框 */}
<Dialog open={!!selectedTaskId} onOpenChange={(open) => !open && setSelectedTaskId(null)}>
<DialogContent className="sm:max-w-[600px]">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
{selectedTask && (
<div className="space-y-4 py-4">
<div className="grid grid-cols-2 gap-4">
<div>
<Label className="text-sm text-muted-foreground"></Label>
<p className="font-medium">{selectedTask.name}</p>
</div>
<div>
<Label className="text-sm text-muted-foreground"></Label>
<p className="font-medium">{selectedTask.source}</p>
</div>
<div>
<Label className="text-sm text-muted-foreground"></Label>
<p className="font-medium">{selectedTask.schedule}</p>
</div>
<div>
<Label className="text-sm text-muted-foreground"></Label>
<div>{getStatusBadge(selectedTask.status)}</div>
</div>
<div>
<Label className="text-sm text-muted-foreground"></Label>
<p className="font-medium">{selectedTask.lastRun || "从未运行"}</p>
</div>
<div>
<Label className="text-sm text-muted-foreground"></Label>
<p className="font-medium">{selectedTask.nextRun || "无计划"}</p>
</div>
<div>
<Label className="text-sm text-muted-foreground"></Label>
<p className="font-medium">{selectedTask.recordsCollected.toLocaleString()}</p>
</div>
</div>
<Separator />
<div className="space-y-2">
<Label></Label>
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow>
<TableCell>ID</TableCell>
<TableCell></TableCell>
<TableCell>
<Switch defaultChecked />
</TableCell>
</TableRow>
<TableRow>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell>
<Switch defaultChecked />
</TableCell>
</TableRow>
<TableRow>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell>
<Switch defaultChecked />
</TableCell>
</TableRow>
<TableRow>
<TableCell>IMEI</TableCell>
<TableCell></TableCell>
<TableCell>
<Switch defaultChecked />
</TableCell>
</TableRow>
<TableRow>
<TableCell></TableCell>
<TableCell></TableCell>
<TableCell>
<Switch defaultChecked />
</TableCell>
</TableRow>
</TableBody>
</Table>
</div>
<div className="space-y-2">
<Label></Label>
<div className="grid grid-cols-2 gap-2">
<div className="flex items-center space-x-2">
<Switch defaultChecked />
<Label></Label>
</div>
<div className="flex items-center space-x-2">
<Switch defaultChecked />
<Label></Label>
</div>
<div className="flex items-center space-x-2">
<Switch defaultChecked />
<Label></Label>
</div>
<div className="flex items-center space-x-2">
<Switch defaultChecked />
<Label></Label>
</div>
</div>
</div>
</div>
)}
<DialogFooter>
<Button variant="outline" onClick={() => setSelectedTaskId(null)}>
</Button>
<Button variant="outline" onClick={() => {}}>
</Button>
<Button onClick={() => setSelectedTaskId(null)}></Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
)
}