mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-08 00:26:37 +08:00
feat(frontend): align custom channels table layout and styling with events tab
This commit is contained in:
@@ -15,7 +15,6 @@ import {Label} from "@/components/ui/label"
|
|||||||
import {Textarea} from "@/components/ui/textarea"
|
import {Textarea} from "@/components/ui/textarea"
|
||||||
import {Switch} from "@/components/ui/switch"
|
import {Switch} from "@/components/ui/switch"
|
||||||
import {Badge} from "@/components/ui/badge"
|
import {Badge} from "@/components/ui/badge"
|
||||||
import {Card, CardContent, CardHeader, CardTitle} from "@/components/ui/card"
|
|
||||||
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow,} from "@/components/ui/table"
|
import {Table, TableBody, TableCell, TableHead, TableHeader, TableRow,} from "@/components/ui/table"
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -254,120 +253,122 @@ export function SettingsTab() {
|
|||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="pt-4 space-y-6">
|
<div className="pt-4 space-y-4">
|
||||||
<Card className="h-full flex flex-col">
|
<div className="flex items-center justify-between">
|
||||||
<CardHeader className="pb-3 border-b bg-muted/10">
|
<div>
|
||||||
<div className="flex items-center justify-between">
|
<h2 className="text-sm font-semibold">自定义推送通道</h2>
|
||||||
<div>
|
<p className="text-[11px] text-muted-foreground mt-0.5">
|
||||||
<CardTitle className="text-base font-semibold">自定义推送通道</CardTitle>
|
添加、配置及管理用于第三方 Webhook 对接的自定义数据推送通道
|
||||||
<p className="text-xs text-muted-foreground mt-0.5">
|
</p>
|
||||||
添加、配置及管理用于第三方 Webhook 对接的自定义数据推送通道
|
</div>
|
||||||
</p>
|
<Button size="sm" onClick={handleCreateChannelClick} className="text-xs">
|
||||||
</div>
|
<Plus className="size-3.5 mr-1" />
|
||||||
<Button size="sm" onClick={handleCreateChannelClick} className="text-xs">
|
新建消息通道
|
||||||
<Plus className="size-3.5 mr-1" />
|
</Button>
|
||||||
新建消息通道
|
</div>
|
||||||
</Button>
|
|
||||||
</div>
|
{channelsQuery.isLoading ? (
|
||||||
</CardHeader>
|
<LoadingStateWithBorder icon={Settings} description="加载消息通道中..." />
|
||||||
<CardContent className="p-0 flex-1 overflow-x-auto">
|
) : channelsQuery.isError ? (
|
||||||
{channelsQuery.isLoading ? (
|
<div className="p-8 border border-dashed rounded-xl bg-card">
|
||||||
<div className="p-8">
|
<ErrorInline error={channelsQuery.error} onRetry={() => channelsQuery.refetch()} className="justify-center" />
|
||||||
<LoadingStateWithBorder icon={Settings} description="加载消息通道中..." className="border-0 shadow-none animate-pulse" />
|
</div>
|
||||||
</div>
|
) : (channelsQuery.data ?? []).length === 0 ? (
|
||||||
) : channelsQuery.isError ? (
|
<div className="py-12 border border-dashed rounded-lg flex flex-col items-center justify-center text-muted-foreground">
|
||||||
<div className="p-8">
|
<Settings className="size-8 mb-2 opacity-30 animate-spin" style={{ animationDuration: '3s' }} />
|
||||||
<ErrorInline error={channelsQuery.error} onRetry={() => channelsQuery.refetch()} className="justify-center" />
|
<span className="text-xs font-medium">暂无自定义通道配置,请点击右上角新建</span>
|
||||||
</div>
|
</div>
|
||||||
) : (channelsQuery.data ?? []).length === 0 ? (
|
) : (
|
||||||
<div className="py-12 flex flex-col items-center justify-center text-muted-foreground">
|
<div className="border border-dashed shadow-none rounded-lg overflow-hidden">
|
||||||
<Settings className="size-8 mb-2 opacity-30 animate-spin" style={{ animationDuration: '3s' }} />
|
<Table className="w-full caption-bottom text-sm min-w-full">
|
||||||
<span className="text-xs font-medium">暂无自定义通道配置,请点击右上角新建</span>
|
<TableHeader className="sticky top-0 z-20 bg-background">
|
||||||
</div>
|
<TableRow className="border-b border-dashed hover:bg-transparent">
|
||||||
) : (
|
<TableHead className="w-[120px] whitespace-nowrap py-2 h-8">名称</TableHead>
|
||||||
<Table>
|
<TableHead className="w-[100px] whitespace-nowrap py-2 h-8">类型</TableHead>
|
||||||
<TableHeader>
|
<TableHead className="whitespace-nowrap py-2 h-8">备注</TableHead>
|
||||||
<TableRow className="bg-muted/30">
|
<TableHead className="w-[80px] text-center whitespace-nowrap py-2 h-8">状态</TableHead>
|
||||||
<TableHead className="text-xs font-semibold">名称</TableHead>
|
<TableHead className="sticky right-0 text-center bg-background z-10 w-[180px] py-2 h-8">操作</TableHead>
|
||||||
<TableHead className="text-xs font-semibold">类型</TableHead>
|
</TableRow>
|
||||||
<TableHead className="text-xs font-semibold">备注</TableHead>
|
</TableHeader>
|
||||||
<TableHead className="text-xs font-semibold w-[80px] text-center">状态</TableHead>
|
<TableBody>
|
||||||
<TableHead className="text-xs font-semibold w-[180px] text-right">操作</TableHead>
|
{(channelsQuery.data ?? []).map(ch => (
|
||||||
</TableRow>
|
<TableRow
|
||||||
</TableHeader>
|
key={ch.id}
|
||||||
<TableBody>
|
className="border-dashed hover:bg-muted/30 cursor-pointer group"
|
||||||
{(channelsQuery.data ?? []).map(ch => (
|
onClick={() => handleEditChannelClick(ch)}
|
||||||
<TableRow key={ch.id} className="hover:bg-muted/5">
|
>
|
||||||
<TableCell className="text-xs font-mono font-bold">
|
<TableCell className="text-xs font-mono font-bold py-1">
|
||||||
{ch.name}
|
{ch.name}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-xs">
|
<TableCell className="py-1">
|
||||||
<Badge variant="outline" className="text-[10px] whitespace-nowrap">
|
<Badge variant="outline" className="text-[10px] py-0 px-1.5 h-4.5 whitespace-nowrap">
|
||||||
{(definitionsQuery.data ?? []).find(d => d.type === ch.type)?.name ?? ch.type}
|
{(definitionsQuery.data ?? []).find(d => d.type === ch.type)?.name ?? ch.type}
|
||||||
</Badge>
|
</Badge>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-xs text-muted-foreground max-w-[200px] truncate">
|
<TableCell className="text-xs text-muted-foreground max-w-[200px] truncate py-1">
|
||||||
{ch.description || <span className="italic">无备注</span>}
|
{ch.description || <span className="italic">无备注</span>}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-center">
|
<TableCell className="text-center py-1" onClick={(e) => e.stopPropagation()}>
|
||||||
<Switch
|
<Switch
|
||||||
checked={ch.enabled}
|
checked={ch.enabled}
|
||||||
onCheckedChange={checked => {
|
onCheckedChange={checked => {
|
||||||
updateChannelMutation.mutate({
|
updateChannelMutation.mutate({
|
||||||
id: ch.id,
|
id: ch.id,
|
||||||
data: {
|
data: {
|
||||||
description: ch.description,
|
description: ch.description,
|
||||||
type: ch.type,
|
type: ch.type,
|
||||||
token: ch.token,
|
token: ch.token,
|
||||||
url: ch.url,
|
url: ch.url,
|
||||||
other: ch.other,
|
other: ch.other,
|
||||||
enabled: checked,
|
enabled: checked,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
/>
|
className="scale-75 data-[state=checked]:bg-green-600 h-4 w-7"
|
||||||
</TableCell>
|
/>
|
||||||
<TableCell className="text-right space-x-1 whitespace-nowrap">
|
</TableCell>
|
||||||
<Button
|
<TableCell className="sticky right-0 text-center bg-background z-10 py-1" onClick={(e) => e.stopPropagation()}>
|
||||||
variant="ghost"
|
<div className="flex items-center justify-center gap-1">
|
||||||
size="sm"
|
<Button
|
||||||
onClick={() => handleTestChannelClick(ch.name)}
|
variant="ghost"
|
||||||
className="h-7 text-[11px] text-primary hover:text-primary hover:bg-primary/10"
|
size="sm"
|
||||||
>
|
onClick={() => handleTestChannelClick(ch.name)}
|
||||||
<Play className="size-2.5 mr-1" />
|
className="h-6 px-2 text-[10px] text-primary hover:text-primary hover:bg-primary/10"
|
||||||
测试
|
>
|
||||||
</Button>
|
<Play className="size-2.5 mr-1" />
|
||||||
<Button
|
测试
|
||||||
variant="ghost"
|
</Button>
|
||||||
size="sm"
|
<Button
|
||||||
onClick={() => handleEditChannelClick(ch)}
|
variant="ghost"
|
||||||
className="h-7 text-[11px]"
|
size="sm"
|
||||||
>
|
onClick={() => handleEditChannelClick(ch)}
|
||||||
<Edit2 className="size-2.5 mr-1" />
|
className="h-6 px-2 text-[10px] text-muted-foreground hover:text-foreground"
|
||||||
编辑
|
>
|
||||||
</Button>
|
<Edit2 className="size-2.5 mr-1" />
|
||||||
<Button
|
编辑
|
||||||
variant="ghost"
|
</Button>
|
||||||
size="sm"
|
<Button
|
||||||
disabled={deleteChannelMutation.isPending}
|
variant="ghost"
|
||||||
onClick={() => {
|
size="sm"
|
||||||
if (confirm(`确定要删除通道 "${ch.name}" 吗?`)) {
|
disabled={deleteChannelMutation.isPending}
|
||||||
deleteChannelMutation.mutate(ch.id)
|
onClick={() => {
|
||||||
}
|
if (confirm(`确定要删除通道 "${ch.name}" 吗?`)) {
|
||||||
}}
|
deleteChannelMutation.mutate(ch.id)
|
||||||
className="h-7 text-[11px] text-destructive hover:text-destructive hover:bg-destructive/10"
|
}
|
||||||
>
|
}}
|
||||||
<Trash2 className="size-2.5 mr-1" />
|
className="h-6 px-2 text-[10px] text-destructive hover:text-destructive hover:bg-destructive/10"
|
||||||
删除
|
>
|
||||||
</Button>
|
<Trash2 className="size-2.5 mr-1" />
|
||||||
</TableCell>
|
删除
|
||||||
</TableRow>
|
</Button>
|
||||||
))}
|
</div>
|
||||||
</TableBody>
|
</TableCell>
|
||||||
</Table>
|
</TableRow>
|
||||||
)}
|
))}
|
||||||
</CardContent>
|
</TableBody>
|
||||||
</Card>
|
</Table>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* ==================== 对话框:新增/编辑消息通道 ==================== */}
|
{/* ==================== 对话框:新增/编辑消息通道 ==================== */}
|
||||||
<Dialog open={channelDialogOpen} onOpenChange={setChannelDialogOpen}>
|
<Dialog open={channelDialogOpen} onOpenChange={setChannelDialogOpen}>
|
||||||
|
|||||||
Reference in New Issue
Block a user