ux(review): spinner, tooltip, chip en cours et snackbar sur Révision IA

This commit is contained in:
4NK IA 2025-09-18 12:56:58 +00:00
parent 4bed3562b1
commit 1e207f734e

View File

@ -8,6 +8,8 @@ import {
Skeleton,
Alert,
Button,
Snackbar,
Alert as MuiAlert,
Chip,
List,
ListItem,
@ -50,7 +52,7 @@ import type { Document } from '../types'
import { confirmDetectedAddress, deleteFolderFile, reviewFileWithAI } from '../services/folderApi'
// Composant mémorisé pour les items de la liste
const DocumentListItem = memo(({ doc, index, onPreview, onDelete, onReplace, onConfirmAddress, onReview, onOpenCorrections, totalCount }: {
const DocumentListItem = memo(({ doc, index, onPreview, onDelete, onReplace, onConfirmAddress, onReview, onOpenCorrections, isReviewing, totalCount }: {
doc: Document,
index: number,
onPreview: (doc: Document) => void,
@ -59,6 +61,7 @@ const DocumentListItem = memo(({ doc, index, onPreview, onDelete, onReplace, onC
onConfirmAddress: (doc: Document) => void,
onReview: (doc: Document) => void,
onOpenCorrections: (doc: Document, corrections: any[]) => void,
isReviewing: boolean,
totalCount: number
}) => {
const getFileIcon = (mimeType: string) => {
@ -151,6 +154,9 @@ const DocumentListItem = memo(({ doc, index, onPreview, onDelete, onReplace, onC
variant="outlined"
/>
)}
{isReviewing && (
<Chip color="default" label="Révision IA en cours…" size="small" />
)}
{(() => {
const anyDoc: any = doc as any
const score: number | undefined =
@ -223,14 +229,18 @@ const DocumentListItem = memo(({ doc, index, onPreview, onDelete, onReplace, onC
>
Aperçu
</Button>
<Button
size="small"
onClick={() => onReview(doc)}
disabled={doc.status !== 'completed'}
fullWidth
>
Révision IA
</Button>
<Tooltip title="Analyse IA (Ollama): calcule un score de fiabilité, propose des corrections, ajoute un avis.">
<span>
<Button
size="small"
onClick={() => onReview(doc)}
disabled={doc.status !== 'completed' || isReviewing}
fullWidth
>
{isReviewing ? 'Révision IA…' : 'Révision IA'}
</Button>
</span>
</Tooltip>
<Button
size="small"
color="error"
@ -276,6 +286,8 @@ export default function UploadView() {
const [reviewDialogOpen, setReviewDialogOpen] = useState(false)
const [reviewDoc, setReviewDoc] = useState<Document | null>(null)
const [reviewCorrections, setReviewCorrections] = useState<any[]>([])
const [reviewingId, setReviewingId] = useState<string | null>(null)
const [snack, setSnack] = useState<{ open: boolean; severity: 'success' | 'error'; message: string }>({ open: false, severity: 'success', message: '' })
const handleConfirmAddress = useCallback((doc: Document) => {
try {
@ -308,10 +320,15 @@ export default function UploadView() {
const handleReview = useCallback(async (doc: Document) => {
if (!currentFolderHash) return
try {
setReviewingId(doc.id)
await reviewFileWithAI(currentFolderHash, doc.id)
await dispatch(loadFolderResults(currentFolderHash)).unwrap()
setSnack({ open: true, severity: 'success', message: 'Révision IA terminée: score et corrections mis à jour.' })
} catch (e) {
console.error('❌ Révision IA:', e)
setSnack({ open: true, severity: 'error', message: "Échec de la révision IA. Réessaie plus tard." })
} finally {
setReviewingId(null)
}
}, [currentFolderHash, dispatch])
@ -581,6 +598,7 @@ export default function UploadView() {
onConfirmAddress={handleConfirmAddress}
onReview={handleReview}
onOpenCorrections={(d, corr) => { setReviewDoc(d); setReviewCorrections(corr); setReviewDialogOpen(true) }}
isReviewing={reviewingId === doc.id}
totalCount={memoizedDocuments.length}
/>
))}
@ -780,6 +798,18 @@ export default function UploadView() {
<Button onClick={() => setReviewDialogOpen(false)}>Fermer</Button>
</DialogActions>
</Dialog>
{/* Snackbar feedback */}
<Snackbar
open={snack.open}
autoHideDuration={3000}
onClose={() => setSnack({ ...snack, open: false })}
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
>
<MuiAlert onClose={() => setSnack({ ...snack, open: false })} severity={snack.severity} variant="filled" sx={{ width: '100%' }}>
{snack.message}
</MuiAlert>
</Snackbar>
</Layout>
)
}