4NK_IA_front/src/views/ExtractionView.tsx
Nicolas Cantu 0bb4ea6678 feat: complete UI implementation with Material-UI and full functionality
- Add complete TypeScript types for all document entities
- Implement Redux store with document slice and async thunks
- Create comprehensive API services layer with external integrations
- Build complete UI views with Material-UI components:
  * UploadView: drag&drop with file preview and status
  * ExtractionView: structured data display (identities, addresses, properties, contracts)
  * AnalyseView: CNI verification, credibility scoring, recommendations
  * ContexteView: external data sources (Cadastre, Géorisques, BODACC, etc.)
  * ConseilView: LLM analysis with risks and next steps
- Add Layout component with navigation tabs
- Configure environment variables for backend integration
- Fix all TypeScript compilation errors
- Replace Grid with Box for better compatibility
- Add comprehensive error handling and loading states
2025-09-10 17:50:42 +02:00

288 lines
9.6 KiB
TypeScript

import { useEffect } from 'react'
import {
Box,
Typography,
Paper,
Card,
CardContent,
Chip,
List,
ListItem,
ListItemText,
Alert,
CircularProgress,
} from '@mui/material'
import {
Person,
LocationOn,
Home,
Description,
Language,
Verified,
} from '@mui/icons-material'
import { useAppDispatch, useAppSelector } from '../store'
import { extractDocument } from '../store/documentSlice'
import { Layout } from '../components/Layout'
export default function ExtractionView() {
const dispatch = useAppDispatch()
const { currentDocument, extractionResult, loading } = useAppSelector(
(state) => state.document
)
useEffect(() => {
if (currentDocument && !extractionResult) {
dispatch(extractDocument(currentDocument.id))
}
}, [currentDocument, extractionResult, dispatch])
if (!currentDocument) {
return (
<Layout>
<Alert severity="info">
Veuillez d'abord téléverser et sélectionner un document.
</Alert>
</Layout>
)
}
if (loading) {
return (
<Layout>
<Box sx={{ display: 'flex', justifyContent: 'center', mt: 4 }}>
<CircularProgress />
<Typography sx={{ ml: 2 }}>Extraction en cours...</Typography>
</Box>
</Layout>
)
}
if (!extractionResult) {
return (
<Layout>
<Alert severity="warning">
Aucun résultat d'extraction disponible.
</Alert>
</Layout>
)
}
return (
<Layout>
<Typography variant="h4" gutterBottom>
Extraction des données
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
{/* Informations générales */}
<Paper sx={{ p: 2 }}>
<Typography variant="h6" gutterBottom>
Informations générales
</Typography>
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
<Chip
icon={<Language />}
label={`Langue: ${extractionResult.language}`}
color="primary"
variant="outlined"
/>
<Chip
icon={<Description />}
label={`Type: ${extractionResult.documentType}`}
color="secondary"
variant="outlined"
/>
<Chip
icon={<Verified />}
label={`Confiance: ${(extractionResult.confidence * 100).toFixed(1)}%`}
color={extractionResult.confidence > 0.8 ? 'success' : 'warning'}
variant="outlined"
/>
</Box>
</Paper>
<Box sx={{ display: 'flex', gap: 3, flexWrap: 'wrap' }}>
{/* Identités */}
<Box sx={{ flex: '1 1 300px' }}>
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
<Person sx={{ mr: 1, verticalAlign: 'middle' }} />
Identités ({extractionResult.identities.length})
</Typography>
<List dense>
{extractionResult.identities.map((identity, index) => (
<ListItem key={index}>
<ListItemText
primary={
identity.type === 'person'
? `${identity.firstName} ${identity.lastName}`
: identity.companyName
}
secondary={
<Box>
<Typography variant="caption" display="block">
Type: {identity.type}
</Typography>
{identity.birthDate && (
<Typography variant="caption" display="block">
Naissance: {identity.birthDate}
</Typography>
)}
{identity.nationality && (
<Typography variant="caption" display="block">
Nationalité: {identity.nationality}
</Typography>
)}
<Typography variant="caption" display="block">
Confiance: {(identity.confidence * 100).toFixed(1)}%
</Typography>
</Box>
}
/>
</ListItem>
))}
</List>
</CardContent>
</Card>
</Box>
{/* Adresses */}
<Box sx={{ flex: '1 1 300px' }}>
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
<LocationOn sx={{ mr: 1, verticalAlign: 'middle' }} />
Adresses ({extractionResult.addresses.length})
</Typography>
<List dense>
{extractionResult.addresses.map((address, index) => (
<ListItem key={index}>
<ListItemText
primary={`${address.street}, ${address.city}`}
secondary={`${address.postalCode} ${address.country}`}
/>
</ListItem>
))}
</List>
</CardContent>
</Card>
</Box>
</Box>
<Box sx={{ display: 'flex', gap: 3, flexWrap: 'wrap' }}>
{/* Biens */}
<Box sx={{ flex: '1 1 300px' }}>
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
<Home sx={{ mr: 1, verticalAlign: 'middle' }} />
Biens ({extractionResult.properties.length})
</Typography>
<List dense>
{extractionResult.properties.map((property, index) => (
<ListItem key={index}>
<ListItemText
primary={`${property.type} - ${property.address.city}`}
secondary={
<Box>
<Typography variant="caption" display="block">
{property.address.street}
</Typography>
{property.surface && (
<Typography variant="caption" display="block">
Surface: {property.surface} m²
</Typography>
)}
{property.cadastralReference && (
<Typography variant="caption" display="block">
Cadastre: {property.cadastralReference}
</Typography>
)}
</Box>
}
/>
</ListItem>
))}
</List>
</CardContent>
</Card>
</Box>
{/* Contrats */}
<Box sx={{ flex: '1 1 300px' }}>
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
<Description sx={{ mr: 1, verticalAlign: 'middle' }} />
Contrats ({extractionResult.contracts.length})
</Typography>
<List dense>
{extractionResult.contracts.map((contract, index) => (
<ListItem key={index}>
<ListItemText
primary={`${contract.type} - ${contract.amount ? `${contract.amount}` : 'Montant non spécifié'}`}
secondary={
<Box>
<Typography variant="caption" display="block">
Parties: {contract.parties.length}
</Typography>
{contract.date && (
<Typography variant="caption" display="block">
Date: {contract.date}
</Typography>
)}
<Typography variant="caption" display="block">
Clauses: {contract.clauses.length}
</Typography>
</Box>
}
/>
</ListItem>
))}
</List>
</CardContent>
</Card>
</Box>
</Box>
{/* Signatures */}
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
Signatures détectées ({extractionResult.signatures.length})
</Typography>
<List dense>
{extractionResult.signatures.map((signature, index) => (
<ListItem key={index}>
<ListItemText primary={signature} />
</ListItem>
))}
</List>
</CardContent>
</Card>
{/* Texte extrait */}
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
Texte extrait
</Typography>
<Paper
sx={{
p: 2,
bgcolor: 'grey.50',
maxHeight: 300,
overflow: 'auto',
}}
>
<Typography variant="body2" sx={{ whiteSpace: 'pre-wrap' }}>
{extractionResult.text}
</Typography>
</Paper>
</CardContent>
</Card>
</Box>
</Layout>
)
}