feat(ui): enhance ClearDocumentsDialog with loading spinner and timeout protection
- Add loading spinner animation during document clearing operation - Implement 30-second timeout protection to prevent hanging operations - Disable all interactive controls during clearing to prevent duplicate requests - Add comprehensive error handling with automatic state reset
This commit is contained in:
parent
45365ff6ef
commit
e064534941
6 changed files with 70 additions and 7 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { useState, useCallback, useEffect } from 'react'
|
||||
import { useState, useCallback, useEffect, useRef } from 'react'
|
||||
import Button from '@/components/ui/Button'
|
||||
import {
|
||||
Dialog,
|
||||
|
|
@ -15,7 +15,7 @@ import { toast } from 'sonner'
|
|||
import { errorMessage } from '@/lib/utils'
|
||||
import { clearDocuments, clearCache } from '@/api/lightrag'
|
||||
|
||||
import { EraserIcon, AlertTriangleIcon } from 'lucide-react'
|
||||
import { EraserIcon, AlertTriangleIcon, Loader2Icon } from 'lucide-react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
// 简单的Label组件
|
||||
|
|
@ -43,18 +43,51 @@ export default function ClearDocumentsDialog({ onDocumentsCleared }: ClearDocume
|
|||
const [open, setOpen] = useState(false)
|
||||
const [confirmText, setConfirmText] = useState('')
|
||||
const [clearCacheOption, setClearCacheOption] = useState(false)
|
||||
const [isClearing, setIsClearing] = useState(false)
|
||||
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
const isConfirmEnabled = confirmText.toLowerCase() === 'yes'
|
||||
|
||||
// 超时常量 (30秒)
|
||||
const CLEAR_TIMEOUT = 30000
|
||||
|
||||
// 重置状态当对话框关闭时
|
||||
useEffect(() => {
|
||||
if (!open) {
|
||||
setConfirmText('')
|
||||
setClearCacheOption(false)
|
||||
setIsClearing(false)
|
||||
|
||||
// 清理超时定时器
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current)
|
||||
timeoutRef.current = null
|
||||
}
|
||||
}
|
||||
}, [open])
|
||||
|
||||
// 组件卸载时的清理
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
// 组件卸载时清理超时定时器
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleClear = useCallback(async () => {
|
||||
if (!isConfirmEnabled) return
|
||||
if (!isConfirmEnabled || isClearing) return
|
||||
|
||||
setIsClearing(true)
|
||||
|
||||
// 设置超时保护
|
||||
timeoutRef.current = setTimeout(() => {
|
||||
if (isClearing) {
|
||||
toast.error(t('documentPanel.clearDocuments.timeout'))
|
||||
setIsClearing(false)
|
||||
setConfirmText('') // 超时后重置确认文本
|
||||
}
|
||||
}, CLEAR_TIMEOUT)
|
||||
|
||||
try {
|
||||
const result = await clearDocuments()
|
||||
|
|
@ -86,8 +119,15 @@ export default function ClearDocumentsDialog({ onDocumentsCleared }: ClearDocume
|
|||
} catch (err) {
|
||||
toast.error(t('documentPanel.clearDocuments.error', { error: errorMessage(err) }))
|
||||
setConfirmText('')
|
||||
} finally {
|
||||
// 清除超时定时器
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current)
|
||||
timeoutRef.current = null
|
||||
}
|
||||
setIsClearing(false)
|
||||
}
|
||||
}, [isConfirmEnabled, clearCacheOption, setOpen, t, onDocumentsCleared])
|
||||
}, [isConfirmEnabled, isClearing, clearCacheOption, setOpen, t, onDocumentsCleared, CLEAR_TIMEOUT])
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
|
|
@ -125,6 +165,7 @@ export default function ClearDocumentsDialog({ onDocumentsCleared }: ClearDocume
|
|||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setConfirmText(e.target.value)}
|
||||
placeholder={t('documentPanel.clearDocuments.confirmPlaceholder')}
|
||||
className="w-full"
|
||||
disabled={isClearing}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
@ -133,6 +174,7 @@ export default function ClearDocumentsDialog({ onDocumentsCleared }: ClearDocume
|
|||
id="clear-cache"
|
||||
checked={clearCacheOption}
|
||||
onCheckedChange={(checked: boolean | 'indeterminate') => setClearCacheOption(checked === true)}
|
||||
disabled={isClearing}
|
||||
/>
|
||||
<Label htmlFor="clear-cache" className="text-sm font-medium cursor-pointer">
|
||||
{t('documentPanel.clearDocuments.clearCache')}
|
||||
|
|
@ -141,15 +183,26 @@ export default function ClearDocumentsDialog({ onDocumentsCleared }: ClearDocume
|
|||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setOpen(false)}>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setOpen(false)}
|
||||
disabled={isClearing}
|
||||
>
|
||||
{t('common.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
onClick={handleClear}
|
||||
disabled={!isConfirmEnabled}
|
||||
disabled={!isConfirmEnabled || isClearing}
|
||||
>
|
||||
{t('documentPanel.clearDocuments.confirmButton')}
|
||||
{isClearing ? (
|
||||
<>
|
||||
<Loader2Icon className="mr-2 h-4 w-4 animate-spin" />
|
||||
{t('documentPanel.clearDocuments.clearing')}
|
||||
</>
|
||||
) : (
|
||||
t('documentPanel.clearDocuments.confirmButton')
|
||||
)}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@
|
|||
"confirmPlaceholder": "اكتب yes للتأكيد",
|
||||
"clearCache": "مسح كاش نموذج اللغة",
|
||||
"confirmButton": "نعم",
|
||||
"clearing": "جارٍ المسح...",
|
||||
"timeout": "انتهت مهلة عملية المسح، يرجى المحاولة مرة أخرى",
|
||||
"success": "تم مسح المستندات بنجاح",
|
||||
"cacheCleared": "تم مسح ذاكرة التخزين المؤقت بنجاح",
|
||||
"cacheClearFailed": "فشل مسح ذاكرة التخزين المؤقت:\n{{error}}",
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@
|
|||
"confirmPlaceholder": "Type yes to confirm",
|
||||
"clearCache": "Clear LLM cache",
|
||||
"confirmButton": "YES",
|
||||
"clearing": "Clearing...",
|
||||
"timeout": "Clear operation timed out, please try again",
|
||||
"success": "Documents cleared successfully",
|
||||
"cacheCleared": "Cache cleared successfully",
|
||||
"cacheClearFailed": "Failed to clear cache:\n{{error}}",
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@
|
|||
"confirmPlaceholder": "Tapez yes pour confirmer",
|
||||
"clearCache": "Effacer le cache LLM",
|
||||
"confirmButton": "OUI",
|
||||
"clearing": "Effacement en cours...",
|
||||
"timeout": "L'opération d'effacement a expiré, veuillez réessayer",
|
||||
"success": "Documents effacés avec succès",
|
||||
"cacheCleared": "Cache effacé avec succès",
|
||||
"cacheClearFailed": "Échec de l'effacement du cache :\n{{error}}",
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@
|
|||
"confirmPlaceholder": "输入 yes 确认",
|
||||
"clearCache": "清空LLM缓存",
|
||||
"confirmButton": "确定",
|
||||
"clearing": "正在清除...",
|
||||
"timeout": "清除操作超时,请重试",
|
||||
"success": "文档清空成功",
|
||||
"cacheCleared": "缓存清空成功",
|
||||
"cacheClearFailed": "清空缓存失败:\n{{error}}",
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@
|
|||
"confirmPlaceholder": "輸入 yes 以確認",
|
||||
"clearCache": "清空 LLM 快取",
|
||||
"confirmButton": "確定",
|
||||
"clearing": "正在清除...",
|
||||
"timeout": "清除操作逾時,請重試",
|
||||
"success": "文件清空成功",
|
||||
"cacheCleared": "快取清空成功",
|
||||
"cacheClearFailed": "清空快取失敗:\n{{error}}",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue