Files
stu-ai-demo/src/app/(main)/dev/arch/package/components/PackageAnalyzeDialog.tsx

189 lines
6.2 KiB
TypeScript
Raw 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 React from 'react'
import { trpc } from '@/lib/trpc'
import { Button } from '@/components/ui/button'
import { FileSearch } from 'lucide-react'
import { toast } from 'sonner'
import { TaskDialog, BaseTaskProgress } from '@/components/common/task-dialog'
import type { AnalyzePackagesProgress } from '@/server/queues'
/**
* 扩展的分析进度类型
*/
interface AnalyzeProgress extends BaseTaskProgress, AnalyzePackagesProgress {}
interface PackageAnalyzeDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
jobId: string | null
onAnalyzeCompleted: () => void
}
interface PackageAnalyzeTriggerProps {
onStartAnalyze: () => void
isStarting: boolean
}
/**
* 依赖包分析触发器按钮
*/
export function PackageAnalyzeTrigger({
onStartAnalyze,
isStarting
}: PackageAnalyzeTriggerProps) {
return (
<Button
variant="default"
onClick={onStartAnalyze}
disabled={isStarting}
>
<FileSearch className="mr-2 h-4 w-4" />
{isStarting ? '启动中...' : '依赖包分析'}
</Button>
)
}
/**
* 依赖包分析进度对话框
*/
export function PackageAnalyzeDialog({
open,
onOpenChange,
jobId,
onAnalyzeCompleted
}: PackageAnalyzeDialogProps) {
// 停止分析任务 mutation
const cancelMutation = trpc.devArch!.cancelAnalyzePackagesJob.useMutation({
onSuccess: () => {
toast.success('已发送停止请求')
},
onError: (error) => {
toast.error(error.message || '停止任务失败')
},
})
// 停止任务
const handleCancelTask = async (taskJobId: string) => {
await cancelMutation.mutateAsync({ jobId: taskJobId })
}
// 自定义状态消息渲染
const renderStatusMessage = (progress: AnalyzeProgress) => {
if (progress.state === 'waiting') {
return '任务等待中...'
} else if (progress.state === 'active') {
if (progress.currentPackage) {
return `正在分析: ${progress.currentPackage}`
}
return '正在分析依赖包...'
} else if (progress.state === 'completed') {
const successCount = (progress.analyzedPackages || 0) - (progress.failedPackages || 0)
const failedCount = progress.failedPackages || 0
const skippedCount = progress.skippedPackages || 0
const parts = [`成功 ${successCount}`]
if (failedCount > 0) {
parts.push(`失败 ${failedCount}`)
}
if (skippedCount > 0) {
parts.push(`跳过 ${skippedCount}`)
}
parts.push(`${progress.totalPackages || 0} 个依赖包`)
return `分析完成!${parts.join('')}`
} else if (progress.state === 'failed') {
return progress.error || '分析失败'
}
return ''
}
// 自定义详细信息渲染
const renderDetails = (progress: AnalyzeProgress) => {
if (progress.totalPackages === undefined && progress.analyzedPackages === undefined) {
return null
}
const successCount = (progress.analyzedPackages || 0) - (progress.failedPackages || 0)
return (
<div className="space-y-4">
{/* 进度统计 */}
<div className="grid grid-cols-2 gap-4 text-sm">
{progress.totalPackages !== undefined && (
<div>
<span className="text-muted-foreground"></span>
<span className="ml-1 font-medium">{progress.totalPackages}</span>
</div>
)}
{progress.analyzedPackages !== undefined && (
<div>
<span className="text-muted-foreground"></span>
<span className="ml-1 font-medium">{progress.analyzedPackages}</span>
</div>
)}
{successCount > 0 && (
<div>
<span className="text-muted-foreground"></span>
<span className="ml-1 font-medium text-green-600">{successCount}</span>
</div>
)}
{progress.failedPackages !== undefined && progress.failedPackages > 0 && (
<div>
<span className="text-muted-foreground"></span>
<span className="ml-1 font-medium text-red-600">{progress.failedPackages}</span>
</div>
)}
{progress.skippedPackages !== undefined && progress.skippedPackages > 0 && (
<div>
<span className="text-muted-foreground"></span>
<span className="ml-1 font-medium text-blue-600">{progress.skippedPackages}</span>
</div>
)}
</div>
{/* 当前处理的依赖包 */}
{progress.currentPackage && progress.state === 'active' && (
<div className="rounded-md bg-muted p-3 text-sm">
<div className="text-muted-foreground mb-1"></div>
<div className="font-mono text-xs break-all">{progress.currentPackage}</div>
</div>
)}
{/* 最近的错误信息 */}
{progress.recentErrors && progress.recentErrors.length > 0 && (
<div className="rounded-md border border-red-200 bg-red-50 p-3 text-sm">
<div className="text-red-800 font-medium mb-2"> (10)</div>
<div className="space-y-2 max-h-40 overflow-y-auto">
{progress.recentErrors.map((err, index) => (
<div key={index} className="text-xs">
<div className="font-mono text-red-700 break-all">{err.packageName}</div>
<div className="text-red-600 mt-1">{err.error}</div>
{index < progress.recentErrors!.length - 1 && (
<div className="border-t border-red-200 mt-2" />
)}
</div>
))}
</div>
</div>
)}
</div>
)
}
return (
<TaskDialog<AnalyzeProgress>
open={open}
onOpenChange={onOpenChange}
useSubscription={trpc.jobs.subscribeAnalyzePackagesProgress.useSubscription}
jobId={jobId}
title="依赖包分析进度"
description="正在使用AI分析项目依赖包请稍候..."
onCancelTask={handleCancelTask}
onTaskCompleted={onAnalyzeCompleted}
isCancelling={cancelMutation.isPending}
renderStatusMessage={renderStatusMessage}
renderDetails={renderDetails}
/>
)
}