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