Files
hair-keeper/src/app/(main)/dev/file/directory-tree/components/FolderAnalyzeDialog.tsx

180 lines
5.8 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 { FolderSearch } from 'lucide-react'
import { toast } from 'sonner'
import { TaskDialog, BaseTaskProgress } from '@/components/common/task-dialog'
import type { AnalyzeFoldersProgress } from '@/server/queues'
/**
* 扩展的分析进度类型
*/
interface AnalyzeProgress extends BaseTaskProgress, AnalyzeFoldersProgress {}
interface FolderAnalyzeDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
jobId: string | null
onAnalyzeCompleted: () => void
}
interface FolderAnalyzeTriggerProps {
onStartAnalyze: () => void
isStarting: boolean
}
/**
* 文件夹分析触发器按钮
*/
export function FolderAnalyzeTrigger({
onStartAnalyze,
isStarting
}: FolderAnalyzeTriggerProps) {
return (
<Button
variant="default"
onClick={onStartAnalyze}
disabled={isStarting}
className="w-full"
>
<FolderSearch className="mr-2 h-4 w-4" />
{isStarting ? '启动中...' : '启动文件夹分析'}
</Button>
)
}
/**
* 文件夹分析进度对话框
*/
export function FolderAnalyzeDialog({
open,
onOpenChange,
jobId,
onAnalyzeCompleted
}: FolderAnalyzeDialogProps) {
// 停止分析任务 mutation
const cancelMutation = trpc.devFile!.cancelAnalyzeFoldersJob.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.currentFolder) {
return `正在分析: ${progress.currentFolder}`
}
return '正在分析文件夹...'
} else if (progress.state === 'completed') {
const successCount = (progress.analyzedFolders || 0) - (progress.failedFolders || 0)
const failedCount = progress.failedFolders || 0
const parts = [`成功 ${successCount}`]
if (failedCount > 0) {
parts.push(`失败 ${failedCount}`)
}
parts.push(`${progress.totalFolders || 0} 个文件夹`)
return `分析完成!${parts.join('')}`
} else if (progress.state === 'failed') {
return progress.error || '分析失败'
}
return ''
}
// 自定义详细信息渲染
const renderDetails = (progress: AnalyzeProgress) => {
if (progress.totalFolders === undefined && progress.analyzedFolders === undefined) {
return null
}
const successCount = (progress.analyzedFolders || 0) - (progress.failedFolders || 0)
return (
<div className="space-y-4">
{/* 进度统计 */}
<div className="grid grid-cols-2 gap-4 text-sm">
{progress.totalFolders !== undefined && (
<div>
<span className="text-muted-foreground"></span>
<span className="ml-1 font-medium">{progress.totalFolders}</span>
</div>
)}
{progress.analyzedFolders !== undefined && (
<div>
<span className="text-muted-foreground"></span>
<span className="ml-1 font-medium">{progress.analyzedFolders}</span>
</div>
)}
{successCount > 0 && (
<div>
<span className="text-muted-foreground"></span>
<span className="ml-1 font-medium text-green-600">{successCount}</span>
</div>
)}
{progress.failedFolders !== undefined && progress.failedFolders > 0 && (
<div>
<span className="text-muted-foreground"></span>
<span className="ml-1 font-medium text-red-600">{progress.failedFolders}</span>
</div>
)}
</div>
{/* 当前处理的文件夹 */}
{progress.currentFolder && 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.currentFolder}</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.folderPath}</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.subscribeAnalyzeFoldersProgress.useSubscription}
jobId={jobId}
title="文件夹分析进度"
description="正在使用AI分析项目文件夹请稍候..."
onCancelTask={handleCancelTask}
onTaskCompleted={onAnalyzeCompleted}
isCancelling={cancelMutation.isPending}
renderStatusMessage={renderStatusMessage}
renderDetails={renderDetails}
/>
)
}