import * as React from 'react'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Textarea } from '@/components/ui/textarea'
import { Label } from '@/components/ui/label'
import type { DesktopLifecycleAction } from '@/lib/types/desktop'
import { Archive, CheckCircle2, Loader2, TrendingUp } from 'lucide-react'
type ActionMeta = {
label: string
title: (recordTitle?: string) => string
description: string
icon: typeof CheckCircle2
variant: 'default' | 'outline' | 'destructive'
}
const ACTION_META: Record<DesktopLifecycleAction, ActionMeta> = {
accept: {
label: '采纳',
title: (t) => (t ? `采纳 - ${t}` : '采纳记录'),
description: '采纳后该记录变为 Accepted,将参与唤醒检索。可补充元数据用于审计与回溯。',
icon: CheckCircle2,
variant: 'default',
},
promote: {
label: '晋升固化',
title: (t) => (t ? `晋升固化 - ${t}` : '晋升固化'),
description: '晋升后状态变为 Canonical,作为长期参考。请填写晋升理由便于追溯。',
icon: TrendingUp,
variant: 'outline',
},
archive: {
label: '归档',
title: (t) => (t ? `归档 - ${t}` : '归档记录'),
description: '归档后该记录从队列中移除,但 ledger 历史保留。请说明归档原因。',
icon: Archive,
variant: 'destructive',
},
}
export type ActionConfirmMetadata = {
actor: string
reason: string
evidence_refs: string[]
}
type Props = {
open: boolean
action: DesktopLifecycleAction | null
recordTitle?: string
defaultActor?: string
loading?: boolean
countLabel?: string
onConfirm: (metadata: ActionConfirmMetadata) => void
onCancel: () => void
}
export function ActionConfirmDialog({
open,
action,
recordTitle,
defaultActor,
loading,
countLabel,
onConfirm,
onCancel,
}: Props) {
const [actor, setActor] = React.useState('')
const [reason, setReason] = React.useState('')
const [evidenceRefs, setEvidenceRefs] = React.useState('')
React.useEffect(() => {
if (open) {
setActor(defaultActor ?? '')
setReason('')
setEvidenceRefs('')
}
}, [open, defaultActor])
if (!action) return null
const meta = ACTION_META[action]
const Icon = meta.icon
function handleConfirm() {
onConfirm({
actor: actor.trim(),
reason: reason.trim(),
evidence_refs: evidenceRefs
.split(/[\n,]/)
.map((s) => s.trim())
.filter(Boolean),
})
}
return (
<Dialog
open={open}
onOpenChange={(o) => {
if (!o) onCancel()
}}
>
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Icon className="h-4 w-4" />
{countLabel ? `${meta.label}${countLabel}` : meta.title(recordTitle)}
</DialogTitle>
<DialogDescription>{meta.description}</DialogDescription>
</DialogHeader>
<div className="space-y-3">
<div className="grid gap-1">
<Label className="text-xs" htmlFor="confirm-actor">
actor
</Label>
<Input
id="confirm-actor"
value={actor}
onChange={(e) => setActor(e.target.value)}
placeholder="long"
autoFocus
/>
</div>
<div className="grid gap-1">
<Label className="text-xs" htmlFor="confirm-reason">
reason
</Label>
<Input
id="confirm-reason"
value={reason}
onChange={(e) => setReason(e.target.value)}
placeholder={
action === 'archive' ? 'duplicate / outdated / ...' : 'approved after review'
}
/>
</div>
<div className="grid gap-1">
<Label className="text-xs" htmlFor="confirm-evidence">
evidence_refs
</Label>
<Textarea
id="confirm-evidence"
value={evidenceRefs}
onChange={(e) => setEvidenceRefs(e.target.value)}
rows={2}
placeholder="session:1, obsidian://note"
/>
<p className="text-[10px] text-muted-foreground">
用逗号或换行分隔。可留空。
</p>
</div>
</div>
<DialogFooter>
<Button variant="ghost" onClick={onCancel} disabled={loading}>
取消
</Button>
<Button
variant={meta.variant}
onClick={handleConfirm}
disabled={loading}
>
{loading ? (
<Loader2 className="h-3.5 w-3.5 animate-spin" />
) : (
<Icon className="h-3.5 w-3.5" />
)}
确认{meta.label}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
)
}