mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 19:42:02 +00:00
75 lines
3.8 KiB
TypeScript
75 lines
3.8 KiB
TypeScript
import { Info, OctagonX, TriangleAlert } from 'lucide-react'
|
|
import type { LucideIcon } from 'lucide-react'
|
|
import clsx from 'clsx'
|
|
import type { SqlCheckItem, SqlCheckLevel } from '../types'
|
|
import { SQL_CHECK_LEVEL_HINT, SQL_CHECK_LEVEL_LABEL, SQL_CHECK_RULE_HINT, SQL_CHECK_TEXT } from '../lib/terms'
|
|
import { SQL_CHECK_TONE, isSqlCheckCode, sortSqlChecks, sqlCheckLevelOf, sqlCheckWhere, sqlRuleLabel } from '../lib/sqlcheck'
|
|
|
|
// ===========================================================================
|
|
// SQL 检查的结果:级别标识(错误、提醒、提示,四通道:图标、文字、颜色、边框)、中文规则名、给人看的说明、涉及的表和列、
|
|
// SQL 片段。证据面板的查询步骤和助手面板共用这一份。
|
|
// ===========================================================================
|
|
|
|
const LEVEL_ICON: Record<SqlCheckLevel, LucideIcon> = { error: OctagonX, warning: TriangleAlert, info: Info }
|
|
|
|
/** 级别标识:图标 + 「错误 / 提醒 / 提示」,悬停写明依据的确证程度 */
|
|
export function SqlLevelBadge({ level: raw, className }: { level: unknown; className?: string }) {
|
|
const level = sqlCheckLevelOf(raw)
|
|
const Icon = LEVEL_ICON[level]
|
|
const tone = SQL_CHECK_TONE[level]
|
|
return (
|
|
<span className={clsx('inline-flex shrink-0 items-center gap-0.5 whitespace-nowrap rounded-full border px-1.5 text-2xs leading-[16px]', className)}
|
|
style={{ color: tone, borderColor: `color-mix(in srgb, ${tone} 45%, var(--border))`, background: `color-mix(in srgb, ${tone} 8%, transparent)` }}
|
|
title={SQL_CHECK_LEVEL_HINT[level]} data-sql-level={level}>
|
|
<Icon size={10} aria-hidden />
|
|
{SQL_CHECK_LEVEL_LABEL[level]}
|
|
</span>
|
|
)
|
|
}
|
|
|
|
/** 一条检查的正文:规则名、说明、涉及的表和列、SQL 片段 */
|
|
export function SqlCheckBody({ check: c, excerpt = true }: { check: SqlCheckItem; excerpt?: boolean }) {
|
|
const where = sqlCheckWhere(c)
|
|
return (
|
|
<>
|
|
<div className="flex min-w-0 flex-wrap items-center gap-x-1.5 gap-y-0.5">
|
|
<SqlLevelBadge level={c.level} />
|
|
<span className="font-medium text-fg" title={isSqlCheckCode(c.code) ? SQL_CHECK_RULE_HINT[c.code] : undefined} data-sql-rule="">
|
|
{sqlRuleLabel(c.code)}
|
|
</span>
|
|
{where && (
|
|
<span className="text-faint" data-sql-where={where}>
|
|
{SQL_CHECK_TEXT.where} <span className="mono text-dim [overflow-wrap:anywhere]">{where}</span>
|
|
</span>
|
|
)}
|
|
</div>
|
|
{c.message && <p className="mt-0.5 leading-relaxed text-dim [overflow-wrap:anywhere]" data-sql-message="">{c.message}</p>}
|
|
{excerpt && c.sql_excerpt && (
|
|
<pre className="mono mt-1 max-h-20 overflow-auto whitespace-pre-wrap rounded bg-bg px-1.5 py-1 leading-relaxed text-dim [overflow-wrap:anywhere]"
|
|
aria-label={SQL_CHECK_TEXT.excerpt} title={SQL_CHECK_TEXT.excerpt} data-sql-excerpt="">
|
|
{c.sql_excerpt}
|
|
</pre>
|
|
)}
|
|
</>
|
|
)
|
|
}
|
|
|
|
/** 检查结果的清单:错误在前。每条一个淡色框,框的颜色跟级别走 */
|
|
export function SqlCheckList({ checks, className }: { checks: SqlCheckItem[]; className?: string }) {
|
|
const list = sortSqlChecks(checks)
|
|
return (
|
|
<ul className={clsx('space-y-1 text-2xs', className)} aria-label={SQL_CHECK_TEXT.listLabel(list.length)} data-sql-checks={list.length}>
|
|
{list.map((c, i) => {
|
|
const tone = SQL_CHECK_TONE[sqlCheckLevelOf(c.level)]
|
|
return (
|
|
<li key={`${c.code}:${c.table}:${c.column ?? ''}:${i}`} className="rounded border px-2 py-1.5"
|
|
style={{ borderColor: `color-mix(in srgb, ${tone} 30%, var(--border))`, background: `color-mix(in srgb, ${tone} 5%, transparent)` }}
|
|
data-sql-check={c.code} data-level={sqlCheckLevelOf(c.level)}>
|
|
<SqlCheckBody check={c} />
|
|
</li>
|
|
)
|
|
})}
|
|
</ul>
|
|
)
|
|
}
|