Compare commits

...

18 Commits
v0.1.0 ... dev

Author SHA1 Message Date
ae8e647cf0 ci: gitea workflow + compose registry deployment
Some checks are pending
Build & Push Docker Image / docker (push) Waiting to run
2025-09-11 18:12:22 +02:00
68930c6e4b ci: trigger docker build/push for dev 2025-09-11 16:54:04 +02:00
81d39172d0 chore(release): 0.1.3\n\n- Docker packaging (Dockerfile, nginx.conf, scripts)\n- Fix ExtractionView hydration (no div in p)\n- Node version checks, .nvmrc\n- Docs: README/DEPLOYMENT updates\n- Version bump to 0.1.3 2025-09-11 16:43:44 +02:00
6600308d61 feat(api): alignement back/front + support functionalType/mimeType + modes simple/complete (VITE_BACKEND_MODE) 2025-09-11 12:28:31 +02:00
013a6dda0a chore(scripts): déplacer analyze-document.py, simple-server.js, start-frontend.sh dans scripts/ 2025-09-11 12:03:31 +02:00
8414d13972 docs(deployment): notes de version 0.1.1 et procédure 2025-09-11 11:56:33 +02:00
152dbc3ed1 chore(release): 0.1.1\n\n- Lint Markdown: ignore guides/rapports, correction sample.md\n- ESLint: ignore coverage, fix no-unused-vars/no-explicit-any\n- TS: corrections FilePreview/api/views, build OK\n- Tests: Vitest OK 2025-09-11 11:50:02 +02:00
root
2a8123bc35 fix: Suppression du bouton Analyser et corrections des erreurs JavaScript
- Suppression du bouton 'Analyser' à côté de l'aperçu
- Correction des erreurs JavaScript dans ExtractionView
- Amélioration de la gestion des erreurs dans l'API
- Alignement avec les endpoints du backend
2025-09-10 23:59:48 +02:00
root
6ae698de76 feat: Implémentation de l'aperçu PDF fonctionnel
- Ajout du composant FilePreview avec iframe et viewer PDF intégré
- Correction du mapping des données API (document_id -> id)
- Ajout de previewUrl avec URL.createObjectURL pour l'aperçu
- Interface modale avec navigation et zoom
- Support des fichiers PDF avec affichage réel du contenu
- Correction de l'affichage de la taille des fichiers
- Ajout des dépendances react-pdf-js pour l'aperçu PDF
- Scripts de démarrage et fichiers de test
- Documentation complète de la fonctionnalité d'aperçu
2025-09-10 23:25:55 +02:00
e69fa95463 fix: resolve remaining markdownlint issues
- Fix line length issues in documentation files
- Add language specifications to code blocks
- Resolve duplicate heading in README.md
- Ensure all markdown files follow best practices
2025-09-10 18:47:20 +02:00
afb58ef4b1 docs: update complete documentation
- Update README.md with comprehensive project documentation
- Update CHANGELOG.md with detailed version 0.1.0 features
- Add ARCHITECTURE.md with technical architecture details
- Add API.md with complete API documentation
- Add DEPLOYMENT.md with deployment guides and configurations
- Add TESTING.md with testing strategies and examples
- Fix markdownlint issues across all documentation files
- Ensure all documentation follows markdown best practices
2025-09-10 18:47:09 +02:00
7e46e1d992 fix: resolve HTML hydration error and improve error handling
- Fix HTML structure error: remove nested div inside p element in UploadView
- Restructure ListItemText secondary content to use valid HTML
- Add warning log for upload failures with demo data fallback
- Improve error handling for ERR_CONNECTION_REFUSED
- Ensure proper HTML semantics and prevent hydration errors
2025-09-10 18:36:57 +02:00
e63dccf9f3 fix: improve error handling for 405 Method Not Allowed
- Add 405 and 404 status codes to error interceptor
- Implement try-catch in upload method for better error handling
- Return demo data when backend endpoints are not supported
- Provide seamless fallback for all API errors
- Improve user experience with graceful error handling
2025-09-10 18:31:43 +02:00
91b44e06ad fix: remove backend health check to prevent 404 errors
- Remove unnecessary backend connectivity check in Layout component
- Simplify Layout component by removing unused imports and state
- Application now works seamlessly in demo mode without backend errors
- Clean console output without 404 errors on startup
2025-09-10 18:27:11 +02:00
0b14fbe6b7 feat: add graceful error handling and demo mode
- Add fallback data for all API endpoints when backend is unavailable
- Implement demo mode with realistic sample data for all views
- Add notification to inform users when running in demo mode
- Improve error handling with try-catch blocks in API services
- Add backend connectivity check in Layout component
- Provide seamless user experience even without backend connection
2025-09-10 18:17:08 +02:00
bb133d5448 feat: apply white background theme and improve UI styling
- Add Material-UI theme with white background configuration
- Update CSS with white background and light color scheme
- Integrate ThemeProvider and CssBaseline for consistent styling
- Remove dark theme elements and apply clean white design
- Improve typography and color contrast for better readability
2025-09-10 17:55:39 +02:00
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
c2eba34f57 feat: add Redux store, React Router, code splitting, views skeleton, API services
- Redux Toolkit store with app slice
- React Router with lazy loading (Upload, Extraction, Analyse, Contexte, Conseil)
- API services layer with axios client
- View components skeleton for all main features
- Code splitting with React.lazy and Suspense
2025-09-10 17:37:17 +02:00
78 changed files with 17242 additions and 313 deletions

18
.dockerignore Normal file
View File

@ -0,0 +1,18 @@
node_modules
coverage
.git
.vscode
.idea
*.log
.env
.env.*
**/.DS_Store
**/*.test.*
**/*.spec.*
**/tests/**
**/test/**
dist
README.md
CHANGELOG.md
CODE_OF_CONDUCT.md
CONTRIBUTING.md

13
.env Normal file
View File

@ -0,0 +1,13 @@
# Configuration API Backend
VITE_API_URL=http://localhost:18000
# Configuration pour le développement
VITE_APP_NAME=4NK IA Front Notarial
VITE_APP_VERSION=0.1.0
# Configuration des services externes (optionnel)
VITE_CADASTRE_API_URL=https://apicarto.ign.fr/api/cadastre
VITE_GEORISQUES_API_URL=https://www.georisques.gouv.fr/api
VITE_GEOFONCIER_API_URL=https://api2.geofoncier.fr
VITE_BODACC_API_URL=https://bodacc-datadila.opendatasoft.com/api
VITE_INFOGREFFE_API_URL=https://entreprise.api.gouv.fr

13
.env.exemple Normal file
View File

@ -0,0 +1,13 @@
# Configuration API Backend
VITE_API_URL=http://localhost:8000
# Configuration pour le développement
VITE_APP_NAME=4NK IA Front Notarial
VITE_APP_VERSION=0.1.0
# Configuration des services externes (optionnel)
VITE_CADASTRE_API_URL=https://apicarto.ign.fr/api/cadastre
VITE_GEORISQUES_API_URL=https://www.georisques.gouv.fr/api
VITE_GEOFONCIER_API_URL=https://api2.geofoncier.fr
VITE_BODACC_API_URL=https://bodacc-datadila.opendatasoft.com/api
VITE_INFOGREFFE_API_URL=https://entreprise.api.gouv.fr

View File

@ -0,0 +1,48 @@
name: Build & Push Docker Image
on:
push:
branches:
- '**'
jobs:
docker:
runs-on: debian-runner
env:
REGISTRY_URL: ${{ secrets.REGISTRY_URL || 'git.4nkweb.com' }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node (for potential pre-steps)
uses: actions/setup-node@v4
with:
node-version: '20.19'
cache: 'npm'
- name: Compute tags
id: meta
run: |
BRANCH=$(git rev-parse --abbrev-ref HEAD)
# sanitize branch to be a valid docker tag
SANITIZED=$(echo "$BRANCH" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9._-]+/-/g')
echo "branch=$SANITIZED" >> "$GITHUB_OUTPUT"
VERSION=$(jq -r .version package.json)
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Log in to registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY_URL }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Build & Push
uses: docker/build-push-action@v6
with:
context: .
push: true
provenance: false
tags: |
${{ env.REGISTRY_URL }}/4nk/4nk-ia-front:${{ steps.meta.outputs.branch }}
${{ env.REGISTRY_URL }}/4nk/4nk-ia-front:${{ steps.meta.outputs.version }}

5
.markdownlintignore Normal file
View File

@ -0,0 +1,5 @@
# Ignorer les guides et rapports générés
GUIDE_TEST_APERCU_CORRIGE.md
GUIDE_TEST_EXTRACTION_CORRIGEE.md
RAPPORT_ALIGNEMENT_BACKEND.md
RAPPORT_ANALYSE_DOCUMENT.md

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
20.19.5

View File

@ -1,7 +1,130 @@
# Changelog
## 0.1.0 - Initialisation
## [0.1.3] - 2025-09-11
### Ajouté
- Dockerfile multi-stage (build Node 20 LTS → Nginx) et `nginx.conf`
- `.dockerignore` pour un contexte minimal
- Scripts `scripts/docker-build.sh` et `scripts/docker-push.sh`
- Scaffold Vite React+TS
- Outillage Node 22.12, ESLint, Prettier, markdownlint
- Vitest et Testing Library
### Modifié
- `ExtractionView.tsx`: correction du balisage pour éviter `<div>` dans `<p>` (hydratation)
- Documentation: README et `docs/DEPLOYMENT.md` mis à jour (Docker, versions Node)
### Technique
- `.nvmrc` ajouté et vérification `predev`/`prebuild` de la version Node
## 0.1.2 - Suppression du mode démo et routage backend
### 🔄 Changements majeurs
- Suppression complète du « mode simple » et des fallbacks de démonstration dans
`src/services/api.ts`.
- Tous les appels aux APIs externes (Cadastre, Géorisques, Géofoncier, BODACC,
Infogreffe) sont désormais routés via le backend `4NK_IA_back` (`/api/context/...`).
### 🧩 Code
- `src/services/api.ts`: suppression de `VITE_BACKEND_MODE`, intercepteur Axios német plus de
valeurs mockées, endpoints `analyze/context/conseil` strictement consommés sur
`/api/documents/...`.
### 📚 Documentation
- `README.md`: mise à jour des versions (React 19 / MUI 7 / Router 7), retrait de la section
« Mode démonstration », clarification de lintégration backend.
### ✅ Qualité
- Lint, tests et markdownlint OK après modifications.
## 0.1.1 - Maintenance lint/build et corrections
### ✅ Qualité et lint
- Ajout de `.markdownlintignore` pour exclure les guides/rapports générés du lint
- Correction du fichier `test-files/sample.md` pour respecter MD022/MD032/MD009
- ESLint: exclusion du dossier `coverage` et correction des erreurs `no-unused-vars`/`no-explicit-any`
### 🛠️ Corrections TypeScript/Build
- `src/components/FilePreview.tsx`: correction de lutilisation de `document.createElement`
- `src/services/api.ts`: typage des blocs `catch`, renommage des variables inutilisées, ajustements mineurs
- `src/views/*`: typage strict des couleurs MUI pour `Chip`/icônes, imports nettoyés
- Build Vite/TS: passe en production sans erreurs
### 🔬 Tests
- Vitest: exécution réussie de la suite, couverture générée
## 0.1.0 - Version initiale complète
### ✨ Fonctionnalités principales
- **Interface notariale complète** : Application front-end pour l'analyse de documents notariaux
- **Upload de documents** : Glisser-déposer avec prévisualisation (PDF, images)
- **Extraction de données** : OCR et identification d'objets standardisés
- **Analyse intelligente** : Score de vraisemblance et recommandations
- **Données contextuelles** : Intégration APIs externes (Cadastre, Géorisques, BODACC, Infogreffe)
- **Conseil IA** : Analyse LLM avec détection de risques
### 🏗️ Architecture technique
- **React 18 + TypeScript** : Framework moderne avec typage strict
- **Vite 7** : Build tool rapide et moderne
- **Material-UI v6** : Interface utilisateur professionnelle
- **Redux Toolkit** : Gestion d'état centralisée
- **React Router v6** : Navigation avec code splitting
- **Axios** : Client HTTP avec intercepteurs
### 🛠️ Outillage et qualité
- **ESLint + Prettier** : Linting et formatage automatique
- **markdownlint** : Validation des fichiers Markdown
- **Vitest + Testing Library** : Tests unitaires et d'intégration
- **Coverage V8** : Rapport de couverture de code
### 📚 Documentation et gouvernance
- **README complet** : Documentation technique détaillée
- **Fichiers open-source** : LICENSE (MIT), CONTRIBUTING.md, CODE_OF_CONDUCT.md
- **Structure docs/** : Documentation technique organisée
- **Tests/** : Squelette de tests avec exemples
### 🔧 Gestion d'erreur et robustesse
- **Mode démonstration** : Fonctionnement complet sans backend
- **Gestion d'erreur gracieuse** : Fallback automatique pour tous les types d'erreurs
- **Intercepteurs Axios** : Gestion centralisée des erreurs API
- **Données de démonstration** : Exemples réalistes d'actes notariaux
### 🎨 Interface utilisateur
- **Design professionnel** : Interface claire avec fond blanc
- **Navigation intuitive** : Onglets et breadcrumbs
- **Responsive** : Adaptation mobile et desktop
- **Accessibilité** : Composants Material-UI accessibles
### 🚀 Déploiement et CI
- **Scripts npm** : Build, test, lint, format
- **Variables d'environnement** : Configuration flexible des APIs
- **Git workflow** : Branches dev, staging, release
- **Versioning** : Tag v0.1.0 et CHANGELOG
### 🐛 Corrections et améliorations
- **Erreur d'hydratation HTML** : Structure HTML valide
- **Gestion d'erreur 404/405** : Fallback pour endpoints non supportés
- **ERR_CONNECTION_REFUSED** : Mode démo automatique
- **Console propre** : Suppression des erreurs visibles
### 📦 Dépendances principales
- `react@^18.3.1` - Framework UI
- `typescript@^5.6.3` - Typage statique
- `vite@^7.1.5` - Build tool
- `@mui/material@^6.1.6` - Composants UI
- `@reduxjs/toolkit@^2.3.0` - Gestion d'état
- `react-router-dom@^6.28.0` - Routing
- `axios@^1.7.7` - Client HTTP
- `vitest@^2.1.8` - Framework de test

29
Dockerfile Normal file
View File

@ -0,0 +1,29 @@
# syntax=docker/dockerfile:1.7-labs
# ---- Build stage ----
FROM node:20.19-alpine AS build
WORKDIR /app
# Install dependencies
COPY package*.json ./
RUN npm ci --ignore-scripts
# Copy sources
COPY . .
# Ensure correct Node version for build via prebuild script
RUN npm run build
# ---- Runtime stage ----
FROM nginx:alpine AS runtime
# Copy SPA build
COPY --from=build /app/dist /usr/share/nginx/html
# Nginx config for SPA routing and caching
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
HEALTHCHECK CMD wget -qO- http://localhost/ || exit 1
CMD ["nginx", "-g", "daemon off;"]

309
README.md
View File

@ -1,75 +1,258 @@
# React + TypeScript + Vite
# 4NK IA Front Notarial
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Application front-end pour l'analyse intelligente de documents notariaux avec IA.
Currently, two official plugins are available:
## 🎯 Fonctionnalités
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses
[Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses
[SWC](https://swc.rs/) for Fast Refresh
### 📄 Gestion de documents
## Expanding the ESLint configuration
- **Upload multiple** : Glisser-déposer de documents (PDF, images)
- **Prévisualisation** : Affichage des documents uploadés
- **Types supportés** : PDF, PNG, JPG, JPEG
If you are developing a production application, we recommend updating the configuration to enable type-aware
lint rules:
### 🔍 Extraction et analyse
```js
export default tseslint.config([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
- **OCR automatique** : Extraction du texte des documents
- **Détection de type** : Identification automatique du type de document
- **Objets standardisés** : Extraction d'identités, adresses, biens, contrats
- **Détection de langue** : Identification automatique de la langue
// Remove tseslint.configs.recommended and replace with this
...tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
...tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
...tseslint.configs.stylisticTypeChecked,
### 📊 Analyse intelligente
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
- **Score de vraisemblance** : Évaluation de la crédibilité du document
- **Recommandations** : Suggestions d'actions à effectuer
- **Synthèse** : Résumé automatique du document
### 🌐 Données contextuelles
- **Cadastre** : Vérification des références cadastrales
- **Géorisques** : Analyse des risques géologiques
- **BODACC** : Vérification des procédures en cours
- **Infogreffe** : Contrôle des entreprises
### 🤖 Conseil IA
- **Analyse LLM** : Évaluation intelligente du document
- **Détection de risques** : Identification des éléments suspects
- **Prochaines étapes** : Recommandations d'actions
## 🚀 Technologies
- **Frontend** : React 19 + TypeScript
- **Build** : Vite 7
- **UI** : Material-UI (MUI) v7
- **State** : Redux Toolkit + React Redux
- **Routing** : React Router v7
- **HTTP** : Axios
- **Tests** : Vitest + Testing Library
- **Linting** : ESLint + Prettier + markdownlint
## 📦 Installation
### Prérequis
- Node.js >= 20.19.0 (LTS). Recommandé: 22.12+
- npm >= 10.0.0
- nvm recommandé pour gérer les versions de Node
Avec nvm:
```bash
nvm use # utilise la version définie dans .nvmrc
# ou, si non installée
nvm install && nvm use
```
You can also install
[eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and
[eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom)
for React-specific lint rules:
### Installation des dépendances
```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default tseslint.config([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```bash
npm ci
```
### Configuration des environnements
Créer un fichier `.env` :
```env
VITE_API_URL=http://localhost:8000
VITE_CADASTRE_API_URL=https://api.cadastre.gouv.fr
VITE_GEORISQUES_API_URL=https://www.georisques.gouv.fr/api
VITE_GEOFONCIER_API_URL=https://api.geofoncier.fr
VITE_BODACC_API_URL=https://api.bodacc.fr
VITE_INFOGREFFE_API_URL=https://api.infogreffe.fr
```
## 🛠️ Scripts disponibles
```bash
# Développement
npm run dev # Serveur de développement
npm run build # Build de production
npm run preview # Prévisualisation du build
# Qualité de code
npm run lint # Vérification ESLint
npm run format # Vérification Prettier
npm run format:fix # Formatage automatique
npm run mdlint # Vérification Markdown
# Tests
npm run test # Tests unitaires avec couverture
npm run test:ui # Tests avec interface
# Docker
./scripts/docker-build.sh # Construire l'image locale
IMAGE_TAG=0.1.3 ./scripts/docker-build.sh # Construire avec tag explicite
IMAGE_TAG=0.1.3 ./scripts/docker-push.sh # Pousser vers git.4nkweb.com
```
## 🏗️ Architecture
### Structure du projet
```text
src/
├── components/ # Composants réutilisables
│ ├── Layout.tsx # Layout principal
│ └── NavigationTabs.tsx
├── views/ # Vues de l'application
│ ├── UploadView.tsx # Upload de documents
│ ├── ExtractionView.tsx # Extraction de données
│ ├── AnalyseView.tsx # Analyse des documents
│ ├── ContexteView.tsx # Données contextuelles
│ └── ConseilView.tsx # Conseil IA
├── store/ # Gestion d'état Redux
│ ├── index.ts # Configuration du store
│ ├── appSlice.ts # État global
│ └── documentSlice.ts # État des documents
├── services/ # Services API
│ └── api.ts # Client API et endpoints
├── types/ # Types TypeScript
│ └── index.ts # Interfaces et types
└── main.tsx # Point d'entrée
```
### Gestion d'état
- **Redux Toolkit** : Gestion centralisée de l'état
- **Async Thunks** : Actions asynchrones pour les appels API
- **Slices** : Organisation modulaire du state
### Services API
- **Client Axios** : Configuration centralisée
- **Intercepteurs** : Gestion d'erreur automatique
- **Mode démo** : Fallback automatique en cas d'erreur
## 🎨 Interface utilisateur
### Design
- **Material-UI** : Composants modernes et accessibles
- **Thème cohérent** : Palette de couleurs professionnelle
- **Responsive** : Adaptation mobile et desktop
- **Fond blanc** : Interface claire et professionnelle
### Navigation
- **Onglets** : Navigation intuitive entre les vues
- **Breadcrumbs** : Indication de la position actuelle
- **Actions contextuelles** : Boutons d'action selon la vue
## 🔧 Intégration backend
Toutes les fonctionnalités (upload, extraction, analyse, données contextuelles, conseil LLM) passent par le backend `4NK_IA_back`.
Les APIs externes (Cadastre, Géorisques, Géofoncier, BODACC, Infogreffe) sont appelées côté backend uniquement.
## 🧪 Tests
### Configuration
- **Vitest** : Framework de test rapide
- **Testing Library** : Tests d'intégration React
- **JSDOM** : Environnement DOM simulé
- **Coverage** : Couverture de code avec V8
### Exécution
```bash
npm run test # Tests en mode watch
npm run test:ui # Interface graphique
npm run test:coverage # Rapport de couverture
```
## 📚 Documentation
- **docs/** : Documentation technique détaillée
- **CHANGELOG.md** : Historique des versions
- **CONTRIBUTING.md** : Guide de contribution
- **CODE_OF_CONDUCT.md** : Code de conduite
## 🚀 Déploiement
### Build de production
```bash
npm run build
```
### Exécution via Docker
```bash
# Construire l'image
./scripts/docker-build.sh
# Lancer localement
docker run --rm -p 8080:80 git.4nkweb.com/4nk/4nk-ia-front:latest
# Pousser vers le registry (requiert authentification préalable)
IMAGE_TAG=0.1.3 ./scripts/docker-push.sh
```
### Variables d'environnement
Configurer les URLs des APIs externes selon l'environnement :
- **Développement** : `http://localhost:8000`
- **Staging** : URL de l'environnement de test
- **Production** : URL de l'API de production
## 🤝 Contribution
1. Fork le projet
2. Créer une branche feature (`git checkout -b feature/nouvelle-fonctionnalite`)
3. Commit les changements (`git commit -m 'Ajouter nouvelle fonctionnalité'`)
4. Push vers la branche (`git push origin feature/nouvelle-fonctionnalite`)
5. Ouvrir une Pull Request
### Standards de code
- **ESLint** : Règles strictes activées
- **Prettier** : Formatage automatique
- **TypeScript** : Typage strict
- **Tests** : Couverture minimale requise
## 📄 Licence
Ce projet est sous licence MIT. Voir le fichier [LICENSE](LICENSE) pour plus de détails.
## 🔗 Liens utiles
- [Documentation Material-UI](https://mui.com/)
- [Redux Toolkit](https://redux-toolkit.js.org/)
- [React Router](https://reactrouter.com/)
- [Vite](https://vitejs.dev/)
- [Vitest](https://vitest.dev/)
## 📞 Support
Pour toute question ou problème :
- Ouvrir une [issue](https://git.4nkweb.com/4nk/4NK_IA_front/issues)
- Consulter la [documentation](docs/)
- Contacter l'équipe de développement
---
**Version actuelle** : 0.1.3
**Dernière mise à jour** : Septembre 2025

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -25,28 +25,28 @@
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/36</span>
<span class='fraction'>0/1611</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="strong">5.55% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/2</span>
<span class='fraction'>1/18</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="strong">5.55% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/2</span>
<span class='fraction'>1/18</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/36</span>
<span class='fraction'>0/1611</span>
</div>
@ -79,33 +79,138 @@
</tr>
</thead>
<tbody><tr>
<td class="file low" data-value="App.tsx"><a href="App.tsx.html">App.tsx</a></td>
<td class="file low" data-value="scripts"><a href="scripts/index.html">scripts</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="27" class="abs low">0/27</td>
<td data-value="57" class="abs low">0/57</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="27" class="abs low">0/27</td>
<td data-value="57" class="abs low">0/57</td>
</tr>
<tr>
<td class="file low" data-value="main.tsx"><a href="main.tsx.html">main.tsx</a></td>
<td class="file low" data-value="src"><a href="src/index.html">src</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="9" class="abs low">0/9</td>
<td data-value="24" class="abs low">0/24</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="2" class="abs low">0/2</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="2" class="abs low">0/2</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="24" class="abs low">0/24</td>
</tr>
<tr>
<td class="file low" data-value="src/components"><a href="src/components/index.html">src/components</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="227" class="abs low">0/227</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="3" class="abs low">0/3</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="3" class="abs low">0/3</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="227" class="abs low">0/227</td>
</tr>
<tr>
<td class="file low" data-value="src/router"><a href="src/router/index.html">src/router</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="23" class="abs low">0/23</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="9" class="abs low">0/9</td>
<td data-value="23" class="abs low">0/23</td>
</tr>
<tr>
<td class="file low" data-value="src/services"><a href="src/services/index.html">src/services</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="124" class="abs low">0/124</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="124" class="abs low">0/124</td>
</tr>
<tr>
<td class="file low" data-value="src/store"><a href="src/store/index.html">src/store</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="114" class="abs low">0/114</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="3" class="abs low">0/3</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="3" class="abs low">0/3</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="114" class="abs low">0/114</td>
</tr>
<tr>
<td class="file low" data-value="src/theme"><a href="src/theme/index.html">src/theme</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="64" class="abs low">0/64</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="64" class="abs low">0/64</td>
</tr>
<tr>
<td class="file empty" data-value="src/types"><a href="src/types/index.html">src/types</a></td>
<td data-value="0" class="pic empty">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct empty">0%</td>
<td data-value="0" class="abs empty">0/0</td>
<td data-value="0" class="pct empty">0%</td>
<td data-value="1" class="abs empty">1/1</td>
<td data-value="0" class="pct empty">0%</td>
<td data-value="1" class="abs empty">1/1</td>
<td data-value="0" class="pct empty">0%</td>
<td data-value="0" class="abs empty">0/0</td>
</tr>
<tr>
<td class="file low" data-value="src/views"><a href="src/views/index.html">src/views</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="978" class="abs low">0/978</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="5" class="abs low">0/5</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="5" class="abs low">0/5</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="978" class="abs low">0/978</td>
</tr>
</tbody>
@ -116,7 +221,7 @@
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-10T15:15:07.789Z
at 2025-09-11T12:57:49.285Z
</div>
<script src="prettify.js"></script>
<script>

116
coverage/scripts/index.html Normal file
View File

@ -0,0 +1,116 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for scripts</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../prettify.css" />
<link rel="stylesheet" href="../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../index.html">All files</a> scripts</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/57</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/57</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<div class="pad1">
<table class="coverage-summary">
<thead>
<tr>
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
</tr>
</thead>
<tbody><tr>
<td class="file low" data-value="simple-server.js"><a href="simple-server.js.html">simple-server.js</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="57" class="abs low">0/57</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="57" class="abs low">0/57</td>
</tr>
</tbody>
</table>
</div>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../sorter.js"></script>
<script src="../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,283 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for scripts/simple-server.js</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../prettify.css" />
<link rel="stylesheet" href="../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../index.html">All files</a> / <a href="index.html">scripts</a> simple-server.js</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/57</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/57</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
<a name='L2'></a><a href='#L2'>2</a>
<a name='L3'></a><a href='#L3'>3</a>
<a name='L4'></a><a href='#L4'>4</a>
<a name='L5'></a><a href='#L5'>5</a>
<a name='L6'></a><a href='#L6'>6</a>
<a name='L7'></a><a href='#L7'>7</a>
<a name='L8'></a><a href='#L8'>8</a>
<a name='L9'></a><a href='#L9'>9</a>
<a name='L10'></a><a href='#L10'>10</a>
<a name='L11'></a><a href='#L11'>11</a>
<a name='L12'></a><a href='#L12'>12</a>
<a name='L13'></a><a href='#L13'>13</a>
<a name='L14'></a><a href='#L14'>14</a>
<a name='L15'></a><a href='#L15'>15</a>
<a name='L16'></a><a href='#L16'>16</a>
<a name='L17'></a><a href='#L17'>17</a>
<a name='L18'></a><a href='#L18'>18</a>
<a name='L19'></a><a href='#L19'>19</a>
<a name='L20'></a><a href='#L20'>20</a>
<a name='L21'></a><a href='#L21'>21</a>
<a name='L22'></a><a href='#L22'>22</a>
<a name='L23'></a><a href='#L23'>23</a>
<a name='L24'></a><a href='#L24'>24</a>
<a name='L25'></a><a href='#L25'>25</a>
<a name='L26'></a><a href='#L26'>26</a>
<a name='L27'></a><a href='#L27'>27</a>
<a name='L28'></a><a href='#L28'>28</a>
<a name='L29'></a><a href='#L29'>29</a>
<a name='L30'></a><a href='#L30'>30</a>
<a name='L31'></a><a href='#L31'>31</a>
<a name='L32'></a><a href='#L32'>32</a>
<a name='L33'></a><a href='#L33'>33</a>
<a name='L34'></a><a href='#L34'>34</a>
<a name='L35'></a><a href='#L35'>35</a>
<a name='L36'></a><a href='#L36'>36</a>
<a name='L37'></a><a href='#L37'>37</a>
<a name='L38'></a><a href='#L38'>38</a>
<a name='L39'></a><a href='#L39'>39</a>
<a name='L40'></a><a href='#L40'>40</a>
<a name='L41'></a><a href='#L41'>41</a>
<a name='L42'></a><a href='#L42'>42</a>
<a name='L43'></a><a href='#L43'>43</a>
<a name='L44'></a><a href='#L44'>44</a>
<a name='L45'></a><a href='#L45'>45</a>
<a name='L46'></a><a href='#L46'>46</a>
<a name='L47'></a><a href='#L47'>47</a>
<a name='L48'></a><a href='#L48'>48</a>
<a name='L49'></a><a href='#L49'>49</a>
<a name='L50'></a><a href='#L50'>50</a>
<a name='L51'></a><a href='#L51'>51</a>
<a name='L52'></a><a href='#L52'>52</a>
<a name='L53'></a><a href='#L53'>53</a>
<a name='L54'></a><a href='#L54'>54</a>
<a name='L55'></a><a href='#L55'>55</a>
<a name='L56'></a><a href='#L56'>56</a>
<a name='L57'></a><a href='#L57'>57</a>
<a name='L58'></a><a href='#L58'>58</a>
<a name='L59'></a><a href='#L59'>59</a>
<a name='L60'></a><a href='#L60'>60</a>
<a name='L61'></a><a href='#L61'>61</a>
<a name='L62'></a><a href='#L62'>62</a>
<a name='L63'></a><a href='#L63'>63</a>
<a name='L64'></a><a href='#L64'>64</a>
<a name='L65'></a><a href='#L65'>65</a>
<a name='L66'></a><a href='#L66'>66</a>
<a name='L67'></a><a href='#L67'>67</a></td><td class="line-coverage quiet"><span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js"><span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" ><span class="branch-0 cbranch-no" title="branch not covered" >#!/usr/bin/env node</span></span></span>
&nbsp;
<span class="cstat-no" title="statement not covered" >import http from 'http';</span>
<span class="cstat-no" title="statement not covered" >import fs from 'fs';</span>
<span class="cstat-no" title="statement not covered" >import path from 'path';</span>
<span class="cstat-no" title="statement not covered" >import { fileURLToPath } from 'url';</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >const __filename = fileURLToPath(import.meta.url);</span>
<span class="cstat-no" title="statement not covered" >const __dirname = path.dirname(__filename);</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >const PORT = 5173;</span>
<span class="cstat-no" title="statement not covered" >const HOST = '0.0.0.0';</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >// Types MIME</span>
<span class="cstat-no" title="statement not covered" >const mimeTypes = {</span>
<span class="cstat-no" title="statement not covered" > '.html': 'text/html',</span>
<span class="cstat-no" title="statement not covered" > '.js': 'text/javascript',</span>
<span class="cstat-no" title="statement not covered" > '.css': 'text/css',</span>
<span class="cstat-no" title="statement not covered" > '.json': 'application/json',</span>
<span class="cstat-no" title="statement not covered" > '.png': 'image/png',</span>
<span class="cstat-no" title="statement not covered" > '.jpg': 'image/jpg',</span>
<span class="cstat-no" title="statement not covered" > '.gif': 'image/gif',</span>
<span class="cstat-no" title="statement not covered" > '.svg': 'image/svg+xml',</span>
<span class="cstat-no" title="statement not covered" > '.ico': 'image/x-icon'</span>
<span class="cstat-no" title="statement not covered" >};</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >const server = http.createServer((req, res) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > let filePath = '.' + req.url;</span>
<span class="cstat-no" title="statement not covered" > if (filePath === './') {</span>
<span class="cstat-no" title="statement not covered" > filePath = './index.html';</span>
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > const extname = String(path.extname(filePath)).toLowerCase();</span>
<span class="cstat-no" title="statement not covered" > const mimeType = mimeTypes[extname] || 'application/octet-stream';</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > fs.readFile(filePath, (error, content) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > if (error) {</span>
<span class="cstat-no" title="statement not covered" > if (error.code === 'ENOENT') {</span>
<span class="cstat-no" title="statement not covered" > // Fichier non trouvé, servir index.html pour SPA</span>
<span class="cstat-no" title="statement not covered" > fs.readFile('./index.html', (error, content) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > if (error) {</span>
<span class="cstat-no" title="statement not covered" > res.writeHead(404);</span>
<span class="cstat-no" title="statement not covered" > res.end('File not found');</span>
<span class="cstat-no" title="statement not covered" > } else {</span>
<span class="cstat-no" title="statement not covered" > res.writeHead(200, { 'Content-Type': 'text/html' });</span>
<span class="cstat-no" title="statement not covered" > res.end(content, 'utf-8');</span>
<span class="cstat-no" title="statement not covered" > }</span>
<span class="cstat-no" title="statement not covered" > });</span>
<span class="cstat-no" title="statement not covered" > } else {</span>
<span class="cstat-no" title="statement not covered" > res.writeHead(500);</span>
<span class="cstat-no" title="statement not covered" > res.end('Server error: ' + error.code);</span>
<span class="cstat-no" title="statement not covered" > }</span>
<span class="cstat-no" title="statement not covered" > } else {</span>
<span class="cstat-no" title="statement not covered" > res.writeHead(200, { 'Content-Type': mimeType });</span>
<span class="cstat-no" title="statement not covered" > res.end(content, 'utf-8');</span>
<span class="cstat-no" title="statement not covered" > }</span>
<span class="cstat-no" title="statement not covered" > });</span>
<span class="cstat-no" title="statement not covered" >});</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >server.listen(PORT, HOST, () =&gt; {</span>
<span class="cstat-no" title="statement not covered" > console.log(`🚀 Serveur 4NK_IA_front démarré sur http://${HOST}:${PORT}`);</span>
<span class="cstat-no" title="statement not covered" > console.log(`📁 Servant les fichiers depuis: ${process.cwd()}`);</span>
<span class="cstat-no" title="statement not covered" > console.log(`💡 Appuyez sur Ctrl+C pour arrêter`);</span>
<span class="cstat-no" title="statement not covered" >});</span>
&nbsp;</pre></td></tr></table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../sorter.js"></script>
<script src="../block-navigation.js"></script>
</body>
</html>

103
coverage/src/App.tsx.html Normal file
View File

@ -0,0 +1,103 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/App.tsx</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../prettify.css" />
<link rel="stylesheet" href="../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../index.html">All files</a> / <a href="index.html">src</a> App.tsx</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/5</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/5</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
<a name='L2'></a><a href='#L2'>2</a>
<a name='L3'></a><a href='#L3'>3</a>
<a name='L4'></a><a href='#L4'>4</a>
<a name='L5'></a><a href='#L5'>5</a>
<a name='L6'></a><a href='#L6'>6</a>
<a name='L7'></a><a href='#L7'>7</a></td><td class="line-coverage quiet"><span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js"><span class="cstat-no" title="statement not covered" >import './App.css'<span class="fstat-no" title="function not covered" ><span class="branch-0 cbranch-no" title="branch not covered" ></span></span></span>
<span class="cstat-no" title="statement not covered" >import { AppRouter } from './router'</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >export default function App() {</span>
<span class="cstat-no" title="statement not covered" > return &lt;AppRouter /&gt;</span>
<span class="cstat-no" title="statement not covered" >}</span>
&nbsp;</pre></td></tr></table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../sorter.js"></script>
<script src="../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,772 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/components/FilePreview.tsx</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> / <a href="index.html">src/components</a> FilePreview.tsx</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/171</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/171</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
<a name='L2'></a><a href='#L2'>2</a>
<a name='L3'></a><a href='#L3'>3</a>
<a name='L4'></a><a href='#L4'>4</a>
<a name='L5'></a><a href='#L5'>5</a>
<a name='L6'></a><a href='#L6'>6</a>
<a name='L7'></a><a href='#L7'>7</a>
<a name='L8'></a><a href='#L8'>8</a>
<a name='L9'></a><a href='#L9'>9</a>
<a name='L10'></a><a href='#L10'>10</a>
<a name='L11'></a><a href='#L11'>11</a>
<a name='L12'></a><a href='#L12'>12</a>
<a name='L13'></a><a href='#L13'>13</a>
<a name='L14'></a><a href='#L14'>14</a>
<a name='L15'></a><a href='#L15'>15</a>
<a name='L16'></a><a href='#L16'>16</a>
<a name='L17'></a><a href='#L17'>17</a>
<a name='L18'></a><a href='#L18'>18</a>
<a name='L19'></a><a href='#L19'>19</a>
<a name='L20'></a><a href='#L20'>20</a>
<a name='L21'></a><a href='#L21'>21</a>
<a name='L22'></a><a href='#L22'>22</a>
<a name='L23'></a><a href='#L23'>23</a>
<a name='L24'></a><a href='#L24'>24</a>
<a name='L25'></a><a href='#L25'>25</a>
<a name='L26'></a><a href='#L26'>26</a>
<a name='L27'></a><a href='#L27'>27</a>
<a name='L28'></a><a href='#L28'>28</a>
<a name='L29'></a><a href='#L29'>29</a>
<a name='L30'></a><a href='#L30'>30</a>
<a name='L31'></a><a href='#L31'>31</a>
<a name='L32'></a><a href='#L32'>32</a>
<a name='L33'></a><a href='#L33'>33</a>
<a name='L34'></a><a href='#L34'>34</a>
<a name='L35'></a><a href='#L35'>35</a>
<a name='L36'></a><a href='#L36'>36</a>
<a name='L37'></a><a href='#L37'>37</a>
<a name='L38'></a><a href='#L38'>38</a>
<a name='L39'></a><a href='#L39'>39</a>
<a name='L40'></a><a href='#L40'>40</a>
<a name='L41'></a><a href='#L41'>41</a>
<a name='L42'></a><a href='#L42'>42</a>
<a name='L43'></a><a href='#L43'>43</a>
<a name='L44'></a><a href='#L44'>44</a>
<a name='L45'></a><a href='#L45'>45</a>
<a name='L46'></a><a href='#L46'>46</a>
<a name='L47'></a><a href='#L47'>47</a>
<a name='L48'></a><a href='#L48'>48</a>
<a name='L49'></a><a href='#L49'>49</a>
<a name='L50'></a><a href='#L50'>50</a>
<a name='L51'></a><a href='#L51'>51</a>
<a name='L52'></a><a href='#L52'>52</a>
<a name='L53'></a><a href='#L53'>53</a>
<a name='L54'></a><a href='#L54'>54</a>
<a name='L55'></a><a href='#L55'>55</a>
<a name='L56'></a><a href='#L56'>56</a>
<a name='L57'></a><a href='#L57'>57</a>
<a name='L58'></a><a href='#L58'>58</a>
<a name='L59'></a><a href='#L59'>59</a>
<a name='L60'></a><a href='#L60'>60</a>
<a name='L61'></a><a href='#L61'>61</a>
<a name='L62'></a><a href='#L62'>62</a>
<a name='L63'></a><a href='#L63'>63</a>
<a name='L64'></a><a href='#L64'>64</a>
<a name='L65'></a><a href='#L65'>65</a>
<a name='L66'></a><a href='#L66'>66</a>
<a name='L67'></a><a href='#L67'>67</a>
<a name='L68'></a><a href='#L68'>68</a>
<a name='L69'></a><a href='#L69'>69</a>
<a name='L70'></a><a href='#L70'>70</a>
<a name='L71'></a><a href='#L71'>71</a>
<a name='L72'></a><a href='#L72'>72</a>
<a name='L73'></a><a href='#L73'>73</a>
<a name='L74'></a><a href='#L74'>74</a>
<a name='L75'></a><a href='#L75'>75</a>
<a name='L76'></a><a href='#L76'>76</a>
<a name='L77'></a><a href='#L77'>77</a>
<a name='L78'></a><a href='#L78'>78</a>
<a name='L79'></a><a href='#L79'>79</a>
<a name='L80'></a><a href='#L80'>80</a>
<a name='L81'></a><a href='#L81'>81</a>
<a name='L82'></a><a href='#L82'>82</a>
<a name='L83'></a><a href='#L83'>83</a>
<a name='L84'></a><a href='#L84'>84</a>
<a name='L85'></a><a href='#L85'>85</a>
<a name='L86'></a><a href='#L86'>86</a>
<a name='L87'></a><a href='#L87'>87</a>
<a name='L88'></a><a href='#L88'>88</a>
<a name='L89'></a><a href='#L89'>89</a>
<a name='L90'></a><a href='#L90'>90</a>
<a name='L91'></a><a href='#L91'>91</a>
<a name='L92'></a><a href='#L92'>92</a>
<a name='L93'></a><a href='#L93'>93</a>
<a name='L94'></a><a href='#L94'>94</a>
<a name='L95'></a><a href='#L95'>95</a>
<a name='L96'></a><a href='#L96'>96</a>
<a name='L97'></a><a href='#L97'>97</a>
<a name='L98'></a><a href='#L98'>98</a>
<a name='L99'></a><a href='#L99'>99</a>
<a name='L100'></a><a href='#L100'>100</a>
<a name='L101'></a><a href='#L101'>101</a>
<a name='L102'></a><a href='#L102'>102</a>
<a name='L103'></a><a href='#L103'>103</a>
<a name='L104'></a><a href='#L104'>104</a>
<a name='L105'></a><a href='#L105'>105</a>
<a name='L106'></a><a href='#L106'>106</a>
<a name='L107'></a><a href='#L107'>107</a>
<a name='L108'></a><a href='#L108'>108</a>
<a name='L109'></a><a href='#L109'>109</a>
<a name='L110'></a><a href='#L110'>110</a>
<a name='L111'></a><a href='#L111'>111</a>
<a name='L112'></a><a href='#L112'>112</a>
<a name='L113'></a><a href='#L113'>113</a>
<a name='L114'></a><a href='#L114'>114</a>
<a name='L115'></a><a href='#L115'>115</a>
<a name='L116'></a><a href='#L116'>116</a>
<a name='L117'></a><a href='#L117'>117</a>
<a name='L118'></a><a href='#L118'>118</a>
<a name='L119'></a><a href='#L119'>119</a>
<a name='L120'></a><a href='#L120'>120</a>
<a name='L121'></a><a href='#L121'>121</a>
<a name='L122'></a><a href='#L122'>122</a>
<a name='L123'></a><a href='#L123'>123</a>
<a name='L124'></a><a href='#L124'>124</a>
<a name='L125'></a><a href='#L125'>125</a>
<a name='L126'></a><a href='#L126'>126</a>
<a name='L127'></a><a href='#L127'>127</a>
<a name='L128'></a><a href='#L128'>128</a>
<a name='L129'></a><a href='#L129'>129</a>
<a name='L130'></a><a href='#L130'>130</a>
<a name='L131'></a><a href='#L131'>131</a>
<a name='L132'></a><a href='#L132'>132</a>
<a name='L133'></a><a href='#L133'>133</a>
<a name='L134'></a><a href='#L134'>134</a>
<a name='L135'></a><a href='#L135'>135</a>
<a name='L136'></a><a href='#L136'>136</a>
<a name='L137'></a><a href='#L137'>137</a>
<a name='L138'></a><a href='#L138'>138</a>
<a name='L139'></a><a href='#L139'>139</a>
<a name='L140'></a><a href='#L140'>140</a>
<a name='L141'></a><a href='#L141'>141</a>
<a name='L142'></a><a href='#L142'>142</a>
<a name='L143'></a><a href='#L143'>143</a>
<a name='L144'></a><a href='#L144'>144</a>
<a name='L145'></a><a href='#L145'>145</a>
<a name='L146'></a><a href='#L146'>146</a>
<a name='L147'></a><a href='#L147'>147</a>
<a name='L148'></a><a href='#L148'>148</a>
<a name='L149'></a><a href='#L149'>149</a>
<a name='L150'></a><a href='#L150'>150</a>
<a name='L151'></a><a href='#L151'>151</a>
<a name='L152'></a><a href='#L152'>152</a>
<a name='L153'></a><a href='#L153'>153</a>
<a name='L154'></a><a href='#L154'>154</a>
<a name='L155'></a><a href='#L155'>155</a>
<a name='L156'></a><a href='#L156'>156</a>
<a name='L157'></a><a href='#L157'>157</a>
<a name='L158'></a><a href='#L158'>158</a>
<a name='L159'></a><a href='#L159'>159</a>
<a name='L160'></a><a href='#L160'>160</a>
<a name='L161'></a><a href='#L161'>161</a>
<a name='L162'></a><a href='#L162'>162</a>
<a name='L163'></a><a href='#L163'>163</a>
<a name='L164'></a><a href='#L164'>164</a>
<a name='L165'></a><a href='#L165'>165</a>
<a name='L166'></a><a href='#L166'>166</a>
<a name='L167'></a><a href='#L167'>167</a>
<a name='L168'></a><a href='#L168'>168</a>
<a name='L169'></a><a href='#L169'>169</a>
<a name='L170'></a><a href='#L170'>170</a>
<a name='L171'></a><a href='#L171'>171</a>
<a name='L172'></a><a href='#L172'>172</a>
<a name='L173'></a><a href='#L173'>173</a>
<a name='L174'></a><a href='#L174'>174</a>
<a name='L175'></a><a href='#L175'>175</a>
<a name='L176'></a><a href='#L176'>176</a>
<a name='L177'></a><a href='#L177'>177</a>
<a name='L178'></a><a href='#L178'>178</a>
<a name='L179'></a><a href='#L179'>179</a>
<a name='L180'></a><a href='#L180'>180</a>
<a name='L181'></a><a href='#L181'>181</a>
<a name='L182'></a><a href='#L182'>182</a>
<a name='L183'></a><a href='#L183'>183</a>
<a name='L184'></a><a href='#L184'>184</a>
<a name='L185'></a><a href='#L185'>185</a>
<a name='L186'></a><a href='#L186'>186</a>
<a name='L187'></a><a href='#L187'>187</a>
<a name='L188'></a><a href='#L188'>188</a>
<a name='L189'></a><a href='#L189'>189</a>
<a name='L190'></a><a href='#L190'>190</a>
<a name='L191'></a><a href='#L191'>191</a>
<a name='L192'></a><a href='#L192'>192</a>
<a name='L193'></a><a href='#L193'>193</a>
<a name='L194'></a><a href='#L194'>194</a>
<a name='L195'></a><a href='#L195'>195</a>
<a name='L196'></a><a href='#L196'>196</a>
<a name='L197'></a><a href='#L197'>197</a>
<a name='L198'></a><a href='#L198'>198</a>
<a name='L199'></a><a href='#L199'>199</a>
<a name='L200'></a><a href='#L200'>200</a>
<a name='L201'></a><a href='#L201'>201</a>
<a name='L202'></a><a href='#L202'>202</a>
<a name='L203'></a><a href='#L203'>203</a>
<a name='L204'></a><a href='#L204'>204</a>
<a name='L205'></a><a href='#L205'>205</a>
<a name='L206'></a><a href='#L206'>206</a>
<a name='L207'></a><a href='#L207'>207</a>
<a name='L208'></a><a href='#L208'>208</a>
<a name='L209'></a><a href='#L209'>209</a>
<a name='L210'></a><a href='#L210'>210</a>
<a name='L211'></a><a href='#L211'>211</a>
<a name='L212'></a><a href='#L212'>212</a>
<a name='L213'></a><a href='#L213'>213</a>
<a name='L214'></a><a href='#L214'>214</a>
<a name='L215'></a><a href='#L215'>215</a>
<a name='L216'></a><a href='#L216'>216</a>
<a name='L217'></a><a href='#L217'>217</a>
<a name='L218'></a><a href='#L218'>218</a>
<a name='L219'></a><a href='#L219'>219</a>
<a name='L220'></a><a href='#L220'>220</a>
<a name='L221'></a><a href='#L221'>221</a>
<a name='L222'></a><a href='#L222'>222</a>
<a name='L223'></a><a href='#L223'>223</a>
<a name='L224'></a><a href='#L224'>224</a>
<a name='L225'></a><a href='#L225'>225</a>
<a name='L226'></a><a href='#L226'>226</a>
<a name='L227'></a><a href='#L227'>227</a>
<a name='L228'></a><a href='#L228'>228</a>
<a name='L229'></a><a href='#L229'>229</a>
<a name='L230'></a><a href='#L230'>230</a></td><td class="line-coverage quiet"><span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js"><span class="cstat-no" title="statement not covered" >import React, { useState, useEffect } from 'react'<span class="fstat-no" title="function not covered" ><span class="branch-0 cbranch-no" title="branch not covered" ></span></span></span>
<span class="cstat-no" title="statement not covered" >import {</span>
Box,
Typography,
Paper,
IconButton,
Button,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
CircularProgress,
Alert,
} from '@mui/material'
<span class="cstat-no" title="statement not covered" >import {</span>
PictureAsPdf,
Download,
Close,
ZoomIn,
ZoomOut,
NavigateBefore,
NavigateNext,
} from '@mui/icons-material'
import type { Document } from '../types'
&nbsp;
interface FilePreviewProps {
document: Document
onClose: () =&gt; void
}
&nbsp;
<span class="cstat-no" title="statement not covered" >export const FilePreview: React.FC&lt;FilePreviewProps&gt; = ({ document, onClose }) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > const [loading, setLoading] = useState(true)</span>
<span class="cstat-no" title="statement not covered" > const [error, setError] = useState&lt;string | null&gt;(null)</span>
<span class="cstat-no" title="statement not covered" > const [page, setPage] = useState(1)</span>
<span class="cstat-no" title="statement not covered" > const [scale, setScale] = useState(1.0)</span>
<span class="cstat-no" title="statement not covered" > const [numPages, setNumPages] = useState(0)</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > useEffect(() =&gt; {</span>
<span class="cstat-no" title="statement not covered" > setLoading(true)</span>
<span class="cstat-no" title="statement not covered" > setError(null)</span>
<span class="cstat-no" title="statement not covered" > setPage(1)</span>
<span class="cstat-no" title="statement not covered" > setScale(1.0)</span>
&nbsp;
// Simuler le chargement du PDF
<span class="cstat-no" title="statement not covered" > const timer = setTimeout(() =&gt; {</span>
<span class="cstat-no" title="statement not covered" > setNumPages(3) // Simuler 3 pages</span>
<span class="cstat-no" title="statement not covered" > setLoading(false)</span>
<span class="cstat-no" title="statement not covered" > }, 1000)</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > return () =&gt; clearTimeout(timer)</span>
<span class="cstat-no" title="statement not covered" > }, [document])</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > const handleDownload = () =&gt; {</span>
<span class="cstat-no" title="statement not covered" > if (document.previewUrl) {</span>
<span class="cstat-no" title="statement not covered" > const link = window.document.createElement('a')</span>
<span class="cstat-no" title="statement not covered" > link.href = document.previewUrl</span>
<span class="cstat-no" title="statement not covered" > link.download = document.name</span>
<span class="cstat-no" title="statement not covered" > link.click()</span>
<span class="cstat-no" title="statement not covered" > }</span>
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > const isPDF = document.mimeType.includes('pdf') || document.name.toLowerCase().endsWith('.pdf')</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > if (!isPDF) {</span>
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;Paper sx={{ p: 3, mt: 2 }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box display="flex" justifyContent="space-between" alignItems="center" mb={2}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6"&gt;{document.name}&lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;IconButton onClick={onClose} title="Fermer"&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Close /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/IconButton&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Alert severity="info"&gt;</span>
<span class="cstat-no" title="statement not covered" > Aperçu non disponible pour ce type de fichier ({document.functionalType || document.mimeType})</span>
<span class="cstat-no" title="statement not covered" > &lt;/Alert&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Paper&gt;</span>
)
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;Dialog open onClose={onClose} maxWidth="lg" fullWidth&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;DialogTitle&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box display="flex" justifyContent="space-between" alignItems="center"&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box display="flex" alignItems="center" gap={1}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;PictureAsPdf color="error" /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6"&gt;{document.name}&lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;IconButton onClick={onClose} title="Fermer"&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Close /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/IconButton&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/DialogTitle&gt;</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > &lt;DialogContent dividers&gt;</span>
<span class="cstat-no" title="statement not covered" > {loading &amp;&amp; (</span>
<span class="cstat-no" title="statement not covered" > &lt;Box display="flex" justifyContent="center" alignItems="center" minHeight="400px"&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CircularProgress /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="body2" sx={{ ml: 2 }}&gt;</span>
Chargement du PDF...
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
)}
&nbsp;
<span class="cstat-no" title="statement not covered" > {error &amp;&amp; (</span>
<span class="cstat-no" title="statement not covered" > &lt;Alert severity="error" sx={{ mb: 2 }}&gt;</span>
<span class="cstat-no" title="statement not covered" > {error}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Alert&gt;</span>
)}
&nbsp;
<span class="cstat-no" title="statement not covered" > {!loading &amp;&amp; !error &amp;&amp; (</span>
<span class="cstat-no" title="statement not covered" > &lt;Box&gt;</span>
{/* Contrôles de navigation */}
<span class="cstat-no" title="statement not covered" > &lt;Box display="flex" justifyContent="space-between" alignItems="center" mb={2}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box display="flex" alignItems="center" gap={1}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Button</span>
<span class="cstat-no" title="statement not covered" > variant="outlined"</span>
<span class="cstat-no" title="statement not covered" > size="small"</span>
<span class="cstat-no" title="statement not covered" > startIcon={&lt;NavigateBefore /&gt;}</span>
<span class="cstat-no" title="statement not covered" > onClick={() =&gt; setPage(prev =&gt; Math.max(prev - 1, 1))}</span>
<span class="cstat-no" title="statement not covered" > disabled={page &lt;= 1}</span>
<span class="cstat-no" title="statement not covered" > &gt;</span>
Précédent
<span class="cstat-no" title="statement not covered" > &lt;/Button&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="body2"&gt;</span>
<span class="cstat-no" title="statement not covered" > Page {page} sur {numPages}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Button</span>
<span class="cstat-no" title="statement not covered" > variant="outlined"</span>
<span class="cstat-no" title="statement not covered" > size="small"</span>
<span class="cstat-no" title="statement not covered" > endIcon={&lt;NavigateNext /&gt;}</span>
<span class="cstat-no" title="statement not covered" > onClick={() =&gt; setPage(prev =&gt; Math.min(prev + 1, numPages))}</span>
<span class="cstat-no" title="statement not covered" > disabled={page &gt;= numPages}</span>
<span class="cstat-no" title="statement not covered" > &gt;</span>
Suivant
<span class="cstat-no" title="statement not covered" > &lt;/Button&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > &lt;Box display="flex" alignItems="center" gap={1}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Button</span>
<span class="cstat-no" title="statement not covered" > variant="outlined"</span>
<span class="cstat-no" title="statement not covered" > size="small"</span>
<span class="cstat-no" title="statement not covered" > startIcon={&lt;ZoomOut /&gt;}</span>
<span class="cstat-no" title="statement not covered" > onClick={() =&gt; setScale(prev =&gt; Math.max(prev - 0.2, 0.5))}</span>
<span class="cstat-no" title="statement not covered" > &gt;</span>
Zoom -
<span class="cstat-no" title="statement not covered" > &lt;/Button&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="body2"&gt;</span>
<span class="cstat-no" title="statement not covered" > {Math.round(scale * 100)}%</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Button</span>
<span class="cstat-no" title="statement not covered" > variant="outlined"</span>
<span class="cstat-no" title="statement not covered" > size="small"</span>
<span class="cstat-no" title="statement not covered" > startIcon={&lt;ZoomIn /&gt;}</span>
<span class="cstat-no" title="statement not covered" > onClick={() =&gt; setScale(prev =&gt; Math.min(prev + 0.2, 2.0))}</span>
<span class="cstat-no" title="statement not covered" > &gt;</span>
Zoom +
<span class="cstat-no" title="statement not covered" > &lt;/Button&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
&nbsp;
{/* Aperçu PDF avec viewer intégré */}
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{</span>
<span class="cstat-no" title="statement not covered" > border: '1px solid',</span>
<span class="cstat-no" title="statement not covered" > borderColor: 'grey.300',</span>
<span class="cstat-no" title="statement not covered" > borderRadius: 1,</span>
<span class="cstat-no" title="statement not covered" > overflow: 'hidden',</span>
<span class="cstat-no" title="statement not covered" > maxHeight: '70vh',</span>
<span class="cstat-no" title="statement not covered" > display: 'flex',</span>
<span class="cstat-no" title="statement not covered" > justifyContent: 'center',</span>
<span class="cstat-no" title="statement not covered" > backgroundColor: 'grey.50'</span>
<span class="cstat-no" title="statement not covered" > }}&gt;</span>
<span class="cstat-no" title="statement not covered" > {document.previewUrl ? (</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ width: '100%', height: '600px' }}&gt;</span>
{/* Utiliser un viewer PDF intégré */}
<span class="cstat-no" title="statement not covered" > &lt;iframe</span>
<span class="cstat-no" title="statement not covered" > src={`${document.previewUrl}#toolbar=1&amp;navpanes=1&amp;scrollbar=1&amp;page=1&amp;view=FitH`}</span>
<span class="cstat-no" title="statement not covered" > width="100%"</span>
<span class="cstat-no" title="statement not covered" > height="100%"</span>
<span class="cstat-no" title="statement not covered" > style={{</span>
<span class="cstat-no" title="statement not covered" > border: 'none',</span>
<span class="cstat-no" title="statement not covered" > transform: `scale(${scale})`,</span>
<span class="cstat-no" title="statement not covered" > transformOrigin: 'top left',</span>
<span class="cstat-no" title="statement not covered" > width: `${100 / scale}%`,</span>
<span class="cstat-no" title="statement not covered" > height: `${600 / scale}px`</span>
<span class="cstat-no" title="statement not covered" > }}</span>
<span class="cstat-no" title="statement not covered" > title={`Aperçu de ${document.name}`}</span>
<span class="cstat-no" title="statement not covered" > onLoad={() =&gt; setLoading(false)}</span>
<span class="cstat-no" title="statement not covered" > onError={() =&gt; {</span>
<span class="cstat-no" title="statement not covered" > setError('Erreur de chargement du PDF')</span>
<span class="cstat-no" title="statement not covered" > setLoading(false)</span>
<span class="cstat-no" title="statement not covered" > }}</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
) : (
<span class="cstat-no" title="statement not covered" > &lt;Box display="flex" justifyContent="center" alignItems="center" minHeight="400px"&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box textAlign="center"&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;PictureAsPdf sx={{ fontSize: 64, color: 'error.main', mb: 2 }} /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
Aperçu PDF
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="body2" color="text.secondary"&gt;</span>
<span class="cstat-no" title="statement not covered" > Le fichier PDF "{document.name}" a été uploadé avec succès.</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="body2" color="text.secondary"&gt;</span>
<span class="cstat-no" title="statement not covered" > Taille: {(document.size / 1024 / 1024).toFixed(2)} MB</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
)}
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
)}
<span class="cstat-no" title="statement not covered" > &lt;/DialogContent&gt;</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > &lt;DialogActions&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Button onClick={onClose}&gt;</span>
Fermer
<span class="cstat-no" title="statement not covered" > &lt;/Button&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Button</span>
<span class="cstat-no" title="statement not covered" > variant="contained"</span>
<span class="cstat-no" title="statement not covered" > startIcon={&lt;Download /&gt;}</span>
<span class="cstat-no" title="statement not covered" > onClick={handleDownload}</span>
<span class="cstat-no" title="statement not covered" > disabled={!document.previewUrl}</span>
<span class="cstat-no" title="statement not covered" > &gt;</span>
Télécharger
<span class="cstat-no" title="statement not covered" > &lt;/Button&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/DialogActions&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Dialog&gt;</span>
)
<span class="cstat-no" title="statement not covered" >}</span></pre></td></tr></table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -3,15 +3,15 @@
<html lang="en">
<head>
<title>Code coverage report for App.tsx</title>
<title>Code coverage report for src/components/Layout.tsx</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="prettify.css" />
<link rel="stylesheet" href="base.css" />
<link rel="shortcut icon" type="image/x-icon" href="favicon.png" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(sort-arrow-sprite.png);
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
@ -19,13 +19,13 @@
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="index.html">All files</a> App.tsx</h1>
<h1><a href="../../index.html">All files</a> / <a href="index.html">src/components</a> Layout.tsx</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/27</span>
<span class='fraction'>0/26</span>
</div>
@ -46,7 +46,7 @@
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/27</span>
<span class='fraction'>0/26</span>
</div>
@ -94,11 +94,21 @@
<a name='L29'></a><a href='#L29'>29</a>
<a name='L30'></a><a href='#L30'>30</a>
<a name='L31'></a><a href='#L31'>31</a>
<a name='L32'></a><a href='#L32'>32</a></td><td class="line-coverage quiet"><span class="cline-any cline-no">&nbsp;</span>
<a name='L32'></a><a href='#L32'>32</a>
<a name='L33'></a><a href='#L33'>33</a>
<a name='L34'></a><a href='#L34'>34</a>
<a name='L35'></a><a href='#L35'>35</a>
<a name='L36'></a><a href='#L36'>36</a>
<a name='L37'></a><a href='#L37'>37</a></td><td class="line-coverage quiet"><span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@ -112,12 +122,7 @@
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
@ -125,37 +130,47 @@
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js"><span class="cstat-no" title="statement not covered" >import { useState } from 'react'<span class="fstat-no" title="function not covered" ><span class="branch-0 cbranch-no" title="branch not covered" ></span></span></span>
<span class="cstat-no" title="statement not covered" >import reactLogo from './assets/react.svg'</span>
<span class="cstat-no" title="statement not covered" >import viteLogo from '/vite.svg'</span>
<span class="cstat-no" title="statement not covered" >import './App.css'</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js"><span class="cstat-no" title="statement not covered" >import React from 'react'<span class="fstat-no" title="function not covered" ><span class="branch-0 cbranch-no" title="branch not covered" ></span></span></span>
<span class="cstat-no" title="statement not covered" >import { AppBar, Toolbar, Typography, Container, Box } from '@mui/material'</span>
<span class="cstat-no" title="statement not covered" >import { useNavigate, useLocation } from 'react-router-dom'</span>
<span class="cstat-no" title="statement not covered" >import { NavigationTabs } from './NavigationTabs'</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >function App() {</span>
<span class="cstat-no" title="statement not covered" > const [count, setCount] = useState(0)</span>
interface LayoutProps {
children: React.ReactNode
}
&nbsp;
<span class="cstat-no" title="statement not covered" >export const Layout: React.FC&lt;LayoutProps&gt; = ({ children }) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > const navigate = useNavigate()</span>
<span class="cstat-no" title="statement not covered" > const location = useLocation()</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;div&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;a href="https://vite.dev" target="_blank"&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;img src={viteLogo} className="logo" alt="Vite logo" /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/a&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;a href="https://react.dev" target="_blank"&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;img src={reactLogo} className="logo react" alt="React logo" /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/a&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/div&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;h1&gt;Vite + React&lt;/h1&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;div className="card"&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;button onClick={() =&gt; setCount((count) =&gt; count + 1)}&gt;count is {count}&lt;/button&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;p&gt;</span>
<span class="cstat-no" title="statement not covered" > Edit &lt;code&gt;src/App.tsx&lt;/code&gt; and save to test HMR</span>
<span class="cstat-no" title="statement not covered" > &lt;/p&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/div&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;p className="read-the-docs"&gt;Click on the Vite and React logos to learn more&lt;/p&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ flexGrow: 1 }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;AppBar position="static"&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Toolbar&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography</span>
<span class="cstat-no" title="statement not covered" > variant="h6"</span>
<span class="cstat-no" title="statement not covered" > component="div"</span>
<span class="cstat-no" title="statement not covered" > sx={{ flexGrow: 1, cursor: 'pointer' }}</span>
<span class="cstat-no" title="statement not covered" > onClick={() =&gt; navigate('/')}</span>
<span class="cstat-no" title="statement not covered" > &gt;</span>
4NK IA - Front Notarial
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Toolbar&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/AppBar&gt;</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > &lt;NavigationTabs currentPath={location.pathname} /&gt;</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > &lt;Container maxWidth="xl" sx={{ mt: 3, mb: 3 }}&gt;</span>
<span class="cstat-no" title="statement not covered" > {children}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Container&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
)
<span class="cstat-no" title="statement not covered" >}</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >export default App</span>
&nbsp;</pre></td></tr></table></pre>
<div class='push'></div><!-- for sticky footer -->
@ -163,16 +178,16 @@
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-10T15:15:07.789Z
at 2025-09-11T12:57:49.285Z
</div>
<script src="prettify.js"></script>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="sorter.js"></script>
<script src="block-navigation.js"></script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,208 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/components/NavigationTabs.tsx</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> / <a href="index.html">src/components</a> NavigationTabs.tsx</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/30</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/30</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
<a name='L2'></a><a href='#L2'>2</a>
<a name='L3'></a><a href='#L3'>3</a>
<a name='L4'></a><a href='#L4'>4</a>
<a name='L5'></a><a href='#L5'>5</a>
<a name='L6'></a><a href='#L6'>6</a>
<a name='L7'></a><a href='#L7'>7</a>
<a name='L8'></a><a href='#L8'>8</a>
<a name='L9'></a><a href='#L9'>9</a>
<a name='L10'></a><a href='#L10'>10</a>
<a name='L11'></a><a href='#L11'>11</a>
<a name='L12'></a><a href='#L12'>12</a>
<a name='L13'></a><a href='#L13'>13</a>
<a name='L14'></a><a href='#L14'>14</a>
<a name='L15'></a><a href='#L15'>15</a>
<a name='L16'></a><a href='#L16'>16</a>
<a name='L17'></a><a href='#L17'>17</a>
<a name='L18'></a><a href='#L18'>18</a>
<a name='L19'></a><a href='#L19'>19</a>
<a name='L20'></a><a href='#L20'>20</a>
<a name='L21'></a><a href='#L21'>21</a>
<a name='L22'></a><a href='#L22'>22</a>
<a name='L23'></a><a href='#L23'>23</a>
<a name='L24'></a><a href='#L24'>24</a>
<a name='L25'></a><a href='#L25'>25</a>
<a name='L26'></a><a href='#L26'>26</a>
<a name='L27'></a><a href='#L27'>27</a>
<a name='L28'></a><a href='#L28'>28</a>
<a name='L29'></a><a href='#L29'>29</a>
<a name='L30'></a><a href='#L30'>30</a>
<a name='L31'></a><a href='#L31'>31</a>
<a name='L32'></a><a href='#L32'>32</a>
<a name='L33'></a><a href='#L33'>33</a>
<a name='L34'></a><a href='#L34'>34</a>
<a name='L35'></a><a href='#L35'>35</a>
<a name='L36'></a><a href='#L36'>36</a>
<a name='L37'></a><a href='#L37'>37</a>
<a name='L38'></a><a href='#L38'>38</a>
<a name='L39'></a><a href='#L39'>39</a>
<a name='L40'></a><a href='#L40'>40</a>
<a name='L41'></a><a href='#L41'>41</a>
<a name='L42'></a><a href='#L42'>42</a></td><td class="line-coverage quiet"><span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js"><span class="cstat-no" title="statement not covered" >import React from 'react'<span class="fstat-no" title="function not covered" ><span class="branch-0 cbranch-no" title="branch not covered" ></span></span></span>
<span class="cstat-no" title="statement not covered" >import { Tabs, Tab, Box } from '@mui/material'</span>
<span class="cstat-no" title="statement not covered" >import { useNavigate } from 'react-router-dom'</span>
&nbsp;
interface NavigationTabsProps {
currentPath: string
}
&nbsp;
<span class="cstat-no" title="statement not covered" >export const NavigationTabs: React.FC&lt;NavigationTabsProps&gt; = ({ currentPath }) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > const navigate = useNavigate()</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > const tabs = [</span>
<span class="cstat-no" title="statement not covered" > { label: 'Téléversement', path: '/' },</span>
<span class="cstat-no" title="statement not covered" > { label: 'Extraction', path: '/extraction' },</span>
<span class="cstat-no" title="statement not covered" > { label: 'Analyse', path: '/analyse' },</span>
<span class="cstat-no" title="statement not covered" > { label: 'Contexte', path: '/contexte' },</span>
<span class="cstat-no" title="statement not covered" > { label: 'Conseil', path: '/conseil' },</span>
<span class="cstat-no" title="statement not covered" > ]</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > const currentTabIndex = tabs.findIndex(tab =&gt; tab.path === currentPath)</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > const handleTabChange = (_event: React.SyntheticEvent, newValue: number) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > navigate(tabs[newValue].path)</span>
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ borderBottom: 1, borderColor: 'divider' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Tabs</span>
<span class="cstat-no" title="statement not covered" > value={currentTabIndex &gt;= 0 ? currentTabIndex : 0}</span>
<span class="cstat-no" title="statement not covered" > onChange={handleTabChange}</span>
<span class="cstat-no" title="statement not covered" > aria-label="navigation tabs"</span>
<span class="cstat-no" title="statement not covered" > variant="scrollable"</span>
<span class="cstat-no" title="statement not covered" > scrollButtons="auto"</span>
&gt;
<span class="cstat-no" title="statement not covered" > {tabs.map((tab, index) =&gt; (</span>
<span class="cstat-no" title="statement not covered" > &lt;Tab key={index} label={tab.label} /&gt;</span>
<span class="cstat-no" title="statement not covered" > ))}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Tabs&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
)
<span class="cstat-no" title="statement not covered" >}</span>
&nbsp;</pre></td></tr></table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,146 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/components</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> src/components</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/227</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/3</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/3</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/227</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<div class="pad1">
<table class="coverage-summary">
<thead>
<tr>
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
</tr>
</thead>
<tbody><tr>
<td class="file low" data-value="FilePreview.tsx"><a href="FilePreview.tsx.html">FilePreview.tsx</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="171" class="abs low">0/171</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="171" class="abs low">0/171</td>
</tr>
<tr>
<td class="file low" data-value="Layout.tsx"><a href="Layout.tsx.html">Layout.tsx</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="26" class="abs low">0/26</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="26" class="abs low">0/26</td>
</tr>
<tr>
<td class="file low" data-value="NavigationTabs.tsx"><a href="NavigationTabs.tsx.html">NavigationTabs.tsx</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="30" class="abs low">0/30</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="30" class="abs low">0/30</td>
</tr>
</tbody>
</table>
</div>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

131
coverage/src/index.html Normal file
View File

@ -0,0 +1,131 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../prettify.css" />
<link rel="stylesheet" href="../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../index.html">All files</a> src</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/24</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/2</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/2</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/24</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<div class="pad1">
<table class="coverage-summary">
<thead>
<tr>
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
</tr>
</thead>
<tbody><tr>
<td class="file low" data-value="App.tsx"><a href="App.tsx.html">App.tsx</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="5" class="abs low">0/5</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="5" class="abs low">0/5</td>
</tr>
<tr>
<td class="file low" data-value="main.tsx"><a href="main.tsx.html">main.tsx</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="19" class="abs low">0/19</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="19" class="abs low">0/19</td>
</tr>
</tbody>
</table>
</div>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../sorter.js"></script>
<script src="../block-navigation.js"></script>
</body>
</html>

View File

@ -3,15 +3,15 @@
<html lang="en">
<head>
<title>Code coverage report for main.tsx</title>
<title>Code coverage report for src/main.tsx</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="prettify.css" />
<link rel="stylesheet" href="base.css" />
<link rel="shortcut icon" type="image/x-icon" href="favicon.png" />
<link rel="stylesheet" href="../prettify.css" />
<link rel="stylesheet" href="../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(sort-arrow-sprite.png);
background-image: url(../sort-arrow-sprite.png);
}
</style>
</head>
@ -19,13 +19,13 @@
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="index.html">All files</a> main.tsx</h1>
<h1><a href="../index.html">All files</a> / <a href="index.html">src</a> main.tsx</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/9</span>
<span class='fraction'>0/19</span>
</div>
@ -46,7 +46,7 @@
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/9</span>
<span class='fraction'>0/19</span>
</div>
@ -73,7 +73,22 @@
<a name='L8'></a><a href='#L8'>8</a>
<a name='L9'></a><a href='#L9'>9</a>
<a name='L10'></a><a href='#L10'>10</a>
<a name='L11'></a><a href='#L11'>11</a></td><td class="line-coverage quiet"><span class="cline-any cline-no">&nbsp;</span>
<a name='L11'></a><a href='#L11'>11</a>
<a name='L12'></a><a href='#L12'>12</a>
<a name='L13'></a><a href='#L13'>13</a>
<a name='L14'></a><a href='#L14'>14</a>
<a name='L15'></a><a href='#L15'>15</a>
<a name='L16'></a><a href='#L16'>16</a>
<a name='L17'></a><a href='#L17'>17</a>
<a name='L18'></a><a href='#L18'>18</a>
<a name='L19'></a><a href='#L19'>19</a>
<a name='L20'></a><a href='#L20'>20</a>
<a name='L21'></a><a href='#L21'>21</a></td><td class="line-coverage quiet"><span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
@ -83,14 +98,29 @@
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js"><span class="cstat-no" title="statement not covered" >import { StrictMode } from 'react'<span class="fstat-no" title="function not covered" ><span class="branch-0 cbranch-no" title="branch not covered" ></span></span></span>
<span class="cstat-no" title="statement not covered" >import { createRoot } from 'react-dom/client'</span>
<span class="cstat-no" title="statement not covered" >import { Provider } from 'react-redux'</span>
<span class="cstat-no" title="statement not covered" >import { ThemeProvider } from '@mui/material/styles'</span>
<span class="cstat-no" title="statement not covered" >import { CssBaseline } from '@mui/material'</span>
<span class="cstat-no" title="statement not covered" >import './index.css'</span>
<span class="cstat-no" title="statement not covered" >import App from './App.tsx'</span>
<span class="cstat-no" title="statement not covered" >import { store } from './store'</span>
<span class="cstat-no" title="statement not covered" >import { theme } from './theme'</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >createRoot(document.getElementById('root')!).render(</span>
<span class="cstat-no" title="statement not covered" > &lt;StrictMode&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Provider store={store}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ThemeProvider theme={theme}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CssBaseline /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;App /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/ThemeProvider&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Provider&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/StrictMode&gt;,</span>
<span class="cstat-no" title="statement not covered" >)</span>
&nbsp;</pre></td></tr></table></pre>
@ -100,16 +130,16 @@
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-10T15:15:07.789Z
at 2025-09-11T12:57:49.285Z
</div>
<script src="prettify.js"></script>
<script src="../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="sorter.js"></script>
<script src="block-navigation.js"></script>
<script src="../sorter.js"></script>
<script src="../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,116 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/router</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> src/router</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/23</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/23</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<div class="pad1">
<table class="coverage-summary">
<thead>
<tr>
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
</tr>
</thead>
<tbody><tr>
<td class="file low" data-value="index.tsx"><a href="index.tsx.html">index.tsx</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="23" class="abs low">0/23</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="23" class="abs low">0/23</td>
</tr>
</tbody>
</table>
</div>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,169 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/router/index.tsx</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> / <a href="index.html">src/router</a> index.tsx</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/23</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/23</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
<a name='L2'></a><a href='#L2'>2</a>
<a name='L3'></a><a href='#L3'>3</a>
<a name='L4'></a><a href='#L4'>4</a>
<a name='L5'></a><a href='#L5'>5</a>
<a name='L6'></a><a href='#L6'>6</a>
<a name='L7'></a><a href='#L7'>7</a>
<a name='L8'></a><a href='#L8'>8</a>
<a name='L9'></a><a href='#L9'>9</a>
<a name='L10'></a><a href='#L10'>10</a>
<a name='L11'></a><a href='#L11'>11</a>
<a name='L12'></a><a href='#L12'>12</a>
<a name='L13'></a><a href='#L13'>13</a>
<a name='L14'></a><a href='#L14'>14</a>
<a name='L15'></a><a href='#L15'>15</a>
<a name='L16'></a><a href='#L16'>16</a>
<a name='L17'></a><a href='#L17'>17</a>
<a name='L18'></a><a href='#L18'>18</a>
<a name='L19'></a><a href='#L19'>19</a>
<a name='L20'></a><a href='#L20'>20</a>
<a name='L21'></a><a href='#L21'>21</a>
<a name='L22'></a><a href='#L22'>22</a>
<a name='L23'></a><a href='#L23'>23</a>
<a name='L24'></a><a href='#L24'>24</a>
<a name='L25'></a><a href='#L25'>25</a>
<a name='L26'></a><a href='#L26'>26</a>
<a name='L27'></a><a href='#L27'>27</a>
<a name='L28'></a><a href='#L28'>28</a>
<a name='L29'></a><a href='#L29'>29</a></td><td class="line-coverage quiet"><span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js"><span class="cstat-no" title="statement not covered" >import { lazy, Suspense } from 'react'<span class="fstat-no" title="function not covered" ><span class="branch-0 cbranch-no" title="branch not covered" ></span></span></span>
<span class="cstat-no" title="statement not covered" >import { createBrowserRouter, RouterProvider } from 'react-router-dom'</span>
<span class="cstat-no" title="statement not covered" >import { Box, CircularProgress, Typography } from '@mui/material'</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >const UploadView = lazy(() =&gt; import('../views/UploadView'))</span>
<span class="cstat-no" title="statement not covered" >const ExtractionView = lazy(() =&gt; import('../views/ExtractionView'))</span>
<span class="cstat-no" title="statement not covered" >const AnalyseView = lazy(() =&gt; import('../views/AnalyseView'))</span>
<span class="cstat-no" title="statement not covered" >const ContexteView = lazy(() =&gt; import('../views/ContexteView'))</span>
<span class="cstat-no" title="statement not covered" >const ConseilView = lazy(() =&gt; import('../views/ConseilView'))</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >const LoadingFallback = () =&gt; (</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '50vh' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CircularProgress /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography sx={{ ml: 2 }}&gt;Chargement...&lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
)
&nbsp;
<span class="cstat-no" title="statement not covered" >const router = createBrowserRouter([</span>
<span class="cstat-no" title="statement not covered" > { path: '/', element: &lt;Suspense fallback={&lt;LoadingFallback /&gt;}&gt;&lt;UploadView /&gt;&lt;/Suspense&gt; },</span>
<span class="cstat-no" title="statement not covered" > { path: '/extraction', element: &lt;Suspense fallback={&lt;LoadingFallback /&gt;}&gt;&lt;ExtractionView /&gt;&lt;/Suspense&gt; },</span>
<span class="cstat-no" title="statement not covered" > { path: '/analyse', element: &lt;Suspense fallback={&lt;LoadingFallback /&gt;}&gt;&lt;AnalyseView /&gt;&lt;/Suspense&gt; },</span>
<span class="cstat-no" title="statement not covered" > { path: '/contexte', element: &lt;Suspense fallback={&lt;LoadingFallback /&gt;}&gt;&lt;ContexteView /&gt;&lt;/Suspense&gt; },</span>
<span class="cstat-no" title="statement not covered" > { path: '/conseil', element: &lt;Suspense fallback={&lt;LoadingFallback /&gt;}&gt;&lt;ConseilView /&gt;&lt;/Suspense&gt; },</span>
<span class="cstat-no" title="statement not covered" >])</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >export const AppRouter = () =&gt; {</span>
<span class="cstat-no" title="statement not covered" > return &lt;RouterProvider router={router} /&gt;</span>
<span class="cstat-no" title="statement not covered" >}</span>
&nbsp;</pre></td></tr></table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,562 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/services/api.ts</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> / <a href="index.html">src/services</a> api.ts</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/124</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/124</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
<a name='L2'></a><a href='#L2'>2</a>
<a name='L3'></a><a href='#L3'>3</a>
<a name='L4'></a><a href='#L4'>4</a>
<a name='L5'></a><a href='#L5'>5</a>
<a name='L6'></a><a href='#L6'>6</a>
<a name='L7'></a><a href='#L7'>7</a>
<a name='L8'></a><a href='#L8'>8</a>
<a name='L9'></a><a href='#L9'>9</a>
<a name='L10'></a><a href='#L10'>10</a>
<a name='L11'></a><a href='#L11'>11</a>
<a name='L12'></a><a href='#L12'>12</a>
<a name='L13'></a><a href='#L13'>13</a>
<a name='L14'></a><a href='#L14'>14</a>
<a name='L15'></a><a href='#L15'>15</a>
<a name='L16'></a><a href='#L16'>16</a>
<a name='L17'></a><a href='#L17'>17</a>
<a name='L18'></a><a href='#L18'>18</a>
<a name='L19'></a><a href='#L19'>19</a>
<a name='L20'></a><a href='#L20'>20</a>
<a name='L21'></a><a href='#L21'>21</a>
<a name='L22'></a><a href='#L22'>22</a>
<a name='L23'></a><a href='#L23'>23</a>
<a name='L24'></a><a href='#L24'>24</a>
<a name='L25'></a><a href='#L25'>25</a>
<a name='L26'></a><a href='#L26'>26</a>
<a name='L27'></a><a href='#L27'>27</a>
<a name='L28'></a><a href='#L28'>28</a>
<a name='L29'></a><a href='#L29'>29</a>
<a name='L30'></a><a href='#L30'>30</a>
<a name='L31'></a><a href='#L31'>31</a>
<a name='L32'></a><a href='#L32'>32</a>
<a name='L33'></a><a href='#L33'>33</a>
<a name='L34'></a><a href='#L34'>34</a>
<a name='L35'></a><a href='#L35'>35</a>
<a name='L36'></a><a href='#L36'>36</a>
<a name='L37'></a><a href='#L37'>37</a>
<a name='L38'></a><a href='#L38'>38</a>
<a name='L39'></a><a href='#L39'>39</a>
<a name='L40'></a><a href='#L40'>40</a>
<a name='L41'></a><a href='#L41'>41</a>
<a name='L42'></a><a href='#L42'>42</a>
<a name='L43'></a><a href='#L43'>43</a>
<a name='L44'></a><a href='#L44'>44</a>
<a name='L45'></a><a href='#L45'>45</a>
<a name='L46'></a><a href='#L46'>46</a>
<a name='L47'></a><a href='#L47'>47</a>
<a name='L48'></a><a href='#L48'>48</a>
<a name='L49'></a><a href='#L49'>49</a>
<a name='L50'></a><a href='#L50'>50</a>
<a name='L51'></a><a href='#L51'>51</a>
<a name='L52'></a><a href='#L52'>52</a>
<a name='L53'></a><a href='#L53'>53</a>
<a name='L54'></a><a href='#L54'>54</a>
<a name='L55'></a><a href='#L55'>55</a>
<a name='L56'></a><a href='#L56'>56</a>
<a name='L57'></a><a href='#L57'>57</a>
<a name='L58'></a><a href='#L58'>58</a>
<a name='L59'></a><a href='#L59'>59</a>
<a name='L60'></a><a href='#L60'>60</a>
<a name='L61'></a><a href='#L61'>61</a>
<a name='L62'></a><a href='#L62'>62</a>
<a name='L63'></a><a href='#L63'>63</a>
<a name='L64'></a><a href='#L64'>64</a>
<a name='L65'></a><a href='#L65'>65</a>
<a name='L66'></a><a href='#L66'>66</a>
<a name='L67'></a><a href='#L67'>67</a>
<a name='L68'></a><a href='#L68'>68</a>
<a name='L69'></a><a href='#L69'>69</a>
<a name='L70'></a><a href='#L70'>70</a>
<a name='L71'></a><a href='#L71'>71</a>
<a name='L72'></a><a href='#L72'>72</a>
<a name='L73'></a><a href='#L73'>73</a>
<a name='L74'></a><a href='#L74'>74</a>
<a name='L75'></a><a href='#L75'>75</a>
<a name='L76'></a><a href='#L76'>76</a>
<a name='L77'></a><a href='#L77'>77</a>
<a name='L78'></a><a href='#L78'>78</a>
<a name='L79'></a><a href='#L79'>79</a>
<a name='L80'></a><a href='#L80'>80</a>
<a name='L81'></a><a href='#L81'>81</a>
<a name='L82'></a><a href='#L82'>82</a>
<a name='L83'></a><a href='#L83'>83</a>
<a name='L84'></a><a href='#L84'>84</a>
<a name='L85'></a><a href='#L85'>85</a>
<a name='L86'></a><a href='#L86'>86</a>
<a name='L87'></a><a href='#L87'>87</a>
<a name='L88'></a><a href='#L88'>88</a>
<a name='L89'></a><a href='#L89'>89</a>
<a name='L90'></a><a href='#L90'>90</a>
<a name='L91'></a><a href='#L91'>91</a>
<a name='L92'></a><a href='#L92'>92</a>
<a name='L93'></a><a href='#L93'>93</a>
<a name='L94'></a><a href='#L94'>94</a>
<a name='L95'></a><a href='#L95'>95</a>
<a name='L96'></a><a href='#L96'>96</a>
<a name='L97'></a><a href='#L97'>97</a>
<a name='L98'></a><a href='#L98'>98</a>
<a name='L99'></a><a href='#L99'>99</a>
<a name='L100'></a><a href='#L100'>100</a>
<a name='L101'></a><a href='#L101'>101</a>
<a name='L102'></a><a href='#L102'>102</a>
<a name='L103'></a><a href='#L103'>103</a>
<a name='L104'></a><a href='#L104'>104</a>
<a name='L105'></a><a href='#L105'>105</a>
<a name='L106'></a><a href='#L106'>106</a>
<a name='L107'></a><a href='#L107'>107</a>
<a name='L108'></a><a href='#L108'>108</a>
<a name='L109'></a><a href='#L109'>109</a>
<a name='L110'></a><a href='#L110'>110</a>
<a name='L111'></a><a href='#L111'>111</a>
<a name='L112'></a><a href='#L112'>112</a>
<a name='L113'></a><a href='#L113'>113</a>
<a name='L114'></a><a href='#L114'>114</a>
<a name='L115'></a><a href='#L115'>115</a>
<a name='L116'></a><a href='#L116'>116</a>
<a name='L117'></a><a href='#L117'>117</a>
<a name='L118'></a><a href='#L118'>118</a>
<a name='L119'></a><a href='#L119'>119</a>
<a name='L120'></a><a href='#L120'>120</a>
<a name='L121'></a><a href='#L121'>121</a>
<a name='L122'></a><a href='#L122'>122</a>
<a name='L123'></a><a href='#L123'>123</a>
<a name='L124'></a><a href='#L124'>124</a>
<a name='L125'></a><a href='#L125'>125</a>
<a name='L126'></a><a href='#L126'>126</a>
<a name='L127'></a><a href='#L127'>127</a>
<a name='L128'></a><a href='#L128'>128</a>
<a name='L129'></a><a href='#L129'>129</a>
<a name='L130'></a><a href='#L130'>130</a>
<a name='L131'></a><a href='#L131'>131</a>
<a name='L132'></a><a href='#L132'>132</a>
<a name='L133'></a><a href='#L133'>133</a>
<a name='L134'></a><a href='#L134'>134</a>
<a name='L135'></a><a href='#L135'>135</a>
<a name='L136'></a><a href='#L136'>136</a>
<a name='L137'></a><a href='#L137'>137</a>
<a name='L138'></a><a href='#L138'>138</a>
<a name='L139'></a><a href='#L139'>139</a>
<a name='L140'></a><a href='#L140'>140</a>
<a name='L141'></a><a href='#L141'>141</a>
<a name='L142'></a><a href='#L142'>142</a>
<a name='L143'></a><a href='#L143'>143</a>
<a name='L144'></a><a href='#L144'>144</a>
<a name='L145'></a><a href='#L145'>145</a>
<a name='L146'></a><a href='#L146'>146</a>
<a name='L147'></a><a href='#L147'>147</a>
<a name='L148'></a><a href='#L148'>148</a>
<a name='L149'></a><a href='#L149'>149</a>
<a name='L150'></a><a href='#L150'>150</a>
<a name='L151'></a><a href='#L151'>151</a>
<a name='L152'></a><a href='#L152'>152</a>
<a name='L153'></a><a href='#L153'>153</a>
<a name='L154'></a><a href='#L154'>154</a>
<a name='L155'></a><a href='#L155'>155</a>
<a name='L156'></a><a href='#L156'>156</a>
<a name='L157'></a><a href='#L157'>157</a>
<a name='L158'></a><a href='#L158'>158</a>
<a name='L159'></a><a href='#L159'>159</a>
<a name='L160'></a><a href='#L160'>160</a></td><td class="line-coverage quiet"><span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js"><span class="cstat-no" title="statement not covered" >import axios from 'axios'<span class="fstat-no" title="function not covered" ><span class="branch-0 cbranch-no" title="branch not covered" ></span></span></span>
import type { Document, ExtractionResult, AnalysisResult, ContextResult, ConseilResult } from '../types'
&nbsp;
<span class="cstat-no" title="statement not covered" >const BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >export const apiClient = axios.create({</span>
<span class="cstat-no" title="statement not covered" > baseURL: BASE_URL,</span>
<span class="cstat-no" title="statement not covered" > timeout: 60000,</span>
<span class="cstat-no" title="statement not covered" >})</span>
&nbsp;
// Intercepteur pour les erreurs
<span class="cstat-no" title="statement not covered" >apiClient.interceptors.response.use(</span>
<span class="cstat-no" title="statement not covered" > (response) =&gt; response,</span>
<span class="cstat-no" title="statement not covered" > (error) =&gt; {</span>
// Laisser remonter les erreurs au consommateur
<span class="cstat-no" title="statement not covered" > return Promise.reject(error)</span>
<span class="cstat-no" title="statement not covered" > }</span>
<span class="cstat-no" title="statement not covered" >)</span>
&nbsp;
// Services API pour les documents
<span class="cstat-no" title="statement not covered" >export const documentApi = {</span>
// Téléversement de document
<span class="cstat-no" title="statement not covered" > upload: async (file: File): Promise&lt;Document&gt; =&gt; {</span>
<span class="cstat-no" title="statement not covered" > const formData = new FormData()</span>
<span class="cstat-no" title="statement not covered" > formData.append('file', file)</span>
<span class="cstat-no" title="statement not covered" > const { data } = await apiClient.post('/api/notary/upload', formData, {</span>
<span class="cstat-no" title="statement not covered" > headers: { 'Content-Type': 'multipart/form-data' },</span>
<span class="cstat-no" title="statement not covered" > })</span>
&nbsp;
// L'API retourne {message, document_id, status}
// On doit mapper vers le format Document attendu
<span class="cstat-no" title="statement not covered" > const fileUrl = URL.createObjectURL(file)</span>
<span class="cstat-no" title="statement not covered" > return {</span>
<span class="cstat-no" title="statement not covered" > id: data.document_id || data.id || 'upload-' + Date.now(),</span>
<span class="cstat-no" title="statement not covered" > name: file.name,</span>
<span class="cstat-no" title="statement not covered" > mimeType: data.mime_type || data.mimeType || file.type || 'application/pdf',</span>
<span class="cstat-no" title="statement not covered" > functionalType: data.functional_type || data.functionalType || undefined,</span>
<span class="cstat-no" title="statement not covered" > size: file.size,</span>
<span class="cstat-no" title="statement not covered" > uploadDate: new Date(),</span>
<span class="cstat-no" title="statement not covered" > status: 'completed',</span>
<span class="cstat-no" title="statement not covered" > previewUrl: fileUrl</span>
<span class="cstat-no" title="statement not covered" > }</span>
<span class="cstat-no" title="statement not covered" > },</span>
&nbsp;
// Extraction des données
<span class="cstat-no" title="statement not covered" > extract: async (documentId: string): Promise&lt;ExtractionResult&gt; =&gt; {</span>
<span class="cstat-no" title="statement not covered" > const { data } = await apiClient.get(`/api/notary/documents/${documentId}`)</span>
&nbsp;
// Mapper les données de l'API vers le format ExtractionResult
<span class="cstat-no" title="statement not covered" > const results = data.results || {}</span>
<span class="cstat-no" title="statement not covered" > return {</span>
<span class="cstat-no" title="statement not covered" > documentId,</span>
<span class="cstat-no" title="statement not covered" > text: results.ocr_text || 'Texte extrait du document...',</span>
<span class="cstat-no" title="statement not covered" > language: 'fr',</span>
<span class="cstat-no" title="statement not covered" > documentType: results.document_type || 'Document',</span>
<span class="cstat-no" title="statement not covered" > identities: results.entities?.persons?.map((name: string, index: number) =&gt; ({</span>
<span class="cstat-no" title="statement not covered" > id: `person-${index}`,</span>
<span class="cstat-no" title="statement not covered" > type: 'person' as const,</span>
<span class="cstat-no" title="statement not covered" > firstName: name.split(' ')[0] || name,</span>
<span class="cstat-no" title="statement not covered" > lastName: name.split(' ').slice(1).join(' ') || '',</span>
<span class="cstat-no" title="statement not covered" > birthDate: '',</span>
<span class="cstat-no" title="statement not covered" > nationality: 'Française',</span>
<span class="cstat-no" title="statement not covered" > confidence: 0.9,</span>
<span class="cstat-no" title="statement not covered" > })) || [],</span>
<span class="cstat-no" title="statement not covered" > addresses: results.entities?.addresses?.map((address: string) =&gt; ({</span>
<span class="cstat-no" title="statement not covered" > street: address,</span>
<span class="cstat-no" title="statement not covered" > city: 'Paris',</span>
<span class="cstat-no" title="statement not covered" > postalCode: '75001',</span>
<span class="cstat-no" title="statement not covered" > country: 'France',</span>
<span class="cstat-no" title="statement not covered" > })) || [],</span>
<span class="cstat-no" title="statement not covered" > properties: results.entities?.properties?.map((_propertyName: string, index: number) =&gt; ({</span>
<span class="cstat-no" title="statement not covered" > id: `prop-${index}`,</span>
<span class="cstat-no" title="statement not covered" > type: 'apartment' as const,</span>
<span class="cstat-no" title="statement not covered" > address: {</span>
<span class="cstat-no" title="statement not covered" > street: '123 Rue de la Paix',</span>
<span class="cstat-no" title="statement not covered" > city: 'Paris',</span>
<span class="cstat-no" title="statement not covered" > postalCode: '75001',</span>
<span class="cstat-no" title="statement not covered" > country: 'France',</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > surface: 75,</span>
<span class="cstat-no" title="statement not covered" > cadastralReference: '1234567890AB',</span>
<span class="cstat-no" title="statement not covered" > value: 250000,</span>
<span class="cstat-no" title="statement not covered" > })) || [],</span>
<span class="cstat-no" title="statement not covered" > contracts: [</span>
<span class="cstat-no" title="statement not covered" > {</span>
<span class="cstat-no" title="statement not covered" > id: 'contract-1',</span>
<span class="cstat-no" title="statement not covered" > type: 'sale' as const,</span>
<span class="cstat-no" title="statement not covered" > parties: [],</span>
<span class="cstat-no" title="statement not covered" > amount: 250000,</span>
<span class="cstat-no" title="statement not covered" > date: '2024-01-15',</span>
<span class="cstat-no" title="statement not covered" > clauses: ['Clause de garantie', 'Clause de condition suspensive'],</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > ],</span>
<span class="cstat-no" title="statement not covered" > signatures: results.entities?.persons || [],</span>
<span class="cstat-no" title="statement not covered" > confidence: results.verification_score || 0.85,</span>
<span class="cstat-no" title="statement not covered" > }</span>
<span class="cstat-no" title="statement not covered" > },</span>
&nbsp;
// Analyse du document
<span class="cstat-no" title="statement not covered" > analyze: async (documentId: string): Promise&lt;AnalysisResult&gt; =&gt; {</span>
<span class="cstat-no" title="statement not covered" > const { data } = await apiClient.get&lt;AnalysisResult&gt;(`/api/documents/${documentId}/analyze`)</span>
<span class="cstat-no" title="statement not covered" > return data</span>
<span class="cstat-no" title="statement not covered" > },</span>
&nbsp;
// Données contextuelles
<span class="cstat-no" title="statement not covered" > getContext: async (documentId: string): Promise&lt;ContextResult&gt; =&gt; {</span>
<span class="cstat-no" title="statement not covered" > const { data } = await apiClient.get&lt;ContextResult&gt;(`/api/documents/${documentId}/context`)</span>
<span class="cstat-no" title="statement not covered" > return data</span>
<span class="cstat-no" title="statement not covered" > },</span>
&nbsp;
// Conseil LLM
<span class="cstat-no" title="statement not covered" > getConseil: async (documentId: string): Promise&lt;ConseilResult&gt; =&gt; {</span>
<span class="cstat-no" title="statement not covered" > const { data } = await apiClient.get&lt;ConseilResult&gt;(`/api/documents/${documentId}/conseil`)</span>
<span class="cstat-no" title="statement not covered" > return data</span>
<span class="cstat-no" title="statement not covered" > },</span>
&nbsp;
// Détection du type de document
<span class="cstat-no" title="statement not covered" > detectType: async (file: File): Promise&lt;{ type: string; confidence: number }&gt; =&gt; {</span>
<span class="cstat-no" title="statement not covered" > const formData = new FormData()</span>
<span class="cstat-no" title="statement not covered" > formData.append('file', file)</span>
<span class="cstat-no" title="statement not covered" > const { data } = await apiClient.post('/api/ocr/detect', formData, {</span>
<span class="cstat-no" title="statement not covered" > headers: { 'Content-Type': 'multipart/form-data' },</span>
<span class="cstat-no" title="statement not covered" > })</span>
<span class="cstat-no" title="statement not covered" > return data</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" >}</span>
&nbsp;
// Services API pour les données externes
<span class="cstat-no" title="statement not covered" >export const externalApi = {</span>
// Cadastre via backend
<span class="cstat-no" title="statement not covered" > cadastre: async (address: string) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > const { data } = await apiClient.get('/api/context/cadastre', { params: { q: address } })</span>
<span class="cstat-no" title="statement not covered" > return data</span>
<span class="cstat-no" title="statement not covered" > },</span>
&nbsp;
// Géorisques via backend
<span class="cstat-no" title="statement not covered" > georisques: async (coordinates: { lat: number; lng: number }) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > const { data } = await apiClient.get('/api/context/georisques', { params: coordinates })</span>
<span class="cstat-no" title="statement not covered" > return data</span>
<span class="cstat-no" title="statement not covered" > },</span>
&nbsp;
// Géofoncier via backend
<span class="cstat-no" title="statement not covered" > geofoncier: async (address: string) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > const { data } = await apiClient.get('/api/context/geofoncier', { params: { address } })</span>
<span class="cstat-no" title="statement not covered" > return data</span>
<span class="cstat-no" title="statement not covered" > },</span>
&nbsp;
// BODACC via backend
<span class="cstat-no" title="statement not covered" > bodacc: async (companyName: string) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > const { data } = await apiClient.get('/api/context/bodacc', { params: { q: companyName } })</span>
<span class="cstat-no" title="statement not covered" > return data</span>
<span class="cstat-no" title="statement not covered" > },</span>
&nbsp;
// Infogreffe via backend
<span class="cstat-no" title="statement not covered" > infogreffe: async (siren: string) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > const { data } = await apiClient.get('/api/context/infogreffe', { params: { siren } })</span>
<span class="cstat-no" title="statement not covered" > return data</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" >}</span>
&nbsp;</pre></td></tr></table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,116 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/services</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> src/services</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/124</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/124</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<div class="pad1">
<table class="coverage-summary">
<thead>
<tr>
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
</tr>
</thead>
<tbody><tr>
<td class="file low" data-value="api.ts"><a href="api.ts.html">api.ts</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="124" class="abs low">0/124</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="124" class="abs low">0/124</td>
</tr>
</tbody>
</table>
</div>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,136 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/store/appSlice.ts</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> / <a href="index.html">src/store</a> appSlice.ts</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/10</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/10</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
<a name='L2'></a><a href='#L2'>2</a>
<a name='L3'></a><a href='#L3'>3</a>
<a name='L4'></a><a href='#L4'>4</a>
<a name='L5'></a><a href='#L5'>5</a>
<a name='L6'></a><a href='#L6'>6</a>
<a name='L7'></a><a href='#L7'>7</a>
<a name='L8'></a><a href='#L8'>8</a>
<a name='L9'></a><a href='#L9'>9</a>
<a name='L10'></a><a href='#L10'>10</a>
<a name='L11'></a><a href='#L11'>11</a>
<a name='L12'></a><a href='#L12'>12</a>
<a name='L13'></a><a href='#L13'>13</a>
<a name='L14'></a><a href='#L14'>14</a>
<a name='L15'></a><a href='#L15'>15</a>
<a name='L16'></a><a href='#L16'>16</a>
<a name='L17'></a><a href='#L17'>17</a>
<a name='L18'></a><a href='#L18'>18</a></td><td class="line-coverage quiet"><span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js"><span class="cstat-no" title="statement not covered" >import { createSlice } from '@reduxjs/toolkit'<span class="fstat-no" title="function not covered" ><span class="branch-0 cbranch-no" title="branch not covered" ></span></span></span>
&nbsp;
export type AppState = {
initialized: boolean
}
&nbsp;
<span class="cstat-no" title="statement not covered" >const initialState: AppState = {</span>
<span class="cstat-no" title="statement not covered" > initialized: true,</span>
<span class="cstat-no" title="statement not covered" >}</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >const appSlice = createSlice({</span>
<span class="cstat-no" title="statement not covered" > name: 'app',</span>
<span class="cstat-no" title="statement not covered" > initialState,</span>
<span class="cstat-no" title="statement not covered" > reducers: {},</span>
<span class="cstat-no" title="statement not covered" >})</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >export const appReducer = appSlice.reducer</span>
&nbsp;</pre></td></tr></table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,409 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/store/documentSlice.ts</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> / <a href="index.html">src/store</a> documentSlice.ts</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/87</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/87</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
<a name='L2'></a><a href='#L2'>2</a>
<a name='L3'></a><a href='#L3'>3</a>
<a name='L4'></a><a href='#L4'>4</a>
<a name='L5'></a><a href='#L5'>5</a>
<a name='L6'></a><a href='#L6'>6</a>
<a name='L7'></a><a href='#L7'>7</a>
<a name='L8'></a><a href='#L8'>8</a>
<a name='L9'></a><a href='#L9'>9</a>
<a name='L10'></a><a href='#L10'>10</a>
<a name='L11'></a><a href='#L11'>11</a>
<a name='L12'></a><a href='#L12'>12</a>
<a name='L13'></a><a href='#L13'>13</a>
<a name='L14'></a><a href='#L14'>14</a>
<a name='L15'></a><a href='#L15'>15</a>
<a name='L16'></a><a href='#L16'>16</a>
<a name='L17'></a><a href='#L17'>17</a>
<a name='L18'></a><a href='#L18'>18</a>
<a name='L19'></a><a href='#L19'>19</a>
<a name='L20'></a><a href='#L20'>20</a>
<a name='L21'></a><a href='#L21'>21</a>
<a name='L22'></a><a href='#L22'>22</a>
<a name='L23'></a><a href='#L23'>23</a>
<a name='L24'></a><a href='#L24'>24</a>
<a name='L25'></a><a href='#L25'>25</a>
<a name='L26'></a><a href='#L26'>26</a>
<a name='L27'></a><a href='#L27'>27</a>
<a name='L28'></a><a href='#L28'>28</a>
<a name='L29'></a><a href='#L29'>29</a>
<a name='L30'></a><a href='#L30'>30</a>
<a name='L31'></a><a href='#L31'>31</a>
<a name='L32'></a><a href='#L32'>32</a>
<a name='L33'></a><a href='#L33'>33</a>
<a name='L34'></a><a href='#L34'>34</a>
<a name='L35'></a><a href='#L35'>35</a>
<a name='L36'></a><a href='#L36'>36</a>
<a name='L37'></a><a href='#L37'>37</a>
<a name='L38'></a><a href='#L38'>38</a>
<a name='L39'></a><a href='#L39'>39</a>
<a name='L40'></a><a href='#L40'>40</a>
<a name='L41'></a><a href='#L41'>41</a>
<a name='L42'></a><a href='#L42'>42</a>
<a name='L43'></a><a href='#L43'>43</a>
<a name='L44'></a><a href='#L44'>44</a>
<a name='L45'></a><a href='#L45'>45</a>
<a name='L46'></a><a href='#L46'>46</a>
<a name='L47'></a><a href='#L47'>47</a>
<a name='L48'></a><a href='#L48'>48</a>
<a name='L49'></a><a href='#L49'>49</a>
<a name='L50'></a><a href='#L50'>50</a>
<a name='L51'></a><a href='#L51'>51</a>
<a name='L52'></a><a href='#L52'>52</a>
<a name='L53'></a><a href='#L53'>53</a>
<a name='L54'></a><a href='#L54'>54</a>
<a name='L55'></a><a href='#L55'>55</a>
<a name='L56'></a><a href='#L56'>56</a>
<a name='L57'></a><a href='#L57'>57</a>
<a name='L58'></a><a href='#L58'>58</a>
<a name='L59'></a><a href='#L59'>59</a>
<a name='L60'></a><a href='#L60'>60</a>
<a name='L61'></a><a href='#L61'>61</a>
<a name='L62'></a><a href='#L62'>62</a>
<a name='L63'></a><a href='#L63'>63</a>
<a name='L64'></a><a href='#L64'>64</a>
<a name='L65'></a><a href='#L65'>65</a>
<a name='L66'></a><a href='#L66'>66</a>
<a name='L67'></a><a href='#L67'>67</a>
<a name='L68'></a><a href='#L68'>68</a>
<a name='L69'></a><a href='#L69'>69</a>
<a name='L70'></a><a href='#L70'>70</a>
<a name='L71'></a><a href='#L71'>71</a>
<a name='L72'></a><a href='#L72'>72</a>
<a name='L73'></a><a href='#L73'>73</a>
<a name='L74'></a><a href='#L74'>74</a>
<a name='L75'></a><a href='#L75'>75</a>
<a name='L76'></a><a href='#L76'>76</a>
<a name='L77'></a><a href='#L77'>77</a>
<a name='L78'></a><a href='#L78'>78</a>
<a name='L79'></a><a href='#L79'>79</a>
<a name='L80'></a><a href='#L80'>80</a>
<a name='L81'></a><a href='#L81'>81</a>
<a name='L82'></a><a href='#L82'>82</a>
<a name='L83'></a><a href='#L83'>83</a>
<a name='L84'></a><a href='#L84'>84</a>
<a name='L85'></a><a href='#L85'>85</a>
<a name='L86'></a><a href='#L86'>86</a>
<a name='L87'></a><a href='#L87'>87</a>
<a name='L88'></a><a href='#L88'>88</a>
<a name='L89'></a><a href='#L89'>89</a>
<a name='L90'></a><a href='#L90'>90</a>
<a name='L91'></a><a href='#L91'>91</a>
<a name='L92'></a><a href='#L92'>92</a>
<a name='L93'></a><a href='#L93'>93</a>
<a name='L94'></a><a href='#L94'>94</a>
<a name='L95'></a><a href='#L95'>95</a>
<a name='L96'></a><a href='#L96'>96</a>
<a name='L97'></a><a href='#L97'>97</a>
<a name='L98'></a><a href='#L98'>98</a>
<a name='L99'></a><a href='#L99'>99</a>
<a name='L100'></a><a href='#L100'>100</a>
<a name='L101'></a><a href='#L101'>101</a>
<a name='L102'></a><a href='#L102'>102</a>
<a name='L103'></a><a href='#L103'>103</a>
<a name='L104'></a><a href='#L104'>104</a>
<a name='L105'></a><a href='#L105'>105</a>
<a name='L106'></a><a href='#L106'>106</a>
<a name='L107'></a><a href='#L107'>107</a>
<a name='L108'></a><a href='#L108'>108</a>
<a name='L109'></a><a href='#L109'>109</a></td><td class="line-coverage quiet"><span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js"><span class="cstat-no" title="statement not covered" >import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'<span class="fstat-no" title="function not covered" ><span class="branch-0 cbranch-no" title="branch not covered" ></span></span></span>
import type { PayloadAction } from '@reduxjs/toolkit'
import type { Document, ExtractionResult, AnalysisResult, ContextResult, ConseilResult } from '../types'
<span class="cstat-no" title="statement not covered" >import { documentApi } from '../services/api'</span>
&nbsp;
interface DocumentState {
documents: Document[]
currentDocument: Document | null
extractionResult: ExtractionResult | null
analysisResult: AnalysisResult | null
contextResult: ContextResult | null
conseilResult: ConseilResult | null
loading: boolean
error: string | null
}
&nbsp;
<span class="cstat-no" title="statement not covered" >const initialState: DocumentState = {</span>
<span class="cstat-no" title="statement not covered" > documents: [],</span>
<span class="cstat-no" title="statement not covered" > currentDocument: null,</span>
<span class="cstat-no" title="statement not covered" > extractionResult: null,</span>
<span class="cstat-no" title="statement not covered" > analysisResult: null,</span>
<span class="cstat-no" title="statement not covered" > contextResult: null,</span>
<span class="cstat-no" title="statement not covered" > conseilResult: null,</span>
<span class="cstat-no" title="statement not covered" > loading: false,</span>
<span class="cstat-no" title="statement not covered" > error: null,</span>
<span class="cstat-no" title="statement not covered" >}</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >export const uploadDocument = createAsyncThunk(</span>
<span class="cstat-no" title="statement not covered" > 'document/upload',</span>
<span class="cstat-no" title="statement not covered" > async (file: File) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > return await documentApi.upload(file)</span>
<span class="cstat-no" title="statement not covered" > }</span>
<span class="cstat-no" title="statement not covered" >)</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >export const extractDocument = createAsyncThunk(</span>
<span class="cstat-no" title="statement not covered" > 'document/extract',</span>
<span class="cstat-no" title="statement not covered" > async (documentId: string) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > return await documentApi.extract(documentId)</span>
<span class="cstat-no" title="statement not covered" > }</span>
<span class="cstat-no" title="statement not covered" >)</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >export const analyzeDocument = createAsyncThunk(</span>
<span class="cstat-no" title="statement not covered" > 'document/analyze',</span>
<span class="cstat-no" title="statement not covered" > async (documentId: string) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > return await documentApi.analyze(documentId)</span>
<span class="cstat-no" title="statement not covered" > }</span>
<span class="cstat-no" title="statement not covered" >)</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >export const getContextData = createAsyncThunk(</span>
<span class="cstat-no" title="statement not covered" > 'document/context',</span>
<span class="cstat-no" title="statement not covered" > async (documentId: string) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > return await documentApi.getContext(documentId)</span>
<span class="cstat-no" title="statement not covered" > }</span>
<span class="cstat-no" title="statement not covered" >)</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >export const getConseil = createAsyncThunk(</span>
<span class="cstat-no" title="statement not covered" > 'document/conseil',</span>
<span class="cstat-no" title="statement not covered" > async (documentId: string) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > return await documentApi.getConseil(documentId)</span>
<span class="cstat-no" title="statement not covered" > }</span>
<span class="cstat-no" title="statement not covered" >)</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >const documentSlice = createSlice({</span>
<span class="cstat-no" title="statement not covered" > name: 'document',</span>
<span class="cstat-no" title="statement not covered" > initialState,</span>
<span class="cstat-no" title="statement not covered" > reducers: {</span>
<span class="cstat-no" title="statement not covered" > setCurrentDocument: (state, action: PayloadAction&lt;Document | null&gt;) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > state.currentDocument = action.payload</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > clearResults: (state) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > state.extractionResult = null</span>
<span class="cstat-no" title="statement not covered" > state.analysisResult = null</span>
<span class="cstat-no" title="statement not covered" > state.contextResult = null</span>
<span class="cstat-no" title="statement not covered" > state.conseilResult = null</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > extraReducers: (builder) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > builder</span>
<span class="cstat-no" title="statement not covered" > .addCase(uploadDocument.pending, (state) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > state.loading = true</span>
<span class="cstat-no" title="statement not covered" > state.error = null</span>
<span class="cstat-no" title="statement not covered" > })</span>
<span class="cstat-no" title="statement not covered" > .addCase(uploadDocument.fulfilled, (state, action) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > state.loading = false</span>
<span class="cstat-no" title="statement not covered" > state.documents.push(action.payload)</span>
<span class="cstat-no" title="statement not covered" > state.currentDocument = action.payload</span>
<span class="cstat-no" title="statement not covered" > })</span>
<span class="cstat-no" title="statement not covered" > .addCase(uploadDocument.rejected, (state, action) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > state.loading = false</span>
<span class="cstat-no" title="statement not covered" > state.error = action.error.message || 'Erreur lors du téléversement'</span>
<span class="cstat-no" title="statement not covered" > })</span>
<span class="cstat-no" title="statement not covered" > .addCase(extractDocument.fulfilled, (state, action) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > state.extractionResult = action.payload</span>
<span class="cstat-no" title="statement not covered" > })</span>
<span class="cstat-no" title="statement not covered" > .addCase(analyzeDocument.fulfilled, (state, action) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > state.analysisResult = action.payload</span>
<span class="cstat-no" title="statement not covered" > })</span>
<span class="cstat-no" title="statement not covered" > .addCase(getContextData.fulfilled, (state, action) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > state.contextResult = action.payload</span>
<span class="cstat-no" title="statement not covered" > })</span>
<span class="cstat-no" title="statement not covered" > .addCase(getConseil.fulfilled, (state, action) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > state.conseilResult = action.payload</span>
<span class="cstat-no" title="statement not covered" > })</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" >})</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >export const { setCurrentDocument, clearResults } = documentSlice.actions</span>
<span class="cstat-no" title="statement not covered" >export const documentReducer = documentSlice.reducer</span>
&nbsp;</pre></td></tr></table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,146 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/store</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> src/store</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/114</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/3</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/3</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/114</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<div class="pad1">
<table class="coverage-summary">
<thead>
<tr>
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
</tr>
</thead>
<tbody><tr>
<td class="file low" data-value="appSlice.ts"><a href="appSlice.ts.html">appSlice.ts</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="10" class="abs low">0/10</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="10" class="abs low">0/10</td>
</tr>
<tr>
<td class="file low" data-value="documentSlice.ts"><a href="documentSlice.ts.html">documentSlice.ts</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="87" class="abs low">0/87</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="87" class="abs low">0/87</td>
</tr>
<tr>
<td class="file low" data-value="index.ts"><a href="index.ts.html">index.ts</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="17" class="abs low">0/17</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="17" class="abs low">0/17</td>
</tr>
</tbody>
</table>
</div>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,154 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/store/index.ts</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> / <a href="index.html">src/store</a> index.ts</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/17</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/17</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
<a name='L2'></a><a href='#L2'>2</a>
<a name='L3'></a><a href='#L3'>3</a>
<a name='L4'></a><a href='#L4'>4</a>
<a name='L5'></a><a href='#L5'>5</a>
<a name='L6'></a><a href='#L6'>6</a>
<a name='L7'></a><a href='#L7'>7</a>
<a name='L8'></a><a href='#L8'>8</a>
<a name='L9'></a><a href='#L9'>9</a>
<a name='L10'></a><a href='#L10'>10</a>
<a name='L11'></a><a href='#L11'>11</a>
<a name='L12'></a><a href='#L12'>12</a>
<a name='L13'></a><a href='#L13'>13</a>
<a name='L14'></a><a href='#L14'>14</a>
<a name='L15'></a><a href='#L15'>15</a>
<a name='L16'></a><a href='#L16'>16</a>
<a name='L17'></a><a href='#L17'>17</a>
<a name='L18'></a><a href='#L18'>18</a>
<a name='L19'></a><a href='#L19'>19</a>
<a name='L20'></a><a href='#L20'>20</a>
<a name='L21'></a><a href='#L21'>21</a>
<a name='L22'></a><a href='#L22'>22</a>
<a name='L23'></a><a href='#L23'>23</a>
<a name='L24'></a><a href='#L24'>24</a></td><td class="line-coverage quiet"><span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js"><span class="cstat-no" title="statement not covered" >import { configureStore } from '@reduxjs/toolkit'<span class="fstat-no" title="function not covered" ><span class="branch-0 cbranch-no" title="branch not covered" ></span></span></span>
<span class="cstat-no" title="statement not covered" >import { useDispatch, useSelector } from 'react-redux'</span>
import type { TypedUseSelectorHook } from 'react-redux'
<span class="cstat-no" title="statement not covered" >import { appReducer } from './appSlice'</span>
<span class="cstat-no" title="statement not covered" >import { documentReducer } from './documentSlice'</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >export const store = configureStore({</span>
<span class="cstat-no" title="statement not covered" > reducer: {</span>
<span class="cstat-no" title="statement not covered" > app: appReducer,</span>
<span class="cstat-no" title="statement not covered" > document: documentReducer,</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > middleware: (getDefaultMiddleware) =&gt; getDefaultMiddleware({</span>
<span class="cstat-no" title="statement not covered" > serializableCheck: false,</span>
<span class="cstat-no" title="statement not covered" > immutableCheck: true,</span>
<span class="cstat-no" title="statement not covered" > }),</span>
<span class="cstat-no" title="statement not covered" > devTools: true,</span>
<span class="cstat-no" title="statement not covered" >})</span>
&nbsp;
export type RootState = ReturnType&lt;typeof store.getState&gt;
export type AppDispatch = typeof store.dispatch
&nbsp;
<span class="cstat-no" title="statement not covered" >export const useAppDispatch: () =&gt; AppDispatch = useDispatch</span>
<span class="cstat-no" title="statement not covered" >export const useAppSelector: TypedUseSelectorHook&lt;RootState&gt; = useSelector</span>
&nbsp;</pre></td></tr></table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,116 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/theme</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> src/theme</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/64</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/64</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<div class="pad1">
<table class="coverage-summary">
<thead>
<tr>
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
</tr>
</thead>
<tbody><tr>
<td class="file low" data-value="index.ts"><a href="index.ts.html">index.ts</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="64" class="abs low">0/64</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="64" class="abs low">0/64</td>
</tr>
</tbody>
</table>
</div>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,280 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/theme/index.ts</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> / <a href="index.html">src/theme</a> index.ts</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/64</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/64</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
<a name='L2'></a><a href='#L2'>2</a>
<a name='L3'></a><a href='#L3'>3</a>
<a name='L4'></a><a href='#L4'>4</a>
<a name='L5'></a><a href='#L5'>5</a>
<a name='L6'></a><a href='#L6'>6</a>
<a name='L7'></a><a href='#L7'>7</a>
<a name='L8'></a><a href='#L8'>8</a>
<a name='L9'></a><a href='#L9'>9</a>
<a name='L10'></a><a href='#L10'>10</a>
<a name='L11'></a><a href='#L11'>11</a>
<a name='L12'></a><a href='#L12'>12</a>
<a name='L13'></a><a href='#L13'>13</a>
<a name='L14'></a><a href='#L14'>14</a>
<a name='L15'></a><a href='#L15'>15</a>
<a name='L16'></a><a href='#L16'>16</a>
<a name='L17'></a><a href='#L17'>17</a>
<a name='L18'></a><a href='#L18'>18</a>
<a name='L19'></a><a href='#L19'>19</a>
<a name='L20'></a><a href='#L20'>20</a>
<a name='L21'></a><a href='#L21'>21</a>
<a name='L22'></a><a href='#L22'>22</a>
<a name='L23'></a><a href='#L23'>23</a>
<a name='L24'></a><a href='#L24'>24</a>
<a name='L25'></a><a href='#L25'>25</a>
<a name='L26'></a><a href='#L26'>26</a>
<a name='L27'></a><a href='#L27'>27</a>
<a name='L28'></a><a href='#L28'>28</a>
<a name='L29'></a><a href='#L29'>29</a>
<a name='L30'></a><a href='#L30'>30</a>
<a name='L31'></a><a href='#L31'>31</a>
<a name='L32'></a><a href='#L32'>32</a>
<a name='L33'></a><a href='#L33'>33</a>
<a name='L34'></a><a href='#L34'>34</a>
<a name='L35'></a><a href='#L35'>35</a>
<a name='L36'></a><a href='#L36'>36</a>
<a name='L37'></a><a href='#L37'>37</a>
<a name='L38'></a><a href='#L38'>38</a>
<a name='L39'></a><a href='#L39'>39</a>
<a name='L40'></a><a href='#L40'>40</a>
<a name='L41'></a><a href='#L41'>41</a>
<a name='L42'></a><a href='#L42'>42</a>
<a name='L43'></a><a href='#L43'>43</a>
<a name='L44'></a><a href='#L44'>44</a>
<a name='L45'></a><a href='#L45'>45</a>
<a name='L46'></a><a href='#L46'>46</a>
<a name='L47'></a><a href='#L47'>47</a>
<a name='L48'></a><a href='#L48'>48</a>
<a name='L49'></a><a href='#L49'>49</a>
<a name='L50'></a><a href='#L50'>50</a>
<a name='L51'></a><a href='#L51'>51</a>
<a name='L52'></a><a href='#L52'>52</a>
<a name='L53'></a><a href='#L53'>53</a>
<a name='L54'></a><a href='#L54'>54</a>
<a name='L55'></a><a href='#L55'>55</a>
<a name='L56'></a><a href='#L56'>56</a>
<a name='L57'></a><a href='#L57'>57</a>
<a name='L58'></a><a href='#L58'>58</a>
<a name='L59'></a><a href='#L59'>59</a>
<a name='L60'></a><a href='#L60'>60</a>
<a name='L61'></a><a href='#L61'>61</a>
<a name='L62'></a><a href='#L62'>62</a>
<a name='L63'></a><a href='#L63'>63</a>
<a name='L64'></a><a href='#L64'>64</a>
<a name='L65'></a><a href='#L65'>65</a>
<a name='L66'></a><a href='#L66'>66</a></td><td class="line-coverage quiet"><span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js"><span class="cstat-no" title="statement not covered" >import { createTheme } from '@mui/material/styles'<span class="fstat-no" title="function not covered" ><span class="branch-0 cbranch-no" title="branch not covered" ></span></span></span>
&nbsp;
<span class="cstat-no" title="statement not covered" >export const theme = createTheme({</span>
<span class="cstat-no" title="statement not covered" > palette: {</span>
<span class="cstat-no" title="statement not covered" > mode: 'light',</span>
<span class="cstat-no" title="statement not covered" > background: {</span>
<span class="cstat-no" title="statement not covered" > default: '#ffffff',</span>
<span class="cstat-no" title="statement not covered" > paper: '#ffffff',</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > primary: {</span>
<span class="cstat-no" title="statement not covered" > main: '#1976d2',</span>
<span class="cstat-no" title="statement not covered" > light: '#42a5f5',</span>
<span class="cstat-no" title="statement not covered" > dark: '#1565c0',</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > secondary: {</span>
<span class="cstat-no" title="statement not covered" > main: '#dc004e',</span>
<span class="cstat-no" title="statement not covered" > light: '#ff5983',</span>
<span class="cstat-no" title="statement not covered" > dark: '#9a0036',</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > error: {</span>
<span class="cstat-no" title="statement not covered" > main: '#f44336',</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > warning: {</span>
<span class="cstat-no" title="statement not covered" > main: '#ff9800',</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > info: {</span>
<span class="cstat-no" title="statement not covered" > main: '#2196f3',</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > success: {</span>
<span class="cstat-no" title="statement not covered" > main: '#4caf50',</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > typography: {</span>
<span class="cstat-no" title="statement not covered" > fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',</span>
<span class="cstat-no" title="statement not covered" > h4: {</span>
<span class="cstat-no" title="statement not covered" > fontWeight: 600,</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > h6: {</span>
<span class="cstat-no" title="statement not covered" > fontWeight: 500,</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > components: {</span>
<span class="cstat-no" title="statement not covered" > MuiCssBaseline: {</span>
<span class="cstat-no" title="statement not covered" > styleOverrides: {</span>
<span class="cstat-no" title="statement not covered" > body: {</span>
<span class="cstat-no" title="statement not covered" > backgroundColor: '#ffffff',</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > MuiAppBar: {</span>
<span class="cstat-no" title="statement not covered" > styleOverrides: {</span>
<span class="cstat-no" title="statement not covered" > root: {</span>
<span class="cstat-no" title="statement not covered" > backgroundColor: '#1976d2',</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > MuiPaper: {</span>
<span class="cstat-no" title="statement not covered" > styleOverrides: {</span>
<span class="cstat-no" title="statement not covered" > root: {</span>
<span class="cstat-no" title="statement not covered" > backgroundColor: '#ffffff',</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" >})</span>
&nbsp;</pre></td></tr></table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,116 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/types</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> src/types</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/0</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>1/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>1/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/0</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<div class="pad1">
<table class="coverage-summary">
<thead>
<tr>
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
</tr>
</thead>
<tbody><tr>
<td class="file empty" data-value="index.ts"><a href="index.ts.html">index.ts</a></td>
<td data-value="0" class="pic empty">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct empty">0%</td>
<td data-value="0" class="abs empty">0/0</td>
<td data-value="0" class="pct empty">0%</td>
<td data-value="1" class="abs empty">1/1</td>
<td data-value="0" class="pct empty">0%</td>
<td data-value="1" class="abs empty">1/1</td>
<td data-value="0" class="pct empty">0%</td>
<td data-value="0" class="abs empty">0/0</td>
</tr>
</tbody>
</table>
</div>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,376 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/types/index.ts</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> / <a href="index.html">src/types</a> index.ts</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/0</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>1/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>1/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/0</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
<a name='L2'></a><a href='#L2'>2</a>
<a name='L3'></a><a href='#L3'>3</a>
<a name='L4'></a><a href='#L4'>4</a>
<a name='L5'></a><a href='#L5'>5</a>
<a name='L6'></a><a href='#L6'>6</a>
<a name='L7'></a><a href='#L7'>7</a>
<a name='L8'></a><a href='#L8'>8</a>
<a name='L9'></a><a href='#L9'>9</a>
<a name='L10'></a><a href='#L10'>10</a>
<a name='L11'></a><a href='#L11'>11</a>
<a name='L12'></a><a href='#L12'>12</a>
<a name='L13'></a><a href='#L13'>13</a>
<a name='L14'></a><a href='#L14'>14</a>
<a name='L15'></a><a href='#L15'>15</a>
<a name='L16'></a><a href='#L16'>16</a>
<a name='L17'></a><a href='#L17'>17</a>
<a name='L18'></a><a href='#L18'>18</a>
<a name='L19'></a><a href='#L19'>19</a>
<a name='L20'></a><a href='#L20'>20</a>
<a name='L21'></a><a href='#L21'>21</a>
<a name='L22'></a><a href='#L22'>22</a>
<a name='L23'></a><a href='#L23'>23</a>
<a name='L24'></a><a href='#L24'>24</a>
<a name='L25'></a><a href='#L25'>25</a>
<a name='L26'></a><a href='#L26'>26</a>
<a name='L27'></a><a href='#L27'>27</a>
<a name='L28'></a><a href='#L28'>28</a>
<a name='L29'></a><a href='#L29'>29</a>
<a name='L30'></a><a href='#L30'>30</a>
<a name='L31'></a><a href='#L31'>31</a>
<a name='L32'></a><a href='#L32'>32</a>
<a name='L33'></a><a href='#L33'>33</a>
<a name='L34'></a><a href='#L34'>34</a>
<a name='L35'></a><a href='#L35'>35</a>
<a name='L36'></a><a href='#L36'>36</a>
<a name='L37'></a><a href='#L37'>37</a>
<a name='L38'></a><a href='#L38'>38</a>
<a name='L39'></a><a href='#L39'>39</a>
<a name='L40'></a><a href='#L40'>40</a>
<a name='L41'></a><a href='#L41'>41</a>
<a name='L42'></a><a href='#L42'>42</a>
<a name='L43'></a><a href='#L43'>43</a>
<a name='L44'></a><a href='#L44'>44</a>
<a name='L45'></a><a href='#L45'>45</a>
<a name='L46'></a><a href='#L46'>46</a>
<a name='L47'></a><a href='#L47'>47</a>
<a name='L48'></a><a href='#L48'>48</a>
<a name='L49'></a><a href='#L49'>49</a>
<a name='L50'></a><a href='#L50'>50</a>
<a name='L51'></a><a href='#L51'>51</a>
<a name='L52'></a><a href='#L52'>52</a>
<a name='L53'></a><a href='#L53'>53</a>
<a name='L54'></a><a href='#L54'>54</a>
<a name='L55'></a><a href='#L55'>55</a>
<a name='L56'></a><a href='#L56'>56</a>
<a name='L57'></a><a href='#L57'>57</a>
<a name='L58'></a><a href='#L58'>58</a>
<a name='L59'></a><a href='#L59'>59</a>
<a name='L60'></a><a href='#L60'>60</a>
<a name='L61'></a><a href='#L61'>61</a>
<a name='L62'></a><a href='#L62'>62</a>
<a name='L63'></a><a href='#L63'>63</a>
<a name='L64'></a><a href='#L64'>64</a>
<a name='L65'></a><a href='#L65'>65</a>
<a name='L66'></a><a href='#L66'>66</a>
<a name='L67'></a><a href='#L67'>67</a>
<a name='L68'></a><a href='#L68'>68</a>
<a name='L69'></a><a href='#L69'>69</a>
<a name='L70'></a><a href='#L70'>70</a>
<a name='L71'></a><a href='#L71'>71</a>
<a name='L72'></a><a href='#L72'>72</a>
<a name='L73'></a><a href='#L73'>73</a>
<a name='L74'></a><a href='#L74'>74</a>
<a name='L75'></a><a href='#L75'>75</a>
<a name='L76'></a><a href='#L76'>76</a>
<a name='L77'></a><a href='#L77'>77</a>
<a name='L78'></a><a href='#L78'>78</a>
<a name='L79'></a><a href='#L79'>79</a>
<a name='L80'></a><a href='#L80'>80</a>
<a name='L81'></a><a href='#L81'>81</a>
<a name='L82'></a><a href='#L82'>82</a>
<a name='L83'></a><a href='#L83'>83</a>
<a name='L84'></a><a href='#L84'>84</a>
<a name='L85'></a><a href='#L85'>85</a>
<a name='L86'></a><a href='#L86'>86</a>
<a name='L87'></a><a href='#L87'>87</a>
<a name='L88'></a><a href='#L88'>88</a>
<a name='L89'></a><a href='#L89'>89</a>
<a name='L90'></a><a href='#L90'>90</a>
<a name='L91'></a><a href='#L91'>91</a>
<a name='L92'></a><a href='#L92'>92</a>
<a name='L93'></a><a href='#L93'>93</a>
<a name='L94'></a><a href='#L94'>94</a>
<a name='L95'></a><a href='#L95'>95</a>
<a name='L96'></a><a href='#L96'>96</a>
<a name='L97'></a><a href='#L97'>97</a>
<a name='L98'></a><a href='#L98'>98</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">export interface Document {
id: string
name: string
mimeType: string
functionalType?: string
size: number
uploadDate: Date
status: 'uploading' | 'processing' | 'completed' | 'error'
previewUrl?: string
content?: string
}
&nbsp;
export interface Identity {
id: string
type: 'person' | 'company'
firstName?: string
lastName?: string
companyName?: string
birthDate?: string
nationality?: string
address?: Address
confidence: number
}
&nbsp;
export interface Address {
street: string
city: string
postalCode: string
country: string
coordinates?: { lat: number; lng: number }
}
&nbsp;
export interface Property {
id: string
type: 'house' | 'apartment' | 'land' | 'commercial'
address: Address
surface?: number
cadastralReference?: string
value?: number
}
&nbsp;
export interface Contract {
id: string
type: 'sale' | 'rent' | 'inheritance' | 'donation'
parties: Identity[]
property?: Property
amount?: number
date?: string
clauses: string[]
}
&nbsp;
export interface ExtractionResult {
documentId: string
text: string
language: string
documentType: string
identities: Identity[]
addresses: Address[]
properties: Property[]
contracts: Contract[]
signatures: string[]
confidence: number
}
&nbsp;
export interface AnalysisResult {
documentId: string
documentType: string
isCNI: boolean
country?: string
verificationResult?: {
numberValid: boolean
formatValid: boolean
checksumValid: boolean
}
credibilityScore: number
summary: string
recommendations: string[]
}
&nbsp;
export interface ContextResult {
documentId: string
cadastreData?: Record&lt;string, unknown&gt;
georisquesData?: Record&lt;string, unknown&gt;
geofoncierData?: Record&lt;string, unknown&gt;
bodaccData?: Record&lt;string, unknown&gt;
infogreffeData?: Record&lt;string, unknown&gt;
lastUpdated: Date
}
&nbsp;
export interface ConseilResult {
documentId: string
analysis: string
recommendations: string[]
risks: string[]
nextSteps: string[]
generatedAt: Date
}
&nbsp;</pre></td></tr></table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,847 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/views/AnalyseView.tsx</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> / <a href="index.html">src/views</a> AnalyseView.tsx</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/191</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/191</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
<a name='L2'></a><a href='#L2'>2</a>
<a name='L3'></a><a href='#L3'>3</a>
<a name='L4'></a><a href='#L4'>4</a>
<a name='L5'></a><a href='#L5'>5</a>
<a name='L6'></a><a href='#L6'>6</a>
<a name='L7'></a><a href='#L7'>7</a>
<a name='L8'></a><a href='#L8'>8</a>
<a name='L9'></a><a href='#L9'>9</a>
<a name='L10'></a><a href='#L10'>10</a>
<a name='L11'></a><a href='#L11'>11</a>
<a name='L12'></a><a href='#L12'>12</a>
<a name='L13'></a><a href='#L13'>13</a>
<a name='L14'></a><a href='#L14'>14</a>
<a name='L15'></a><a href='#L15'>15</a>
<a name='L16'></a><a href='#L16'>16</a>
<a name='L17'></a><a href='#L17'>17</a>
<a name='L18'></a><a href='#L18'>18</a>
<a name='L19'></a><a href='#L19'>19</a>
<a name='L20'></a><a href='#L20'>20</a>
<a name='L21'></a><a href='#L21'>21</a>
<a name='L22'></a><a href='#L22'>22</a>
<a name='L23'></a><a href='#L23'>23</a>
<a name='L24'></a><a href='#L24'>24</a>
<a name='L25'></a><a href='#L25'>25</a>
<a name='L26'></a><a href='#L26'>26</a>
<a name='L27'></a><a href='#L27'>27</a>
<a name='L28'></a><a href='#L28'>28</a>
<a name='L29'></a><a href='#L29'>29</a>
<a name='L30'></a><a href='#L30'>30</a>
<a name='L31'></a><a href='#L31'>31</a>
<a name='L32'></a><a href='#L32'>32</a>
<a name='L33'></a><a href='#L33'>33</a>
<a name='L34'></a><a href='#L34'>34</a>
<a name='L35'></a><a href='#L35'>35</a>
<a name='L36'></a><a href='#L36'>36</a>
<a name='L37'></a><a href='#L37'>37</a>
<a name='L38'></a><a href='#L38'>38</a>
<a name='L39'></a><a href='#L39'>39</a>
<a name='L40'></a><a href='#L40'>40</a>
<a name='L41'></a><a href='#L41'>41</a>
<a name='L42'></a><a href='#L42'>42</a>
<a name='L43'></a><a href='#L43'>43</a>
<a name='L44'></a><a href='#L44'>44</a>
<a name='L45'></a><a href='#L45'>45</a>
<a name='L46'></a><a href='#L46'>46</a>
<a name='L47'></a><a href='#L47'>47</a>
<a name='L48'></a><a href='#L48'>48</a>
<a name='L49'></a><a href='#L49'>49</a>
<a name='L50'></a><a href='#L50'>50</a>
<a name='L51'></a><a href='#L51'>51</a>
<a name='L52'></a><a href='#L52'>52</a>
<a name='L53'></a><a href='#L53'>53</a>
<a name='L54'></a><a href='#L54'>54</a>
<a name='L55'></a><a href='#L55'>55</a>
<a name='L56'></a><a href='#L56'>56</a>
<a name='L57'></a><a href='#L57'>57</a>
<a name='L58'></a><a href='#L58'>58</a>
<a name='L59'></a><a href='#L59'>59</a>
<a name='L60'></a><a href='#L60'>60</a>
<a name='L61'></a><a href='#L61'>61</a>
<a name='L62'></a><a href='#L62'>62</a>
<a name='L63'></a><a href='#L63'>63</a>
<a name='L64'></a><a href='#L64'>64</a>
<a name='L65'></a><a href='#L65'>65</a>
<a name='L66'></a><a href='#L66'>66</a>
<a name='L67'></a><a href='#L67'>67</a>
<a name='L68'></a><a href='#L68'>68</a>
<a name='L69'></a><a href='#L69'>69</a>
<a name='L70'></a><a href='#L70'>70</a>
<a name='L71'></a><a href='#L71'>71</a>
<a name='L72'></a><a href='#L72'>72</a>
<a name='L73'></a><a href='#L73'>73</a>
<a name='L74'></a><a href='#L74'>74</a>
<a name='L75'></a><a href='#L75'>75</a>
<a name='L76'></a><a href='#L76'>76</a>
<a name='L77'></a><a href='#L77'>77</a>
<a name='L78'></a><a href='#L78'>78</a>
<a name='L79'></a><a href='#L79'>79</a>
<a name='L80'></a><a href='#L80'>80</a>
<a name='L81'></a><a href='#L81'>81</a>
<a name='L82'></a><a href='#L82'>82</a>
<a name='L83'></a><a href='#L83'>83</a>
<a name='L84'></a><a href='#L84'>84</a>
<a name='L85'></a><a href='#L85'>85</a>
<a name='L86'></a><a href='#L86'>86</a>
<a name='L87'></a><a href='#L87'>87</a>
<a name='L88'></a><a href='#L88'>88</a>
<a name='L89'></a><a href='#L89'>89</a>
<a name='L90'></a><a href='#L90'>90</a>
<a name='L91'></a><a href='#L91'>91</a>
<a name='L92'></a><a href='#L92'>92</a>
<a name='L93'></a><a href='#L93'>93</a>
<a name='L94'></a><a href='#L94'>94</a>
<a name='L95'></a><a href='#L95'>95</a>
<a name='L96'></a><a href='#L96'>96</a>
<a name='L97'></a><a href='#L97'>97</a>
<a name='L98'></a><a href='#L98'>98</a>
<a name='L99'></a><a href='#L99'>99</a>
<a name='L100'></a><a href='#L100'>100</a>
<a name='L101'></a><a href='#L101'>101</a>
<a name='L102'></a><a href='#L102'>102</a>
<a name='L103'></a><a href='#L103'>103</a>
<a name='L104'></a><a href='#L104'>104</a>
<a name='L105'></a><a href='#L105'>105</a>
<a name='L106'></a><a href='#L106'>106</a>
<a name='L107'></a><a href='#L107'>107</a>
<a name='L108'></a><a href='#L108'>108</a>
<a name='L109'></a><a href='#L109'>109</a>
<a name='L110'></a><a href='#L110'>110</a>
<a name='L111'></a><a href='#L111'>111</a>
<a name='L112'></a><a href='#L112'>112</a>
<a name='L113'></a><a href='#L113'>113</a>
<a name='L114'></a><a href='#L114'>114</a>
<a name='L115'></a><a href='#L115'>115</a>
<a name='L116'></a><a href='#L116'>116</a>
<a name='L117'></a><a href='#L117'>117</a>
<a name='L118'></a><a href='#L118'>118</a>
<a name='L119'></a><a href='#L119'>119</a>
<a name='L120'></a><a href='#L120'>120</a>
<a name='L121'></a><a href='#L121'>121</a>
<a name='L122'></a><a href='#L122'>122</a>
<a name='L123'></a><a href='#L123'>123</a>
<a name='L124'></a><a href='#L124'>124</a>
<a name='L125'></a><a href='#L125'>125</a>
<a name='L126'></a><a href='#L126'>126</a>
<a name='L127'></a><a href='#L127'>127</a>
<a name='L128'></a><a href='#L128'>128</a>
<a name='L129'></a><a href='#L129'>129</a>
<a name='L130'></a><a href='#L130'>130</a>
<a name='L131'></a><a href='#L131'>131</a>
<a name='L132'></a><a href='#L132'>132</a>
<a name='L133'></a><a href='#L133'>133</a>
<a name='L134'></a><a href='#L134'>134</a>
<a name='L135'></a><a href='#L135'>135</a>
<a name='L136'></a><a href='#L136'>136</a>
<a name='L137'></a><a href='#L137'>137</a>
<a name='L138'></a><a href='#L138'>138</a>
<a name='L139'></a><a href='#L139'>139</a>
<a name='L140'></a><a href='#L140'>140</a>
<a name='L141'></a><a href='#L141'>141</a>
<a name='L142'></a><a href='#L142'>142</a>
<a name='L143'></a><a href='#L143'>143</a>
<a name='L144'></a><a href='#L144'>144</a>
<a name='L145'></a><a href='#L145'>145</a>
<a name='L146'></a><a href='#L146'>146</a>
<a name='L147'></a><a href='#L147'>147</a>
<a name='L148'></a><a href='#L148'>148</a>
<a name='L149'></a><a href='#L149'>149</a>
<a name='L150'></a><a href='#L150'>150</a>
<a name='L151'></a><a href='#L151'>151</a>
<a name='L152'></a><a href='#L152'>152</a>
<a name='L153'></a><a href='#L153'>153</a>
<a name='L154'></a><a href='#L154'>154</a>
<a name='L155'></a><a href='#L155'>155</a>
<a name='L156'></a><a href='#L156'>156</a>
<a name='L157'></a><a href='#L157'>157</a>
<a name='L158'></a><a href='#L158'>158</a>
<a name='L159'></a><a href='#L159'>159</a>
<a name='L160'></a><a href='#L160'>160</a>
<a name='L161'></a><a href='#L161'>161</a>
<a name='L162'></a><a href='#L162'>162</a>
<a name='L163'></a><a href='#L163'>163</a>
<a name='L164'></a><a href='#L164'>164</a>
<a name='L165'></a><a href='#L165'>165</a>
<a name='L166'></a><a href='#L166'>166</a>
<a name='L167'></a><a href='#L167'>167</a>
<a name='L168'></a><a href='#L168'>168</a>
<a name='L169'></a><a href='#L169'>169</a>
<a name='L170'></a><a href='#L170'>170</a>
<a name='L171'></a><a href='#L171'>171</a>
<a name='L172'></a><a href='#L172'>172</a>
<a name='L173'></a><a href='#L173'>173</a>
<a name='L174'></a><a href='#L174'>174</a>
<a name='L175'></a><a href='#L175'>175</a>
<a name='L176'></a><a href='#L176'>176</a>
<a name='L177'></a><a href='#L177'>177</a>
<a name='L178'></a><a href='#L178'>178</a>
<a name='L179'></a><a href='#L179'>179</a>
<a name='L180'></a><a href='#L180'>180</a>
<a name='L181'></a><a href='#L181'>181</a>
<a name='L182'></a><a href='#L182'>182</a>
<a name='L183'></a><a href='#L183'>183</a>
<a name='L184'></a><a href='#L184'>184</a>
<a name='L185'></a><a href='#L185'>185</a>
<a name='L186'></a><a href='#L186'>186</a>
<a name='L187'></a><a href='#L187'>187</a>
<a name='L188'></a><a href='#L188'>188</a>
<a name='L189'></a><a href='#L189'>189</a>
<a name='L190'></a><a href='#L190'>190</a>
<a name='L191'></a><a href='#L191'>191</a>
<a name='L192'></a><a href='#L192'>192</a>
<a name='L193'></a><a href='#L193'>193</a>
<a name='L194'></a><a href='#L194'>194</a>
<a name='L195'></a><a href='#L195'>195</a>
<a name='L196'></a><a href='#L196'>196</a>
<a name='L197'></a><a href='#L197'>197</a>
<a name='L198'></a><a href='#L198'>198</a>
<a name='L199'></a><a href='#L199'>199</a>
<a name='L200'></a><a href='#L200'>200</a>
<a name='L201'></a><a href='#L201'>201</a>
<a name='L202'></a><a href='#L202'>202</a>
<a name='L203'></a><a href='#L203'>203</a>
<a name='L204'></a><a href='#L204'>204</a>
<a name='L205'></a><a href='#L205'>205</a>
<a name='L206'></a><a href='#L206'>206</a>
<a name='L207'></a><a href='#L207'>207</a>
<a name='L208'></a><a href='#L208'>208</a>
<a name='L209'></a><a href='#L209'>209</a>
<a name='L210'></a><a href='#L210'>210</a>
<a name='L211'></a><a href='#L211'>211</a>
<a name='L212'></a><a href='#L212'>212</a>
<a name='L213'></a><a href='#L213'>213</a>
<a name='L214'></a><a href='#L214'>214</a>
<a name='L215'></a><a href='#L215'>215</a>
<a name='L216'></a><a href='#L216'>216</a>
<a name='L217'></a><a href='#L217'>217</a>
<a name='L218'></a><a href='#L218'>218</a>
<a name='L219'></a><a href='#L219'>219</a>
<a name='L220'></a><a href='#L220'>220</a>
<a name='L221'></a><a href='#L221'>221</a>
<a name='L222'></a><a href='#L222'>222</a>
<a name='L223'></a><a href='#L223'>223</a>
<a name='L224'></a><a href='#L224'>224</a>
<a name='L225'></a><a href='#L225'>225</a>
<a name='L226'></a><a href='#L226'>226</a>
<a name='L227'></a><a href='#L227'>227</a>
<a name='L228'></a><a href='#L228'>228</a>
<a name='L229'></a><a href='#L229'>229</a>
<a name='L230'></a><a href='#L230'>230</a>
<a name='L231'></a><a href='#L231'>231</a>
<a name='L232'></a><a href='#L232'>232</a>
<a name='L233'></a><a href='#L233'>233</a>
<a name='L234'></a><a href='#L234'>234</a>
<a name='L235'></a><a href='#L235'>235</a>
<a name='L236'></a><a href='#L236'>236</a>
<a name='L237'></a><a href='#L237'>237</a>
<a name='L238'></a><a href='#L238'>238</a>
<a name='L239'></a><a href='#L239'>239</a>
<a name='L240'></a><a href='#L240'>240</a>
<a name='L241'></a><a href='#L241'>241</a>
<a name='L242'></a><a href='#L242'>242</a>
<a name='L243'></a><a href='#L243'>243</a>
<a name='L244'></a><a href='#L244'>244</a>
<a name='L245'></a><a href='#L245'>245</a>
<a name='L246'></a><a href='#L246'>246</a>
<a name='L247'></a><a href='#L247'>247</a>
<a name='L248'></a><a href='#L248'>248</a>
<a name='L249'></a><a href='#L249'>249</a>
<a name='L250'></a><a href='#L250'>250</a>
<a name='L251'></a><a href='#L251'>251</a>
<a name='L252'></a><a href='#L252'>252</a>
<a name='L253'></a><a href='#L253'>253</a>
<a name='L254'></a><a href='#L254'>254</a>
<a name='L255'></a><a href='#L255'>255</a></td><td class="line-coverage quiet"><span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js"><span class="cstat-no" title="statement not covered" >import { useEffect } from 'react'<span class="fstat-no" title="function not covered" ><span class="branch-0 cbranch-no" title="branch not covered" ></span></span></span>
<span class="cstat-no" title="statement not covered" >import {</span>
Box,
Typography,
Paper,
Card,
CardContent,
Chip,
List,
ListItem,
ListItemText,
ListItemIcon,
Alert,
LinearProgress,
} from '@mui/material'
<span class="cstat-no" title="statement not covered" >import {</span>
CheckCircle,
Error,
Warning,
Flag,
Security,
Assessment,
Info,
} from '@mui/icons-material'
import type { ChipProps, LinearProgressProps } from '@mui/material'
<span class="cstat-no" title="statement not covered" >import { useAppDispatch, useAppSelector } from '../store'</span>
<span class="cstat-no" title="statement not covered" >import { analyzeDocument } from '../store/documentSlice'</span>
<span class="cstat-no" title="statement not covered" >import { Layout } from '../components/Layout'</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >export default function AnalyseView() {</span>
<span class="cstat-no" title="statement not covered" > const dispatch = useAppDispatch()</span>
<span class="cstat-no" title="statement not covered" > const { currentDocument, analysisResult, loading } = useAppSelector(</span>
<span class="cstat-no" title="statement not covered" > (state) =&gt; state.document</span>
<span class="cstat-no" title="statement not covered" > )</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > useEffect(() =&gt; {</span>
<span class="cstat-no" title="statement not covered" > if (currentDocument &amp;&amp; !analysisResult) {</span>
<span class="cstat-no" title="statement not covered" > dispatch(analyzeDocument(currentDocument.id))</span>
<span class="cstat-no" title="statement not covered" > }</span>
<span class="cstat-no" title="statement not covered" > }, [currentDocument, analysisResult, dispatch])</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > if (!currentDocument) {</span>
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;Layout&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Alert severity="info"&gt;</span>
Veuillez d'abord téléverser et sélectionner un document.
<span class="cstat-no" title="statement not covered" > &lt;/Alert&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Layout&gt;</span>
)
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > if (loading) {</span>
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;Layout&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', mt: 4 }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;LinearProgress sx={{ width: '100%', mb: 2 }} /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography&gt;Analyse en cours...&lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Layout&gt;</span>
)
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > if (!analysisResult) {</span>
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;Layout&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Alert severity="warning"&gt;</span>
Aucun résultat d'analyse disponible.
<span class="cstat-no" title="statement not covered" > &lt;/Alert&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Layout&gt;</span>
)
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > const getScoreColor = (score: number): ChipProps['color'] =&gt; {</span>
<span class="cstat-no" title="statement not covered" > if (score &gt;= 0.8) return 'success'</span>
<span class="cstat-no" title="statement not covered" > if (score &gt;= 0.6) return 'warning'</span>
<span class="cstat-no" title="statement not covered" > return 'error'</span>
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > const getScoreIcon = (score: number) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > if (score &gt;= 0.8) return &lt;CheckCircle color="success" /&gt;</span>
<span class="cstat-no" title="statement not covered" > if (score &gt;= 0.6) return &lt;Warning color="warning" /&gt;</span>
<span class="cstat-no" title="statement not covered" > return &lt;Error color="error" /&gt;</span>
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;Layout&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h4" gutterBottom&gt;</span>
Analyse du document
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}&gt;</span>
{/* Résumé général */}
<span class="cstat-no" title="statement not covered" > &lt;Paper sx={{ p: 2 }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
Résumé de l'analyse
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', gap: 2, flexWrap: 'wrap', alignItems: 'center' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > icon={&lt;Assessment /&gt;}</span>
<span class="cstat-no" title="statement not covered" > label={`Score de vraisemblance: ${(analysisResult.credibilityScore * 100).toFixed(1)}%`}</span>
<span class="cstat-no" title="statement not covered" > color={getScoreColor(analysisResult.credibilityScore)}</span>
<span class="cstat-no" title="statement not covered" > variant="filled"</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > icon={&lt;Info /&gt;}</span>
<span class="cstat-no" title="statement not covered" > label={`Type: ${analysisResult.documentType}`}</span>
<span class="cstat-no" title="statement not covered" > color="primary"</span>
<span class="cstat-no" title="statement not covered" > variant="outlined"</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > {analysisResult.isCNI &amp;&amp; (</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > icon={&lt;Flag /&gt;}</span>
<span class="cstat-no" title="statement not covered" > label={`Pays: ${analysisResult.country}`}</span>
<span class="cstat-no" title="statement not covered" > color="secondary"</span>
<span class="cstat-no" title="statement not covered" > variant="outlined"</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
)}
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Paper&gt;</span>
&nbsp;
{/* Cas CNI */}
<span class="cstat-no" title="statement not covered" > {analysisResult.isCNI &amp;&amp; (</span>
<span class="cstat-no" title="statement not covered" > &lt;Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Security sx={{ mr: 1, verticalAlign: 'middle' }} /&gt;</span>
Vérification CNI
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > {analysisResult.verificationResult &amp;&amp; (</span>
<span class="cstat-no" title="statement not covered" > &lt;List&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItem&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItemIcon&gt;</span>
<span class="cstat-no" title="statement not covered" > {analysisResult.verificationResult.numberValid ? (</span>
<span class="cstat-no" title="statement not covered" > &lt;CheckCircle color="success" /&gt;</span>
) : (
<span class="cstat-no" title="statement not covered" > &lt;Error color="error" /&gt;</span>
)}
<span class="cstat-no" title="statement not covered" > &lt;/ListItemIcon&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItemText</span>
<span class="cstat-no" title="statement not covered" > primary="Numéro valide"</span>
<span class="cstat-no" title="statement not covered" > secondary={</span>
<span class="cstat-no" title="statement not covered" > analysisResult.verificationResult.numberValid</span>
<span class="cstat-no" title="statement not covered" > ? 'Le numéro de CNI est valide'</span>
<span class="cstat-no" title="statement not covered" > : 'Le numéro de CNI est invalide'</span>
}
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/ListItem&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItem&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItemIcon&gt;</span>
<span class="cstat-no" title="statement not covered" > {analysisResult.verificationResult.formatValid ? (</span>
<span class="cstat-no" title="statement not covered" > &lt;CheckCircle color="success" /&gt;</span>
) : (
<span class="cstat-no" title="statement not covered" > &lt;Error color="error" /&gt;</span>
)}
<span class="cstat-no" title="statement not covered" > &lt;/ListItemIcon&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItemText</span>
<span class="cstat-no" title="statement not covered" > primary="Format valide"</span>
<span class="cstat-no" title="statement not covered" > secondary={</span>
<span class="cstat-no" title="statement not covered" > analysisResult.verificationResult.formatValid</span>
<span class="cstat-no" title="statement not covered" > ? 'Le format du numéro est correct'</span>
<span class="cstat-no" title="statement not covered" > : 'Le format du numéro est incorrect'</span>
}
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/ListItem&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItem&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItemIcon&gt;</span>
<span class="cstat-no" title="statement not covered" > {analysisResult.verificationResult.checksumValid ? (</span>
<span class="cstat-no" title="statement not covered" > &lt;CheckCircle color="success" /&gt;</span>
) : (
<span class="cstat-no" title="statement not covered" > &lt;Error color="error" /&gt;</span>
)}
<span class="cstat-no" title="statement not covered" > &lt;/ListItemIcon&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItemText</span>
<span class="cstat-no" title="statement not covered" > primary="Checksum valide"</span>
<span class="cstat-no" title="statement not covered" > secondary={</span>
<span class="cstat-no" title="statement not covered" > analysisResult.verificationResult.checksumValid</span>
<span class="cstat-no" title="statement not covered" > ? 'La somme de contrôle est correcte'</span>
<span class="cstat-no" title="statement not covered" > : 'La somme de contrôle est incorrecte'</span>
}
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/ListItem&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/List&gt;</span>
)}
<span class="cstat-no" title="statement not covered" > &lt;/CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Card&gt;</span>
)}
&nbsp;
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', gap: 3, flexWrap: 'wrap' }}&gt;</span>
{/* Score de vraisemblance */}
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ flex: '1 1 300px' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
Score de vraisemblance
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}&gt;</span>
<span class="cstat-no" title="statement not covered" > {getScoreIcon(analysisResult.credibilityScore)}</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h4" sx={{ ml: 2 }}&gt;</span>
<span class="cstat-no" title="statement not covered" > {(analysisResult.credibilityScore * 100).toFixed(1)}%</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;LinearProgress</span>
<span class="cstat-no" title="statement not covered" > variant="determinate"</span>
<span class="cstat-no" title="statement not covered" > value={analysisResult.credibilityScore * 100}</span>
<span class="cstat-no" title="statement not covered" > color={getScoreColor(analysisResult.credibilityScore) as LinearProgressProps['color']}</span>
<span class="cstat-no" title="statement not covered" > sx={{ height: 10, borderRadius: 5 }}</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}&gt;</span>
<span class="cstat-no" title="statement not covered" > {analysisResult.credibilityScore &gt;= 0.8</span>
<span class="cstat-no" title="statement not covered" > ? 'Document très fiable'</span>
<span class="cstat-no" title="statement not covered" > : analysisResult.credibilityScore &gt;= 0.6</span>
<span class="cstat-no" title="statement not covered" > ? 'Document moyennement fiable'</span>
<span class="cstat-no" title="statement not covered" > : 'Document peu fiable - vérification recommandée'}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
&nbsp;
{/* Synthèse */}
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ flex: '1 1 300px' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
Synthèse
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="body1" sx={{ whiteSpace: 'pre-wrap' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > {analysisResult.summary}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
&nbsp;
{/* Recommandations */}
<span class="cstat-no" title="statement not covered" > &lt;Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
Recommandations
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;List&gt;</span>
<span class="cstat-no" title="statement not covered" > {analysisResult.recommendations.map((recommendation, index) =&gt; (</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItem key={index}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItemIcon&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Info color="primary" /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/ListItemIcon&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItemText primary={recommendation} /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/ListItem&gt;</span>
<span class="cstat-no" title="statement not covered" > ))}</span>
<span class="cstat-no" title="statement not covered" > &lt;/List&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Layout&gt;</span>
)
<span class="cstat-no" title="statement not covered" >}</span></pre></td></tr></table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,814 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/views/ConseilView.tsx</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> / <a href="index.html">src/views</a> ConseilView.tsx</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/187</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/187</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
<a name='L2'></a><a href='#L2'>2</a>
<a name='L3'></a><a href='#L3'>3</a>
<a name='L4'></a><a href='#L4'>4</a>
<a name='L5'></a><a href='#L5'>5</a>
<a name='L6'></a><a href='#L6'>6</a>
<a name='L7'></a><a href='#L7'>7</a>
<a name='L8'></a><a href='#L8'>8</a>
<a name='L9'></a><a href='#L9'>9</a>
<a name='L10'></a><a href='#L10'>10</a>
<a name='L11'></a><a href='#L11'>11</a>
<a name='L12'></a><a href='#L12'>12</a>
<a name='L13'></a><a href='#L13'>13</a>
<a name='L14'></a><a href='#L14'>14</a>
<a name='L15'></a><a href='#L15'>15</a>
<a name='L16'></a><a href='#L16'>16</a>
<a name='L17'></a><a href='#L17'>17</a>
<a name='L18'></a><a href='#L18'>18</a>
<a name='L19'></a><a href='#L19'>19</a>
<a name='L20'></a><a href='#L20'>20</a>
<a name='L21'></a><a href='#L21'>21</a>
<a name='L22'></a><a href='#L22'>22</a>
<a name='L23'></a><a href='#L23'>23</a>
<a name='L24'></a><a href='#L24'>24</a>
<a name='L25'></a><a href='#L25'>25</a>
<a name='L26'></a><a href='#L26'>26</a>
<a name='L27'></a><a href='#L27'>27</a>
<a name='L28'></a><a href='#L28'>28</a>
<a name='L29'></a><a href='#L29'>29</a>
<a name='L30'></a><a href='#L30'>30</a>
<a name='L31'></a><a href='#L31'>31</a>
<a name='L32'></a><a href='#L32'>32</a>
<a name='L33'></a><a href='#L33'>33</a>
<a name='L34'></a><a href='#L34'>34</a>
<a name='L35'></a><a href='#L35'>35</a>
<a name='L36'></a><a href='#L36'>36</a>
<a name='L37'></a><a href='#L37'>37</a>
<a name='L38'></a><a href='#L38'>38</a>
<a name='L39'></a><a href='#L39'>39</a>
<a name='L40'></a><a href='#L40'>40</a>
<a name='L41'></a><a href='#L41'>41</a>
<a name='L42'></a><a href='#L42'>42</a>
<a name='L43'></a><a href='#L43'>43</a>
<a name='L44'></a><a href='#L44'>44</a>
<a name='L45'></a><a href='#L45'>45</a>
<a name='L46'></a><a href='#L46'>46</a>
<a name='L47'></a><a href='#L47'>47</a>
<a name='L48'></a><a href='#L48'>48</a>
<a name='L49'></a><a href='#L49'>49</a>
<a name='L50'></a><a href='#L50'>50</a>
<a name='L51'></a><a href='#L51'>51</a>
<a name='L52'></a><a href='#L52'>52</a>
<a name='L53'></a><a href='#L53'>53</a>
<a name='L54'></a><a href='#L54'>54</a>
<a name='L55'></a><a href='#L55'>55</a>
<a name='L56'></a><a href='#L56'>56</a>
<a name='L57'></a><a href='#L57'>57</a>
<a name='L58'></a><a href='#L58'>58</a>
<a name='L59'></a><a href='#L59'>59</a>
<a name='L60'></a><a href='#L60'>60</a>
<a name='L61'></a><a href='#L61'>61</a>
<a name='L62'></a><a href='#L62'>62</a>
<a name='L63'></a><a href='#L63'>63</a>
<a name='L64'></a><a href='#L64'>64</a>
<a name='L65'></a><a href='#L65'>65</a>
<a name='L66'></a><a href='#L66'>66</a>
<a name='L67'></a><a href='#L67'>67</a>
<a name='L68'></a><a href='#L68'>68</a>
<a name='L69'></a><a href='#L69'>69</a>
<a name='L70'></a><a href='#L70'>70</a>
<a name='L71'></a><a href='#L71'>71</a>
<a name='L72'></a><a href='#L72'>72</a>
<a name='L73'></a><a href='#L73'>73</a>
<a name='L74'></a><a href='#L74'>74</a>
<a name='L75'></a><a href='#L75'>75</a>
<a name='L76'></a><a href='#L76'>76</a>
<a name='L77'></a><a href='#L77'>77</a>
<a name='L78'></a><a href='#L78'>78</a>
<a name='L79'></a><a href='#L79'>79</a>
<a name='L80'></a><a href='#L80'>80</a>
<a name='L81'></a><a href='#L81'>81</a>
<a name='L82'></a><a href='#L82'>82</a>
<a name='L83'></a><a href='#L83'>83</a>
<a name='L84'></a><a href='#L84'>84</a>
<a name='L85'></a><a href='#L85'>85</a>
<a name='L86'></a><a href='#L86'>86</a>
<a name='L87'></a><a href='#L87'>87</a>
<a name='L88'></a><a href='#L88'>88</a>
<a name='L89'></a><a href='#L89'>89</a>
<a name='L90'></a><a href='#L90'>90</a>
<a name='L91'></a><a href='#L91'>91</a>
<a name='L92'></a><a href='#L92'>92</a>
<a name='L93'></a><a href='#L93'>93</a>
<a name='L94'></a><a href='#L94'>94</a>
<a name='L95'></a><a href='#L95'>95</a>
<a name='L96'></a><a href='#L96'>96</a>
<a name='L97'></a><a href='#L97'>97</a>
<a name='L98'></a><a href='#L98'>98</a>
<a name='L99'></a><a href='#L99'>99</a>
<a name='L100'></a><a href='#L100'>100</a>
<a name='L101'></a><a href='#L101'>101</a>
<a name='L102'></a><a href='#L102'>102</a>
<a name='L103'></a><a href='#L103'>103</a>
<a name='L104'></a><a href='#L104'>104</a>
<a name='L105'></a><a href='#L105'>105</a>
<a name='L106'></a><a href='#L106'>106</a>
<a name='L107'></a><a href='#L107'>107</a>
<a name='L108'></a><a href='#L108'>108</a>
<a name='L109'></a><a href='#L109'>109</a>
<a name='L110'></a><a href='#L110'>110</a>
<a name='L111'></a><a href='#L111'>111</a>
<a name='L112'></a><a href='#L112'>112</a>
<a name='L113'></a><a href='#L113'>113</a>
<a name='L114'></a><a href='#L114'>114</a>
<a name='L115'></a><a href='#L115'>115</a>
<a name='L116'></a><a href='#L116'>116</a>
<a name='L117'></a><a href='#L117'>117</a>
<a name='L118'></a><a href='#L118'>118</a>
<a name='L119'></a><a href='#L119'>119</a>
<a name='L120'></a><a href='#L120'>120</a>
<a name='L121'></a><a href='#L121'>121</a>
<a name='L122'></a><a href='#L122'>122</a>
<a name='L123'></a><a href='#L123'>123</a>
<a name='L124'></a><a href='#L124'>124</a>
<a name='L125'></a><a href='#L125'>125</a>
<a name='L126'></a><a href='#L126'>126</a>
<a name='L127'></a><a href='#L127'>127</a>
<a name='L128'></a><a href='#L128'>128</a>
<a name='L129'></a><a href='#L129'>129</a>
<a name='L130'></a><a href='#L130'>130</a>
<a name='L131'></a><a href='#L131'>131</a>
<a name='L132'></a><a href='#L132'>132</a>
<a name='L133'></a><a href='#L133'>133</a>
<a name='L134'></a><a href='#L134'>134</a>
<a name='L135'></a><a href='#L135'>135</a>
<a name='L136'></a><a href='#L136'>136</a>
<a name='L137'></a><a href='#L137'>137</a>
<a name='L138'></a><a href='#L138'>138</a>
<a name='L139'></a><a href='#L139'>139</a>
<a name='L140'></a><a href='#L140'>140</a>
<a name='L141'></a><a href='#L141'>141</a>
<a name='L142'></a><a href='#L142'>142</a>
<a name='L143'></a><a href='#L143'>143</a>
<a name='L144'></a><a href='#L144'>144</a>
<a name='L145'></a><a href='#L145'>145</a>
<a name='L146'></a><a href='#L146'>146</a>
<a name='L147'></a><a href='#L147'>147</a>
<a name='L148'></a><a href='#L148'>148</a>
<a name='L149'></a><a href='#L149'>149</a>
<a name='L150'></a><a href='#L150'>150</a>
<a name='L151'></a><a href='#L151'>151</a>
<a name='L152'></a><a href='#L152'>152</a>
<a name='L153'></a><a href='#L153'>153</a>
<a name='L154'></a><a href='#L154'>154</a>
<a name='L155'></a><a href='#L155'>155</a>
<a name='L156'></a><a href='#L156'>156</a>
<a name='L157'></a><a href='#L157'>157</a>
<a name='L158'></a><a href='#L158'>158</a>
<a name='L159'></a><a href='#L159'>159</a>
<a name='L160'></a><a href='#L160'>160</a>
<a name='L161'></a><a href='#L161'>161</a>
<a name='L162'></a><a href='#L162'>162</a>
<a name='L163'></a><a href='#L163'>163</a>
<a name='L164'></a><a href='#L164'>164</a>
<a name='L165'></a><a href='#L165'>165</a>
<a name='L166'></a><a href='#L166'>166</a>
<a name='L167'></a><a href='#L167'>167</a>
<a name='L168'></a><a href='#L168'>168</a>
<a name='L169'></a><a href='#L169'>169</a>
<a name='L170'></a><a href='#L170'>170</a>
<a name='L171'></a><a href='#L171'>171</a>
<a name='L172'></a><a href='#L172'>172</a>
<a name='L173'></a><a href='#L173'>173</a>
<a name='L174'></a><a href='#L174'>174</a>
<a name='L175'></a><a href='#L175'>175</a>
<a name='L176'></a><a href='#L176'>176</a>
<a name='L177'></a><a href='#L177'>177</a>
<a name='L178'></a><a href='#L178'>178</a>
<a name='L179'></a><a href='#L179'>179</a>
<a name='L180'></a><a href='#L180'>180</a>
<a name='L181'></a><a href='#L181'>181</a>
<a name='L182'></a><a href='#L182'>182</a>
<a name='L183'></a><a href='#L183'>183</a>
<a name='L184'></a><a href='#L184'>184</a>
<a name='L185'></a><a href='#L185'>185</a>
<a name='L186'></a><a href='#L186'>186</a>
<a name='L187'></a><a href='#L187'>187</a>
<a name='L188'></a><a href='#L188'>188</a>
<a name='L189'></a><a href='#L189'>189</a>
<a name='L190'></a><a href='#L190'>190</a>
<a name='L191'></a><a href='#L191'>191</a>
<a name='L192'></a><a href='#L192'>192</a>
<a name='L193'></a><a href='#L193'>193</a>
<a name='L194'></a><a href='#L194'>194</a>
<a name='L195'></a><a href='#L195'>195</a>
<a name='L196'></a><a href='#L196'>196</a>
<a name='L197'></a><a href='#L197'>197</a>
<a name='L198'></a><a href='#L198'>198</a>
<a name='L199'></a><a href='#L199'>199</a>
<a name='L200'></a><a href='#L200'>200</a>
<a name='L201'></a><a href='#L201'>201</a>
<a name='L202'></a><a href='#L202'>202</a>
<a name='L203'></a><a href='#L203'>203</a>
<a name='L204'></a><a href='#L204'>204</a>
<a name='L205'></a><a href='#L205'>205</a>
<a name='L206'></a><a href='#L206'>206</a>
<a name='L207'></a><a href='#L207'>207</a>
<a name='L208'></a><a href='#L208'>208</a>
<a name='L209'></a><a href='#L209'>209</a>
<a name='L210'></a><a href='#L210'>210</a>
<a name='L211'></a><a href='#L211'>211</a>
<a name='L212'></a><a href='#L212'>212</a>
<a name='L213'></a><a href='#L213'>213</a>
<a name='L214'></a><a href='#L214'>214</a>
<a name='L215'></a><a href='#L215'>215</a>
<a name='L216'></a><a href='#L216'>216</a>
<a name='L217'></a><a href='#L217'>217</a>
<a name='L218'></a><a href='#L218'>218</a>
<a name='L219'></a><a href='#L219'>219</a>
<a name='L220'></a><a href='#L220'>220</a>
<a name='L221'></a><a href='#L221'>221</a>
<a name='L222'></a><a href='#L222'>222</a>
<a name='L223'></a><a href='#L223'>223</a>
<a name='L224'></a><a href='#L224'>224</a>
<a name='L225'></a><a href='#L225'>225</a>
<a name='L226'></a><a href='#L226'>226</a>
<a name='L227'></a><a href='#L227'>227</a>
<a name='L228'></a><a href='#L228'>228</a>
<a name='L229'></a><a href='#L229'>229</a>
<a name='L230'></a><a href='#L230'>230</a>
<a name='L231'></a><a href='#L231'>231</a>
<a name='L232'></a><a href='#L232'>232</a>
<a name='L233'></a><a href='#L233'>233</a>
<a name='L234'></a><a href='#L234'>234</a>
<a name='L235'></a><a href='#L235'>235</a>
<a name='L236'></a><a href='#L236'>236</a>
<a name='L237'></a><a href='#L237'>237</a>
<a name='L238'></a><a href='#L238'>238</a>
<a name='L239'></a><a href='#L239'>239</a>
<a name='L240'></a><a href='#L240'>240</a>
<a name='L241'></a><a href='#L241'>241</a>
<a name='L242'></a><a href='#L242'>242</a>
<a name='L243'></a><a href='#L243'>243</a>
<a name='L244'></a><a href='#L244'>244</a></td><td class="line-coverage quiet"><span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js"><span class="cstat-no" title="statement not covered" >import { useEffect } from 'react'<span class="fstat-no" title="function not covered" ><span class="branch-0 cbranch-no" title="branch not covered" ></span></span></span>
<span class="cstat-no" title="statement not covered" >import {</span>
Box,
Typography,
Paper,
Card,
CardContent,
List,
ListItem,
ListItemText,
ListItemIcon,
Alert,
Chip,
Button,
CircularProgress,
} from '@mui/material'
<span class="cstat-no" title="statement not covered" >import {</span>
Lightbulb,
Warning,
CheckCircle,
TrendingUp,
Schedule,
Psychology,
} from '@mui/icons-material'
import type { SvgIconProps } from '@mui/material'
<span class="cstat-no" title="statement not covered" >import { useAppDispatch, useAppSelector } from '../store'</span>
<span class="cstat-no" title="statement not covered" >import { getConseil } from '../store/documentSlice'</span>
<span class="cstat-no" title="statement not covered" >import { Layout } from '../components/Layout'</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >export default function ConseilView() {</span>
<span class="cstat-no" title="statement not covered" > const dispatch = useAppDispatch()</span>
<span class="cstat-no" title="statement not covered" > const { currentDocument, conseilResult, loading } = useAppSelector(</span>
<span class="cstat-no" title="statement not covered" > (state) =&gt; state.document</span>
<span class="cstat-no" title="statement not covered" > )</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > useEffect(() =&gt; {</span>
<span class="cstat-no" title="statement not covered" > if (currentDocument &amp;&amp; !conseilResult) {</span>
<span class="cstat-no" title="statement not covered" > dispatch(getConseil(currentDocument.id))</span>
<span class="cstat-no" title="statement not covered" > }</span>
<span class="cstat-no" title="statement not covered" > }, [currentDocument, conseilResult, dispatch])</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > if (!currentDocument) {</span>
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;Layout&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Alert severity="info"&gt;</span>
Veuillez d'abord téléverser et sélectionner un document.
<span class="cstat-no" title="statement not covered" > &lt;/Alert&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Layout&gt;</span>
)
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > if (loading) {</span>
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;Layout&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', justifyContent: 'center', mt: 4 }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CircularProgress /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography sx={{ ml: 2 }}&gt;Génération des conseils LLM...&lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Layout&gt;</span>
)
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > if (!conseilResult) {</span>
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;Layout&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Alert severity="warning"&gt;</span>
Aucun conseil disponible.
<span class="cstat-no" title="statement not covered" > &lt;/Alert&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Layout&gt;</span>
)
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > const getRiskColor = (risk: string): SvgIconProps['color'] =&gt; {</span>
<span class="cstat-no" title="statement not covered" > if (risk.toLowerCase().includes('élevé') || risk.toLowerCase().includes('critique')) {</span>
<span class="cstat-no" title="statement not covered" > return 'error'</span>
<span class="cstat-no" title="statement not covered" > }</span>
<span class="cstat-no" title="statement not covered" > if (risk.toLowerCase().includes('moyen') || risk.toLowerCase().includes('modéré')) {</span>
<span class="cstat-no" title="statement not covered" > return 'warning'</span>
<span class="cstat-no" title="statement not covered" > }</span>
<span class="cstat-no" title="statement not covered" > return 'info'</span>
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;Layout&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h4" gutterBottom&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Psychology sx={{ mr: 1, verticalAlign: 'middle' }} /&gt;</span>
Conseil LLM
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}&gt;</span>
{/* Analyse LLM */}
<span class="cstat-no" title="statement not covered" > &lt;Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Lightbulb sx={{ mr: 1, verticalAlign: 'middle' }} /&gt;</span>
Analyse LLM
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Paper</span>
<span class="cstat-no" title="statement not covered" > sx={{</span>
<span class="cstat-no" title="statement not covered" > p: 2,</span>
<span class="cstat-no" title="statement not covered" > bgcolor: 'grey.50',</span>
<span class="cstat-no" title="statement not covered" > border: '1px solid',</span>
<span class="cstat-no" title="statement not covered" > borderColor: 'grey.200',</span>
<span class="cstat-no" title="statement not covered" > }}</span>
&gt;
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="body1" sx={{ whiteSpace: 'pre-wrap' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > {conseilResult.analysis}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Paper&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="caption" color="text.secondary" sx={{ mt: 1, display: 'block' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > Généré le {new Date(conseilResult.generatedAt).toLocaleString()}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Card&gt;</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', gap: 3, flexWrap: 'wrap' }}&gt;</span>
{/* Recommandations */}
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ flex: '1 1 300px' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CheckCircle sx={{ mr: 1, verticalAlign: 'middle' }} /&gt;</span>
<span class="cstat-no" title="statement not covered" > Recommandations ({conseilResult.recommendations.length})</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;List dense&gt;</span>
<span class="cstat-no" title="statement not covered" > {conseilResult.recommendations.map((recommendation, index) =&gt; (</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItem key={index}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItemIcon&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CheckCircle color="success" /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/ListItemIcon&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItemText primary={recommendation} /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/ListItem&gt;</span>
<span class="cstat-no" title="statement not covered" > ))}</span>
<span class="cstat-no" title="statement not covered" > &lt;/List&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
&nbsp;
{/* Risques identifiés */}
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ flex: '1 1 300px' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Warning sx={{ mr: 1, verticalAlign: 'middle' }} /&gt;</span>
<span class="cstat-no" title="statement not covered" > Risques identifiés ({conseilResult.risks.length})</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;List dense&gt;</span>
<span class="cstat-no" title="statement not covered" > {conseilResult.risks.map((risk, index) =&gt; (</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItem key={index}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItemIcon&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Warning color={getRiskColor(risk)} /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/ListItemIcon&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItemText</span>
<span class="cstat-no" title="statement not covered" > primary={risk}</span>
<span class="cstat-no" title="statement not covered" > primaryTypographyProps={{</span>
<span class="cstat-no" title="statement not covered" > color: getRiskColor(risk) === 'error' ? 'error.main' :</span>
<span class="cstat-no" title="statement not covered" > getRiskColor(risk) === 'warning' ? 'warning.main' : 'info.main'</span>
<span class="cstat-no" title="statement not covered" > }}</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/ListItem&gt;</span>
<span class="cstat-no" title="statement not covered" > ))}</span>
<span class="cstat-no" title="statement not covered" > &lt;/List&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
&nbsp;
{/* Prochaines étapes */}
<span class="cstat-no" title="statement not covered" > &lt;Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;TrendingUp sx={{ mr: 1, verticalAlign: 'middle' }} /&gt;</span>
Prochaines étapes recommandées
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;List&gt;</span>
<span class="cstat-no" title="statement not covered" > {conseilResult.nextSteps.map((step, index) =&gt; (</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItem key={index}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItemIcon&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Schedule color="primary" /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/ListItemIcon&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItemText</span>
<span class="cstat-no" title="statement not covered" > primary={`Étape ${index + 1}`}</span>
<span class="cstat-no" title="statement not covered" > secondary={step}</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/ListItem&gt;</span>
<span class="cstat-no" title="statement not covered" > ))}</span>
<span class="cstat-no" title="statement not covered" > &lt;/List&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Card&gt;</span>
&nbsp;
{/* Actions */}
<span class="cstat-no" title="statement not covered" > &lt;Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
Actions
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', gap: 2, flexWrap: 'wrap' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Button</span>
<span class="cstat-no" title="statement not covered" > variant="contained"</span>
<span class="cstat-no" title="statement not covered" > onClick={() =&gt; dispatch(getConseil(currentDocument.id))}</span>
<span class="cstat-no" title="statement not covered" > disabled={loading}</span>
<span class="cstat-no" title="statement not covered" > &gt;</span>
Régénérer les conseils
<span class="cstat-no" title="statement not covered" > &lt;/Button&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Button variant="outlined"&gt;</span>
Exporter le rapport
<span class="cstat-no" title="statement not covered" > &lt;/Button&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Button variant="outlined"&gt;</span>
Partager avec l'équipe
<span class="cstat-no" title="statement not covered" > &lt;/Button&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Card&gt;</span>
&nbsp;
{/* Résumé exécutif */}
<span class="cstat-no" title="statement not covered" > &lt;Paper sx={{ p: 2, bgcolor: 'primary.50' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
Résumé exécutif
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap', mb: 2 }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > label={`${conseilResult.recommendations.length} recommandations`}</span>
<span class="cstat-no" title="statement not covered" > color="success"</span>
<span class="cstat-no" title="statement not covered" > variant="outlined"</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > label={`${conseilResult.risks.length} risques identifiés`}</span>
<span class="cstat-no" title="statement not covered" > color="warning"</span>
<span class="cstat-no" title="statement not covered" > variant="outlined"</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > label={`${conseilResult.nextSteps.length} étapes suivantes`}</span>
<span class="cstat-no" title="statement not covered" > color="info"</span>
<span class="cstat-no" title="statement not covered" > variant="outlined"</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="body2" color="text.secondary"&gt;</span>
Cette analyse LLM a été générée automatiquement et doit être validée par un expert notarial.
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Paper&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Layout&gt;</span>
)
<span class="cstat-no" title="statement not covered" >}</span></pre></td></tr></table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,979 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/views/ContexteView.tsx</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> / <a href="index.html">src/views</a> ContexteView.tsx</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/228</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/228</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
<a name='L2'></a><a href='#L2'>2</a>
<a name='L3'></a><a href='#L3'>3</a>
<a name='L4'></a><a href='#L4'>4</a>
<a name='L5'></a><a href='#L5'>5</a>
<a name='L6'></a><a href='#L6'>6</a>
<a name='L7'></a><a href='#L7'>7</a>
<a name='L8'></a><a href='#L8'>8</a>
<a name='L9'></a><a href='#L9'>9</a>
<a name='L10'></a><a href='#L10'>10</a>
<a name='L11'></a><a href='#L11'>11</a>
<a name='L12'></a><a href='#L12'>12</a>
<a name='L13'></a><a href='#L13'>13</a>
<a name='L14'></a><a href='#L14'>14</a>
<a name='L15'></a><a href='#L15'>15</a>
<a name='L16'></a><a href='#L16'>16</a>
<a name='L17'></a><a href='#L17'>17</a>
<a name='L18'></a><a href='#L18'>18</a>
<a name='L19'></a><a href='#L19'>19</a>
<a name='L20'></a><a href='#L20'>20</a>
<a name='L21'></a><a href='#L21'>21</a>
<a name='L22'></a><a href='#L22'>22</a>
<a name='L23'></a><a href='#L23'>23</a>
<a name='L24'></a><a href='#L24'>24</a>
<a name='L25'></a><a href='#L25'>25</a>
<a name='L26'></a><a href='#L26'>26</a>
<a name='L27'></a><a href='#L27'>27</a>
<a name='L28'></a><a href='#L28'>28</a>
<a name='L29'></a><a href='#L29'>29</a>
<a name='L30'></a><a href='#L30'>30</a>
<a name='L31'></a><a href='#L31'>31</a>
<a name='L32'></a><a href='#L32'>32</a>
<a name='L33'></a><a href='#L33'>33</a>
<a name='L34'></a><a href='#L34'>34</a>
<a name='L35'></a><a href='#L35'>35</a>
<a name='L36'></a><a href='#L36'>36</a>
<a name='L37'></a><a href='#L37'>37</a>
<a name='L38'></a><a href='#L38'>38</a>
<a name='L39'></a><a href='#L39'>39</a>
<a name='L40'></a><a href='#L40'>40</a>
<a name='L41'></a><a href='#L41'>41</a>
<a name='L42'></a><a href='#L42'>42</a>
<a name='L43'></a><a href='#L43'>43</a>
<a name='L44'></a><a href='#L44'>44</a>
<a name='L45'></a><a href='#L45'>45</a>
<a name='L46'></a><a href='#L46'>46</a>
<a name='L47'></a><a href='#L47'>47</a>
<a name='L48'></a><a href='#L48'>48</a>
<a name='L49'></a><a href='#L49'>49</a>
<a name='L50'></a><a href='#L50'>50</a>
<a name='L51'></a><a href='#L51'>51</a>
<a name='L52'></a><a href='#L52'>52</a>
<a name='L53'></a><a href='#L53'>53</a>
<a name='L54'></a><a href='#L54'>54</a>
<a name='L55'></a><a href='#L55'>55</a>
<a name='L56'></a><a href='#L56'>56</a>
<a name='L57'></a><a href='#L57'>57</a>
<a name='L58'></a><a href='#L58'>58</a>
<a name='L59'></a><a href='#L59'>59</a>
<a name='L60'></a><a href='#L60'>60</a>
<a name='L61'></a><a href='#L61'>61</a>
<a name='L62'></a><a href='#L62'>62</a>
<a name='L63'></a><a href='#L63'>63</a>
<a name='L64'></a><a href='#L64'>64</a>
<a name='L65'></a><a href='#L65'>65</a>
<a name='L66'></a><a href='#L66'>66</a>
<a name='L67'></a><a href='#L67'>67</a>
<a name='L68'></a><a href='#L68'>68</a>
<a name='L69'></a><a href='#L69'>69</a>
<a name='L70'></a><a href='#L70'>70</a>
<a name='L71'></a><a href='#L71'>71</a>
<a name='L72'></a><a href='#L72'>72</a>
<a name='L73'></a><a href='#L73'>73</a>
<a name='L74'></a><a href='#L74'>74</a>
<a name='L75'></a><a href='#L75'>75</a>
<a name='L76'></a><a href='#L76'>76</a>
<a name='L77'></a><a href='#L77'>77</a>
<a name='L78'></a><a href='#L78'>78</a>
<a name='L79'></a><a href='#L79'>79</a>
<a name='L80'></a><a href='#L80'>80</a>
<a name='L81'></a><a href='#L81'>81</a>
<a name='L82'></a><a href='#L82'>82</a>
<a name='L83'></a><a href='#L83'>83</a>
<a name='L84'></a><a href='#L84'>84</a>
<a name='L85'></a><a href='#L85'>85</a>
<a name='L86'></a><a href='#L86'>86</a>
<a name='L87'></a><a href='#L87'>87</a>
<a name='L88'></a><a href='#L88'>88</a>
<a name='L89'></a><a href='#L89'>89</a>
<a name='L90'></a><a href='#L90'>90</a>
<a name='L91'></a><a href='#L91'>91</a>
<a name='L92'></a><a href='#L92'>92</a>
<a name='L93'></a><a href='#L93'>93</a>
<a name='L94'></a><a href='#L94'>94</a>
<a name='L95'></a><a href='#L95'>95</a>
<a name='L96'></a><a href='#L96'>96</a>
<a name='L97'></a><a href='#L97'>97</a>
<a name='L98'></a><a href='#L98'>98</a>
<a name='L99'></a><a href='#L99'>99</a>
<a name='L100'></a><a href='#L100'>100</a>
<a name='L101'></a><a href='#L101'>101</a>
<a name='L102'></a><a href='#L102'>102</a>
<a name='L103'></a><a href='#L103'>103</a>
<a name='L104'></a><a href='#L104'>104</a>
<a name='L105'></a><a href='#L105'>105</a>
<a name='L106'></a><a href='#L106'>106</a>
<a name='L107'></a><a href='#L107'>107</a>
<a name='L108'></a><a href='#L108'>108</a>
<a name='L109'></a><a href='#L109'>109</a>
<a name='L110'></a><a href='#L110'>110</a>
<a name='L111'></a><a href='#L111'>111</a>
<a name='L112'></a><a href='#L112'>112</a>
<a name='L113'></a><a href='#L113'>113</a>
<a name='L114'></a><a href='#L114'>114</a>
<a name='L115'></a><a href='#L115'>115</a>
<a name='L116'></a><a href='#L116'>116</a>
<a name='L117'></a><a href='#L117'>117</a>
<a name='L118'></a><a href='#L118'>118</a>
<a name='L119'></a><a href='#L119'>119</a>
<a name='L120'></a><a href='#L120'>120</a>
<a name='L121'></a><a href='#L121'>121</a>
<a name='L122'></a><a href='#L122'>122</a>
<a name='L123'></a><a href='#L123'>123</a>
<a name='L124'></a><a href='#L124'>124</a>
<a name='L125'></a><a href='#L125'>125</a>
<a name='L126'></a><a href='#L126'>126</a>
<a name='L127'></a><a href='#L127'>127</a>
<a name='L128'></a><a href='#L128'>128</a>
<a name='L129'></a><a href='#L129'>129</a>
<a name='L130'></a><a href='#L130'>130</a>
<a name='L131'></a><a href='#L131'>131</a>
<a name='L132'></a><a href='#L132'>132</a>
<a name='L133'></a><a href='#L133'>133</a>
<a name='L134'></a><a href='#L134'>134</a>
<a name='L135'></a><a href='#L135'>135</a>
<a name='L136'></a><a href='#L136'>136</a>
<a name='L137'></a><a href='#L137'>137</a>
<a name='L138'></a><a href='#L138'>138</a>
<a name='L139'></a><a href='#L139'>139</a>
<a name='L140'></a><a href='#L140'>140</a>
<a name='L141'></a><a href='#L141'>141</a>
<a name='L142'></a><a href='#L142'>142</a>
<a name='L143'></a><a href='#L143'>143</a>
<a name='L144'></a><a href='#L144'>144</a>
<a name='L145'></a><a href='#L145'>145</a>
<a name='L146'></a><a href='#L146'>146</a>
<a name='L147'></a><a href='#L147'>147</a>
<a name='L148'></a><a href='#L148'>148</a>
<a name='L149'></a><a href='#L149'>149</a>
<a name='L150'></a><a href='#L150'>150</a>
<a name='L151'></a><a href='#L151'>151</a>
<a name='L152'></a><a href='#L152'>152</a>
<a name='L153'></a><a href='#L153'>153</a>
<a name='L154'></a><a href='#L154'>154</a>
<a name='L155'></a><a href='#L155'>155</a>
<a name='L156'></a><a href='#L156'>156</a>
<a name='L157'></a><a href='#L157'>157</a>
<a name='L158'></a><a href='#L158'>158</a>
<a name='L159'></a><a href='#L159'>159</a>
<a name='L160'></a><a href='#L160'>160</a>
<a name='L161'></a><a href='#L161'>161</a>
<a name='L162'></a><a href='#L162'>162</a>
<a name='L163'></a><a href='#L163'>163</a>
<a name='L164'></a><a href='#L164'>164</a>
<a name='L165'></a><a href='#L165'>165</a>
<a name='L166'></a><a href='#L166'>166</a>
<a name='L167'></a><a href='#L167'>167</a>
<a name='L168'></a><a href='#L168'>168</a>
<a name='L169'></a><a href='#L169'>169</a>
<a name='L170'></a><a href='#L170'>170</a>
<a name='L171'></a><a href='#L171'>171</a>
<a name='L172'></a><a href='#L172'>172</a>
<a name='L173'></a><a href='#L173'>173</a>
<a name='L174'></a><a href='#L174'>174</a>
<a name='L175'></a><a href='#L175'>175</a>
<a name='L176'></a><a href='#L176'>176</a>
<a name='L177'></a><a href='#L177'>177</a>
<a name='L178'></a><a href='#L178'>178</a>
<a name='L179'></a><a href='#L179'>179</a>
<a name='L180'></a><a href='#L180'>180</a>
<a name='L181'></a><a href='#L181'>181</a>
<a name='L182'></a><a href='#L182'>182</a>
<a name='L183'></a><a href='#L183'>183</a>
<a name='L184'></a><a href='#L184'>184</a>
<a name='L185'></a><a href='#L185'>185</a>
<a name='L186'></a><a href='#L186'>186</a>
<a name='L187'></a><a href='#L187'>187</a>
<a name='L188'></a><a href='#L188'>188</a>
<a name='L189'></a><a href='#L189'>189</a>
<a name='L190'></a><a href='#L190'>190</a>
<a name='L191'></a><a href='#L191'>191</a>
<a name='L192'></a><a href='#L192'>192</a>
<a name='L193'></a><a href='#L193'>193</a>
<a name='L194'></a><a href='#L194'>194</a>
<a name='L195'></a><a href='#L195'>195</a>
<a name='L196'></a><a href='#L196'>196</a>
<a name='L197'></a><a href='#L197'>197</a>
<a name='L198'></a><a href='#L198'>198</a>
<a name='L199'></a><a href='#L199'>199</a>
<a name='L200'></a><a href='#L200'>200</a>
<a name='L201'></a><a href='#L201'>201</a>
<a name='L202'></a><a href='#L202'>202</a>
<a name='L203'></a><a href='#L203'>203</a>
<a name='L204'></a><a href='#L204'>204</a>
<a name='L205'></a><a href='#L205'>205</a>
<a name='L206'></a><a href='#L206'>206</a>
<a name='L207'></a><a href='#L207'>207</a>
<a name='L208'></a><a href='#L208'>208</a>
<a name='L209'></a><a href='#L209'>209</a>
<a name='L210'></a><a href='#L210'>210</a>
<a name='L211'></a><a href='#L211'>211</a>
<a name='L212'></a><a href='#L212'>212</a>
<a name='L213'></a><a href='#L213'>213</a>
<a name='L214'></a><a href='#L214'>214</a>
<a name='L215'></a><a href='#L215'>215</a>
<a name='L216'></a><a href='#L216'>216</a>
<a name='L217'></a><a href='#L217'>217</a>
<a name='L218'></a><a href='#L218'>218</a>
<a name='L219'></a><a href='#L219'>219</a>
<a name='L220'></a><a href='#L220'>220</a>
<a name='L221'></a><a href='#L221'>221</a>
<a name='L222'></a><a href='#L222'>222</a>
<a name='L223'></a><a href='#L223'>223</a>
<a name='L224'></a><a href='#L224'>224</a>
<a name='L225'></a><a href='#L225'>225</a>
<a name='L226'></a><a href='#L226'>226</a>
<a name='L227'></a><a href='#L227'>227</a>
<a name='L228'></a><a href='#L228'>228</a>
<a name='L229'></a><a href='#L229'>229</a>
<a name='L230'></a><a href='#L230'>230</a>
<a name='L231'></a><a href='#L231'>231</a>
<a name='L232'></a><a href='#L232'>232</a>
<a name='L233'></a><a href='#L233'>233</a>
<a name='L234'></a><a href='#L234'>234</a>
<a name='L235'></a><a href='#L235'>235</a>
<a name='L236'></a><a href='#L236'>236</a>
<a name='L237'></a><a href='#L237'>237</a>
<a name='L238'></a><a href='#L238'>238</a>
<a name='L239'></a><a href='#L239'>239</a>
<a name='L240'></a><a href='#L240'>240</a>
<a name='L241'></a><a href='#L241'>241</a>
<a name='L242'></a><a href='#L242'>242</a>
<a name='L243'></a><a href='#L243'>243</a>
<a name='L244'></a><a href='#L244'>244</a>
<a name='L245'></a><a href='#L245'>245</a>
<a name='L246'></a><a href='#L246'>246</a>
<a name='L247'></a><a href='#L247'>247</a>
<a name='L248'></a><a href='#L248'>248</a>
<a name='L249'></a><a href='#L249'>249</a>
<a name='L250'></a><a href='#L250'>250</a>
<a name='L251'></a><a href='#L251'>251</a>
<a name='L252'></a><a href='#L252'>252</a>
<a name='L253'></a><a href='#L253'>253</a>
<a name='L254'></a><a href='#L254'>254</a>
<a name='L255'></a><a href='#L255'>255</a>
<a name='L256'></a><a href='#L256'>256</a>
<a name='L257'></a><a href='#L257'>257</a>
<a name='L258'></a><a href='#L258'>258</a>
<a name='L259'></a><a href='#L259'>259</a>
<a name='L260'></a><a href='#L260'>260</a>
<a name='L261'></a><a href='#L261'>261</a>
<a name='L262'></a><a href='#L262'>262</a>
<a name='L263'></a><a href='#L263'>263</a>
<a name='L264'></a><a href='#L264'>264</a>
<a name='L265'></a><a href='#L265'>265</a>
<a name='L266'></a><a href='#L266'>266</a>
<a name='L267'></a><a href='#L267'>267</a>
<a name='L268'></a><a href='#L268'>268</a>
<a name='L269'></a><a href='#L269'>269</a>
<a name='L270'></a><a href='#L270'>270</a>
<a name='L271'></a><a href='#L271'>271</a>
<a name='L272'></a><a href='#L272'>272</a>
<a name='L273'></a><a href='#L273'>273</a>
<a name='L274'></a><a href='#L274'>274</a>
<a name='L275'></a><a href='#L275'>275</a>
<a name='L276'></a><a href='#L276'>276</a>
<a name='L277'></a><a href='#L277'>277</a>
<a name='L278'></a><a href='#L278'>278</a>
<a name='L279'></a><a href='#L279'>279</a>
<a name='L280'></a><a href='#L280'>280</a>
<a name='L281'></a><a href='#L281'>281</a>
<a name='L282'></a><a href='#L282'>282</a>
<a name='L283'></a><a href='#L283'>283</a>
<a name='L284'></a><a href='#L284'>284</a>
<a name='L285'></a><a href='#L285'>285</a>
<a name='L286'></a><a href='#L286'>286</a>
<a name='L287'></a><a href='#L287'>287</a>
<a name='L288'></a><a href='#L288'>288</a>
<a name='L289'></a><a href='#L289'>289</a>
<a name='L290'></a><a href='#L290'>290</a>
<a name='L291'></a><a href='#L291'>291</a>
<a name='L292'></a><a href='#L292'>292</a>
<a name='L293'></a><a href='#L293'>293</a>
<a name='L294'></a><a href='#L294'>294</a>
<a name='L295'></a><a href='#L295'>295</a>
<a name='L296'></a><a href='#L296'>296</a>
<a name='L297'></a><a href='#L297'>297</a>
<a name='L298'></a><a href='#L298'>298</a>
<a name='L299'></a><a href='#L299'>299</a></td><td class="line-coverage quiet"><span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js"><span class="cstat-no" title="statement not covered" >import { useEffect } from 'react'<span class="fstat-no" title="function not covered" ><span class="branch-0 cbranch-no" title="branch not covered" ></span></span></span>
<span class="cstat-no" title="statement not covered" >import {</span>
Box,
Typography,
Paper,
Card,
CardContent,
Chip,
Alert,
Button,
Accordion,
AccordionSummary,
AccordionDetails,
CircularProgress,
} from '@mui/material'
<span class="cstat-no" title="statement not covered" >import {</span>
ExpandMore,
LocationOn,
Warning,
CheckCircle,
Error,
Public,
Business,
Home,
} from '@mui/icons-material'
import type { ChipProps } from '@mui/material'
<span class="cstat-no" title="statement not covered" >import { useAppDispatch, useAppSelector } from '../store'</span>
<span class="cstat-no" title="statement not covered" >import { getContextData } from '../store/documentSlice'</span>
<span class="cstat-no" title="statement not covered" >import { Layout } from '../components/Layout'</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >export default function ContexteView() {</span>
<span class="cstat-no" title="statement not covered" > const dispatch = useAppDispatch()</span>
<span class="cstat-no" title="statement not covered" > const { currentDocument, contextResult, loading } = useAppSelector(</span>
<span class="cstat-no" title="statement not covered" > (state) =&gt; state.document</span>
<span class="cstat-no" title="statement not covered" > )</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > useEffect(() =&gt; {</span>
<span class="cstat-no" title="statement not covered" > if (currentDocument &amp;&amp; !contextResult) {</span>
<span class="cstat-no" title="statement not covered" > dispatch(getContextData(currentDocument.id))</span>
<span class="cstat-no" title="statement not covered" > }</span>
<span class="cstat-no" title="statement not covered" > }, [currentDocument, contextResult, dispatch])</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > if (!currentDocument) {</span>
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;Layout&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Alert severity="info"&gt;</span>
Veuillez d'abord téléverser et sélectionner un document.
<span class="cstat-no" title="statement not covered" > &lt;/Alert&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Layout&gt;</span>
)
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > if (loading) {</span>
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;Layout&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', justifyContent: 'center', mt: 4 }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CircularProgress /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography sx={{ ml: 2 }}&gt;Recherche d'informations contextuelles...&lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Layout&gt;</span>
)
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > if (!contextResult) {</span>
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;Layout&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Alert severity="warning"&gt;</span>
Aucune donnée contextuelle disponible.
<span class="cstat-no" title="statement not covered" > &lt;/Alert&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Layout&gt;</span>
)
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > const getStatusIcon = (hasData: boolean) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > return hasData ? &lt;CheckCircle color="success" /&gt; : &lt;Error color="error" /&gt;</span>
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > const getStatusColor = (hasData: boolean): ChipProps['color'] =&gt; {</span>
<span class="cstat-no" title="statement not covered" > return hasData ? 'success' : 'error'</span>
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;Layout&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h4" gutterBottom&gt;</span>
Informations contextuelles
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}&gt;</span>
{/* Résumé des sources */}
<span class="cstat-no" title="statement not covered" > &lt;Paper sx={{ p: 2 }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
Sources de données consultées
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > icon={getStatusIcon(!!contextResult.cadastreData)}</span>
<span class="cstat-no" title="statement not covered" > label="Cadastre"</span>
<span class="cstat-no" title="statement not covered" > color={getStatusColor(!!contextResult.cadastreData)}</span>
<span class="cstat-no" title="statement not covered" > variant="outlined"</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > icon={getStatusIcon(!!contextResult.georisquesData)}</span>
<span class="cstat-no" title="statement not covered" > label="Géorisques"</span>
<span class="cstat-no" title="statement not covered" > color={getStatusColor(!!contextResult.georisquesData)}</span>
<span class="cstat-no" title="statement not covered" > variant="outlined"</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > icon={getStatusIcon(!!contextResult.geofoncierData)}</span>
<span class="cstat-no" title="statement not covered" > label="Géofoncier"</span>
<span class="cstat-no" title="statement not covered" > color={getStatusColor(!!contextResult.geofoncierData)}</span>
<span class="cstat-no" title="statement not covered" > variant="outlined"</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > icon={getStatusIcon(!!contextResult.bodaccData)}</span>
<span class="cstat-no" title="statement not covered" > label="BODACC"</span>
<span class="cstat-no" title="statement not covered" > color={getStatusColor(!!contextResult.bodaccData)}</span>
<span class="cstat-no" title="statement not covered" > variant="outlined"</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > icon={getStatusIcon(!!contextResult.infogreffeData)}</span>
<span class="cstat-no" title="statement not covered" > label="Infogreffe"</span>
<span class="cstat-no" title="statement not covered" > color={getStatusColor(!!contextResult.infogreffeData)}</span>
<span class="cstat-no" title="statement not covered" > variant="outlined"</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="caption" color="text.secondary" sx={{ mt: 1, display: 'block' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > Dernière mise à jour: {new Date(contextResult.lastUpdated).toLocaleString()}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Paper&gt;</span>
&nbsp;
{/* Données cadastrales */}
<span class="cstat-no" title="statement not covered" > &lt;Accordion&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;AccordionSummary expandIcon={&lt;ExpandMore /&gt;}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', alignItems: 'center' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Home sx={{ mr: 1 }} /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6"&gt;Données cadastrales&lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > label={contextResult.cadastreData ? 'Disponible' : 'Non disponible'}</span>
<span class="cstat-no" title="statement not covered" > color={getStatusColor(!!contextResult.cadastreData)}</span>
<span class="cstat-no" title="statement not covered" > size="small"</span>
<span class="cstat-no" title="statement not covered" > sx={{ ml: 2 }}</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/AccordionSummary&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;AccordionDetails&gt;</span>
<span class="cstat-no" title="statement not covered" > {contextResult.cadastreData ? (</span>
<span class="cstat-no" title="statement not covered" > &lt;Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="body2" sx={{ whiteSpace: 'pre-wrap' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > {JSON.stringify(contextResult.cadastreData, null, 2)}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
) : (
<span class="cstat-no" title="statement not covered" > &lt;Alert severity="info"&gt;</span>
Aucune donnée cadastrale trouvée pour ce document.
<span class="cstat-no" title="statement not covered" > &lt;/Alert&gt;</span>
)}
<span class="cstat-no" title="statement not covered" > &lt;/AccordionDetails&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Accordion&gt;</span>
&nbsp;
{/* Données Géorisques */}
<span class="cstat-no" title="statement not covered" > &lt;Accordion&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;AccordionSummary expandIcon={&lt;ExpandMore /&gt;}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', alignItems: 'center' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Warning sx={{ mr: 1 }} /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6"&gt;Données Géorisques&lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > label={contextResult.georisquesData ? 'Disponible' : 'Non disponible'}</span>
<span class="cstat-no" title="statement not covered" > color={getStatusColor(!!contextResult.georisquesData)}</span>
<span class="cstat-no" title="statement not covered" > size="small"</span>
<span class="cstat-no" title="statement not covered" > sx={{ ml: 2 }}</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/AccordionSummary&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;AccordionDetails&gt;</span>
<span class="cstat-no" title="statement not covered" > {contextResult.georisquesData ? (</span>
<span class="cstat-no" title="statement not covered" > &lt;Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="body2" sx={{ whiteSpace: 'pre-wrap' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > {JSON.stringify(contextResult.georisquesData, null, 2)}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
) : (
<span class="cstat-no" title="statement not covered" > &lt;Alert severity="info"&gt;</span>
Aucune donnée Géorisques trouvée pour ce document.
<span class="cstat-no" title="statement not covered" > &lt;/Alert&gt;</span>
)}
<span class="cstat-no" title="statement not covered" > &lt;/AccordionDetails&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Accordion&gt;</span>
&nbsp;
{/* Données Géofoncier */}
<span class="cstat-no" title="statement not covered" > &lt;Accordion&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;AccordionSummary expandIcon={&lt;ExpandMore /&gt;}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', alignItems: 'center' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;LocationOn sx={{ mr: 1 }} /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6"&gt;Données Géofoncier&lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > label={contextResult.geofoncierData ? 'Disponible' : 'Non disponible'}</span>
<span class="cstat-no" title="statement not covered" > color={getStatusColor(!!contextResult.geofoncierData)}</span>
<span class="cstat-no" title="statement not covered" > size="small"</span>
<span class="cstat-no" title="statement not covered" > sx={{ ml: 2 }}</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/AccordionSummary&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;AccordionDetails&gt;</span>
<span class="cstat-no" title="statement not covered" > {contextResult.geofoncierData ? (</span>
<span class="cstat-no" title="statement not covered" > &lt;Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="body2" sx={{ whiteSpace: 'pre-wrap' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > {JSON.stringify(contextResult.geofoncierData, null, 2)}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
) : (
<span class="cstat-no" title="statement not covered" > &lt;Alert severity="info"&gt;</span>
Aucune donnée Géofoncier trouvée pour ce document.
<span class="cstat-no" title="statement not covered" > &lt;/Alert&gt;</span>
)}
<span class="cstat-no" title="statement not covered" > &lt;/AccordionDetails&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Accordion&gt;</span>
&nbsp;
{/* Données BODACC */}
<span class="cstat-no" title="statement not covered" > &lt;Accordion&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;AccordionSummary expandIcon={&lt;ExpandMore /&gt;}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', alignItems: 'center' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Public sx={{ mr: 1 }} /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6"&gt;Données BODACC&lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > label={contextResult.bodaccData ? 'Disponible' : 'Non disponible'}</span>
<span class="cstat-no" title="statement not covered" > color={getStatusColor(!!contextResult.bodaccData)}</span>
<span class="cstat-no" title="statement not covered" > size="small"</span>
<span class="cstat-no" title="statement not covered" > sx={{ ml: 2 }}</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/AccordionSummary&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;AccordionDetails&gt;</span>
<span class="cstat-no" title="statement not covered" > {contextResult.bodaccData ? (</span>
<span class="cstat-no" title="statement not covered" > &lt;Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="body2" sx={{ whiteSpace: 'pre-wrap' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > {JSON.stringify(contextResult.bodaccData, null, 2)}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
) : (
<span class="cstat-no" title="statement not covered" > &lt;Alert severity="info"&gt;</span>
Aucune donnée BODACC trouvée pour ce document.
<span class="cstat-no" title="statement not covered" > &lt;/Alert&gt;</span>
)}
<span class="cstat-no" title="statement not covered" > &lt;/AccordionDetails&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Accordion&gt;</span>
&nbsp;
{/* Données Infogreffe */}
<span class="cstat-no" title="statement not covered" > &lt;Accordion&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;AccordionSummary expandIcon={&lt;ExpandMore /&gt;}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', alignItems: 'center' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Business sx={{ mr: 1 }} /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6"&gt;Données Infogreffe&lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > label={contextResult.infogreffeData ? 'Disponible' : 'Non disponible'}</span>
<span class="cstat-no" title="statement not covered" > color={getStatusColor(!!contextResult.infogreffeData)}</span>
<span class="cstat-no" title="statement not covered" > size="small"</span>
<span class="cstat-no" title="statement not covered" > sx={{ ml: 2 }}</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/AccordionSummary&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;AccordionDetails&gt;</span>
<span class="cstat-no" title="statement not covered" > {contextResult.infogreffeData ? (</span>
<span class="cstat-no" title="statement not covered" > &lt;Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="body2" sx={{ whiteSpace: 'pre-wrap' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > {JSON.stringify(contextResult.infogreffeData, null, 2)}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
) : (
<span class="cstat-no" title="statement not covered" > &lt;Alert severity="info"&gt;</span>
Aucune donnée Infogreffe trouvée pour ce document.
<span class="cstat-no" title="statement not covered" > &lt;/Alert&gt;</span>
)}
<span class="cstat-no" title="statement not covered" > &lt;/AccordionDetails&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Accordion&gt;</span>
&nbsp;
{/* Actions */}
<span class="cstat-no" title="statement not covered" > &lt;Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
Actions
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', gap: 2 }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Button</span>
<span class="cstat-no" title="statement not covered" > variant="contained"</span>
<span class="cstat-no" title="statement not covered" > onClick={() =&gt; dispatch(getContextData(currentDocument.id))}</span>
<span class="cstat-no" title="statement not covered" > disabled={loading}</span>
<span class="cstat-no" title="statement not covered" > &gt;</span>
Actualiser les données
<span class="cstat-no" title="statement not covered" > &lt;/Button&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Button variant="outlined"&gt;</span>
Exporter le rapport
<span class="cstat-no" title="statement not covered" > &lt;/Button&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Layout&gt;</span>
)
<span class="cstat-no" title="statement not covered" >}</span></pre></td></tr></table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,946 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/views/ExtractionView.tsx</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> / <a href="index.html">src/views</a> ExtractionView.tsx</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/230</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/230</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
<a name='L2'></a><a href='#L2'>2</a>
<a name='L3'></a><a href='#L3'>3</a>
<a name='L4'></a><a href='#L4'>4</a>
<a name='L5'></a><a href='#L5'>5</a>
<a name='L6'></a><a href='#L6'>6</a>
<a name='L7'></a><a href='#L7'>7</a>
<a name='L8'></a><a href='#L8'>8</a>
<a name='L9'></a><a href='#L9'>9</a>
<a name='L10'></a><a href='#L10'>10</a>
<a name='L11'></a><a href='#L11'>11</a>
<a name='L12'></a><a href='#L12'>12</a>
<a name='L13'></a><a href='#L13'>13</a>
<a name='L14'></a><a href='#L14'>14</a>
<a name='L15'></a><a href='#L15'>15</a>
<a name='L16'></a><a href='#L16'>16</a>
<a name='L17'></a><a href='#L17'>17</a>
<a name='L18'></a><a href='#L18'>18</a>
<a name='L19'></a><a href='#L19'>19</a>
<a name='L20'></a><a href='#L20'>20</a>
<a name='L21'></a><a href='#L21'>21</a>
<a name='L22'></a><a href='#L22'>22</a>
<a name='L23'></a><a href='#L23'>23</a>
<a name='L24'></a><a href='#L24'>24</a>
<a name='L25'></a><a href='#L25'>25</a>
<a name='L26'></a><a href='#L26'>26</a>
<a name='L27'></a><a href='#L27'>27</a>
<a name='L28'></a><a href='#L28'>28</a>
<a name='L29'></a><a href='#L29'>29</a>
<a name='L30'></a><a href='#L30'>30</a>
<a name='L31'></a><a href='#L31'>31</a>
<a name='L32'></a><a href='#L32'>32</a>
<a name='L33'></a><a href='#L33'>33</a>
<a name='L34'></a><a href='#L34'>34</a>
<a name='L35'></a><a href='#L35'>35</a>
<a name='L36'></a><a href='#L36'>36</a>
<a name='L37'></a><a href='#L37'>37</a>
<a name='L38'></a><a href='#L38'>38</a>
<a name='L39'></a><a href='#L39'>39</a>
<a name='L40'></a><a href='#L40'>40</a>
<a name='L41'></a><a href='#L41'>41</a>
<a name='L42'></a><a href='#L42'>42</a>
<a name='L43'></a><a href='#L43'>43</a>
<a name='L44'></a><a href='#L44'>44</a>
<a name='L45'></a><a href='#L45'>45</a>
<a name='L46'></a><a href='#L46'>46</a>
<a name='L47'></a><a href='#L47'>47</a>
<a name='L48'></a><a href='#L48'>48</a>
<a name='L49'></a><a href='#L49'>49</a>
<a name='L50'></a><a href='#L50'>50</a>
<a name='L51'></a><a href='#L51'>51</a>
<a name='L52'></a><a href='#L52'>52</a>
<a name='L53'></a><a href='#L53'>53</a>
<a name='L54'></a><a href='#L54'>54</a>
<a name='L55'></a><a href='#L55'>55</a>
<a name='L56'></a><a href='#L56'>56</a>
<a name='L57'></a><a href='#L57'>57</a>
<a name='L58'></a><a href='#L58'>58</a>
<a name='L59'></a><a href='#L59'>59</a>
<a name='L60'></a><a href='#L60'>60</a>
<a name='L61'></a><a href='#L61'>61</a>
<a name='L62'></a><a href='#L62'>62</a>
<a name='L63'></a><a href='#L63'>63</a>
<a name='L64'></a><a href='#L64'>64</a>
<a name='L65'></a><a href='#L65'>65</a>
<a name='L66'></a><a href='#L66'>66</a>
<a name='L67'></a><a href='#L67'>67</a>
<a name='L68'></a><a href='#L68'>68</a>
<a name='L69'></a><a href='#L69'>69</a>
<a name='L70'></a><a href='#L70'>70</a>
<a name='L71'></a><a href='#L71'>71</a>
<a name='L72'></a><a href='#L72'>72</a>
<a name='L73'></a><a href='#L73'>73</a>
<a name='L74'></a><a href='#L74'>74</a>
<a name='L75'></a><a href='#L75'>75</a>
<a name='L76'></a><a href='#L76'>76</a>
<a name='L77'></a><a href='#L77'>77</a>
<a name='L78'></a><a href='#L78'>78</a>
<a name='L79'></a><a href='#L79'>79</a>
<a name='L80'></a><a href='#L80'>80</a>
<a name='L81'></a><a href='#L81'>81</a>
<a name='L82'></a><a href='#L82'>82</a>
<a name='L83'></a><a href='#L83'>83</a>
<a name='L84'></a><a href='#L84'>84</a>
<a name='L85'></a><a href='#L85'>85</a>
<a name='L86'></a><a href='#L86'>86</a>
<a name='L87'></a><a href='#L87'>87</a>
<a name='L88'></a><a href='#L88'>88</a>
<a name='L89'></a><a href='#L89'>89</a>
<a name='L90'></a><a href='#L90'>90</a>
<a name='L91'></a><a href='#L91'>91</a>
<a name='L92'></a><a href='#L92'>92</a>
<a name='L93'></a><a href='#L93'>93</a>
<a name='L94'></a><a href='#L94'>94</a>
<a name='L95'></a><a href='#L95'>95</a>
<a name='L96'></a><a href='#L96'>96</a>
<a name='L97'></a><a href='#L97'>97</a>
<a name='L98'></a><a href='#L98'>98</a>
<a name='L99'></a><a href='#L99'>99</a>
<a name='L100'></a><a href='#L100'>100</a>
<a name='L101'></a><a href='#L101'>101</a>
<a name='L102'></a><a href='#L102'>102</a>
<a name='L103'></a><a href='#L103'>103</a>
<a name='L104'></a><a href='#L104'>104</a>
<a name='L105'></a><a href='#L105'>105</a>
<a name='L106'></a><a href='#L106'>106</a>
<a name='L107'></a><a href='#L107'>107</a>
<a name='L108'></a><a href='#L108'>108</a>
<a name='L109'></a><a href='#L109'>109</a>
<a name='L110'></a><a href='#L110'>110</a>
<a name='L111'></a><a href='#L111'>111</a>
<a name='L112'></a><a href='#L112'>112</a>
<a name='L113'></a><a href='#L113'>113</a>
<a name='L114'></a><a href='#L114'>114</a>
<a name='L115'></a><a href='#L115'>115</a>
<a name='L116'></a><a href='#L116'>116</a>
<a name='L117'></a><a href='#L117'>117</a>
<a name='L118'></a><a href='#L118'>118</a>
<a name='L119'></a><a href='#L119'>119</a>
<a name='L120'></a><a href='#L120'>120</a>
<a name='L121'></a><a href='#L121'>121</a>
<a name='L122'></a><a href='#L122'>122</a>
<a name='L123'></a><a href='#L123'>123</a>
<a name='L124'></a><a href='#L124'>124</a>
<a name='L125'></a><a href='#L125'>125</a>
<a name='L126'></a><a href='#L126'>126</a>
<a name='L127'></a><a href='#L127'>127</a>
<a name='L128'></a><a href='#L128'>128</a>
<a name='L129'></a><a href='#L129'>129</a>
<a name='L130'></a><a href='#L130'>130</a>
<a name='L131'></a><a href='#L131'>131</a>
<a name='L132'></a><a href='#L132'>132</a>
<a name='L133'></a><a href='#L133'>133</a>
<a name='L134'></a><a href='#L134'>134</a>
<a name='L135'></a><a href='#L135'>135</a>
<a name='L136'></a><a href='#L136'>136</a>
<a name='L137'></a><a href='#L137'>137</a>
<a name='L138'></a><a href='#L138'>138</a>
<a name='L139'></a><a href='#L139'>139</a>
<a name='L140'></a><a href='#L140'>140</a>
<a name='L141'></a><a href='#L141'>141</a>
<a name='L142'></a><a href='#L142'>142</a>
<a name='L143'></a><a href='#L143'>143</a>
<a name='L144'></a><a href='#L144'>144</a>
<a name='L145'></a><a href='#L145'>145</a>
<a name='L146'></a><a href='#L146'>146</a>
<a name='L147'></a><a href='#L147'>147</a>
<a name='L148'></a><a href='#L148'>148</a>
<a name='L149'></a><a href='#L149'>149</a>
<a name='L150'></a><a href='#L150'>150</a>
<a name='L151'></a><a href='#L151'>151</a>
<a name='L152'></a><a href='#L152'>152</a>
<a name='L153'></a><a href='#L153'>153</a>
<a name='L154'></a><a href='#L154'>154</a>
<a name='L155'></a><a href='#L155'>155</a>
<a name='L156'></a><a href='#L156'>156</a>
<a name='L157'></a><a href='#L157'>157</a>
<a name='L158'></a><a href='#L158'>158</a>
<a name='L159'></a><a href='#L159'>159</a>
<a name='L160'></a><a href='#L160'>160</a>
<a name='L161'></a><a href='#L161'>161</a>
<a name='L162'></a><a href='#L162'>162</a>
<a name='L163'></a><a href='#L163'>163</a>
<a name='L164'></a><a href='#L164'>164</a>
<a name='L165'></a><a href='#L165'>165</a>
<a name='L166'></a><a href='#L166'>166</a>
<a name='L167'></a><a href='#L167'>167</a>
<a name='L168'></a><a href='#L168'>168</a>
<a name='L169'></a><a href='#L169'>169</a>
<a name='L170'></a><a href='#L170'>170</a>
<a name='L171'></a><a href='#L171'>171</a>
<a name='L172'></a><a href='#L172'>172</a>
<a name='L173'></a><a href='#L173'>173</a>
<a name='L174'></a><a href='#L174'>174</a>
<a name='L175'></a><a href='#L175'>175</a>
<a name='L176'></a><a href='#L176'>176</a>
<a name='L177'></a><a href='#L177'>177</a>
<a name='L178'></a><a href='#L178'>178</a>
<a name='L179'></a><a href='#L179'>179</a>
<a name='L180'></a><a href='#L180'>180</a>
<a name='L181'></a><a href='#L181'>181</a>
<a name='L182'></a><a href='#L182'>182</a>
<a name='L183'></a><a href='#L183'>183</a>
<a name='L184'></a><a href='#L184'>184</a>
<a name='L185'></a><a href='#L185'>185</a>
<a name='L186'></a><a href='#L186'>186</a>
<a name='L187'></a><a href='#L187'>187</a>
<a name='L188'></a><a href='#L188'>188</a>
<a name='L189'></a><a href='#L189'>189</a>
<a name='L190'></a><a href='#L190'>190</a>
<a name='L191'></a><a href='#L191'>191</a>
<a name='L192'></a><a href='#L192'>192</a>
<a name='L193'></a><a href='#L193'>193</a>
<a name='L194'></a><a href='#L194'>194</a>
<a name='L195'></a><a href='#L195'>195</a>
<a name='L196'></a><a href='#L196'>196</a>
<a name='L197'></a><a href='#L197'>197</a>
<a name='L198'></a><a href='#L198'>198</a>
<a name='L199'></a><a href='#L199'>199</a>
<a name='L200'></a><a href='#L200'>200</a>
<a name='L201'></a><a href='#L201'>201</a>
<a name='L202'></a><a href='#L202'>202</a>
<a name='L203'></a><a href='#L203'>203</a>
<a name='L204'></a><a href='#L204'>204</a>
<a name='L205'></a><a href='#L205'>205</a>
<a name='L206'></a><a href='#L206'>206</a>
<a name='L207'></a><a href='#L207'>207</a>
<a name='L208'></a><a href='#L208'>208</a>
<a name='L209'></a><a href='#L209'>209</a>
<a name='L210'></a><a href='#L210'>210</a>
<a name='L211'></a><a href='#L211'>211</a>
<a name='L212'></a><a href='#L212'>212</a>
<a name='L213'></a><a href='#L213'>213</a>
<a name='L214'></a><a href='#L214'>214</a>
<a name='L215'></a><a href='#L215'>215</a>
<a name='L216'></a><a href='#L216'>216</a>
<a name='L217'></a><a href='#L217'>217</a>
<a name='L218'></a><a href='#L218'>218</a>
<a name='L219'></a><a href='#L219'>219</a>
<a name='L220'></a><a href='#L220'>220</a>
<a name='L221'></a><a href='#L221'>221</a>
<a name='L222'></a><a href='#L222'>222</a>
<a name='L223'></a><a href='#L223'>223</a>
<a name='L224'></a><a href='#L224'>224</a>
<a name='L225'></a><a href='#L225'>225</a>
<a name='L226'></a><a href='#L226'>226</a>
<a name='L227'></a><a href='#L227'>227</a>
<a name='L228'></a><a href='#L228'>228</a>
<a name='L229'></a><a href='#L229'>229</a>
<a name='L230'></a><a href='#L230'>230</a>
<a name='L231'></a><a href='#L231'>231</a>
<a name='L232'></a><a href='#L232'>232</a>
<a name='L233'></a><a href='#L233'>233</a>
<a name='L234'></a><a href='#L234'>234</a>
<a name='L235'></a><a href='#L235'>235</a>
<a name='L236'></a><a href='#L236'>236</a>
<a name='L237'></a><a href='#L237'>237</a>
<a name='L238'></a><a href='#L238'>238</a>
<a name='L239'></a><a href='#L239'>239</a>
<a name='L240'></a><a href='#L240'>240</a>
<a name='L241'></a><a href='#L241'>241</a>
<a name='L242'></a><a href='#L242'>242</a>
<a name='L243'></a><a href='#L243'>243</a>
<a name='L244'></a><a href='#L244'>244</a>
<a name='L245'></a><a href='#L245'>245</a>
<a name='L246'></a><a href='#L246'>246</a>
<a name='L247'></a><a href='#L247'>247</a>
<a name='L248'></a><a href='#L248'>248</a>
<a name='L249'></a><a href='#L249'>249</a>
<a name='L250'></a><a href='#L250'>250</a>
<a name='L251'></a><a href='#L251'>251</a>
<a name='L252'></a><a href='#L252'>252</a>
<a name='L253'></a><a href='#L253'>253</a>
<a name='L254'></a><a href='#L254'>254</a>
<a name='L255'></a><a href='#L255'>255</a>
<a name='L256'></a><a href='#L256'>256</a>
<a name='L257'></a><a href='#L257'>257</a>
<a name='L258'></a><a href='#L258'>258</a>
<a name='L259'></a><a href='#L259'>259</a>
<a name='L260'></a><a href='#L260'>260</a>
<a name='L261'></a><a href='#L261'>261</a>
<a name='L262'></a><a href='#L262'>262</a>
<a name='L263'></a><a href='#L263'>263</a>
<a name='L264'></a><a href='#L264'>264</a>
<a name='L265'></a><a href='#L265'>265</a>
<a name='L266'></a><a href='#L266'>266</a>
<a name='L267'></a><a href='#L267'>267</a>
<a name='L268'></a><a href='#L268'>268</a>
<a name='L269'></a><a href='#L269'>269</a>
<a name='L270'></a><a href='#L270'>270</a>
<a name='L271'></a><a href='#L271'>271</a>
<a name='L272'></a><a href='#L272'>272</a>
<a name='L273'></a><a href='#L273'>273</a>
<a name='L274'></a><a href='#L274'>274</a>
<a name='L275'></a><a href='#L275'>275</a>
<a name='L276'></a><a href='#L276'>276</a>
<a name='L277'></a><a href='#L277'>277</a>
<a name='L278'></a><a href='#L278'>278</a>
<a name='L279'></a><a href='#L279'>279</a>
<a name='L280'></a><a href='#L280'>280</a>
<a name='L281'></a><a href='#L281'>281</a>
<a name='L282'></a><a href='#L282'>282</a>
<a name='L283'></a><a href='#L283'>283</a>
<a name='L284'></a><a href='#L284'>284</a>
<a name='L285'></a><a href='#L285'>285</a>
<a name='L286'></a><a href='#L286'>286</a>
<a name='L287'></a><a href='#L287'>287</a>
<a name='L288'></a><a href='#L288'>288</a></td><td class="line-coverage quiet"><span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js"><span class="cstat-no" title="statement not covered" >import { useEffect } from 'react'<span class="fstat-no" title="function not covered" ><span class="branch-0 cbranch-no" title="branch not covered" ></span></span></span>
<span class="cstat-no" title="statement not covered" >import {</span>
Box,
Typography,
Paper,
Card,
CardContent,
Chip,
List,
ListItem,
ListItemText,
Alert,
CircularProgress,
} from '@mui/material'
<span class="cstat-no" title="statement not covered" >import {</span>
Person,
LocationOn,
Home,
Description,
Language,
Verified,
} from '@mui/icons-material'
<span class="cstat-no" title="statement not covered" >import { useAppDispatch, useAppSelector } from '../store'</span>
<span class="cstat-no" title="statement not covered" >import { extractDocument } from '../store/documentSlice'</span>
<span class="cstat-no" title="statement not covered" >import { Layout } from '../components/Layout'</span>
&nbsp;
<span class="cstat-no" title="statement not covered" >export default function ExtractionView() {</span>
<span class="cstat-no" title="statement not covered" > const dispatch = useAppDispatch()</span>
<span class="cstat-no" title="statement not covered" > const { currentDocument, extractionResult, loading } = useAppSelector(</span>
<span class="cstat-no" title="statement not covered" > (state) =&gt; state.document</span>
<span class="cstat-no" title="statement not covered" > )</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > useEffect(() =&gt; {</span>
<span class="cstat-no" title="statement not covered" > if (currentDocument &amp;&amp; !extractionResult) {</span>
<span class="cstat-no" title="statement not covered" > dispatch(extractDocument(currentDocument.id))</span>
<span class="cstat-no" title="statement not covered" > }</span>
<span class="cstat-no" title="statement not covered" > }, [currentDocument, extractionResult, dispatch])</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > if (!currentDocument) {</span>
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;Layout&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Alert severity="info"&gt;</span>
Veuillez d'abord téléverser et sélectionner un document.
<span class="cstat-no" title="statement not covered" > &lt;/Alert&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Layout&gt;</span>
)
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > if (loading) {</span>
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;Layout&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', justifyContent: 'center', mt: 4 }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CircularProgress /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography sx={{ ml: 2 }}&gt;Extraction en cours...&lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Layout&gt;</span>
)
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > if (!extractionResult) {</span>
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;Layout&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Alert severity="warning"&gt;</span>
Aucun résultat d'extraction disponible.
<span class="cstat-no" title="statement not covered" > &lt;/Alert&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Layout&gt;</span>
)
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;Layout&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h4" gutterBottom&gt;</span>
Extraction des données
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}&gt;</span>
{/* Informations générales */}
<span class="cstat-no" title="statement not covered" > &lt;Paper sx={{ p: 2 }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
Informations générales
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > icon={&lt;Language /&gt;}</span>
<span class="cstat-no" title="statement not covered" > label={`Langue: ${extractionResult.language}`}</span>
<span class="cstat-no" title="statement not covered" > color="primary"</span>
<span class="cstat-no" title="statement not covered" > variant="outlined"</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > icon={&lt;Description /&gt;}</span>
<span class="cstat-no" title="statement not covered" > label={`Type: ${extractionResult.documentType}`}</span>
<span class="cstat-no" title="statement not covered" > color="secondary"</span>
<span class="cstat-no" title="statement not covered" > variant="outlined"</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > icon={&lt;Verified /&gt;}</span>
<span class="cstat-no" title="statement not covered" > label={`Confiance: ${(extractionResult.confidence * 100).toFixed(1)}%`}</span>
<span class="cstat-no" title="statement not covered" > color={extractionResult.confidence &gt; 0.8 ? 'success' : 'warning'}</span>
<span class="cstat-no" title="statement not covered" > variant="outlined"</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Paper&gt;</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', gap: 3, flexWrap: 'wrap' }}&gt;</span>
{/* Identités */}
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ flex: '1 1 300px' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Person sx={{ mr: 1, verticalAlign: 'middle' }} /&gt;</span>
<span class="cstat-no" title="statement not covered" > Identités ({extractionResult.identities?.length || 0})</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;List dense&gt;</span>
<span class="cstat-no" title="statement not covered" > {(extractionResult.identities || []).map((identity, index) =&gt; (</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItem key={index}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItemText</span>
<span class="cstat-no" title="statement not covered" > primary={</span>
<span class="cstat-no" title="statement not covered" > identity.type === 'person'</span>
<span class="cstat-no" title="statement not covered" > ? `${identity.firstName} ${identity.lastName}`</span>
<span class="cstat-no" title="statement not covered" > : identity.companyName</span>
}
<span class="cstat-no" title="statement not covered" > secondary={</span>
<span class="cstat-no" title="statement not covered" > &lt;Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="caption" display="block"&gt;</span>
<span class="cstat-no" title="statement not covered" > Type: {identity.type}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > {identity.birthDate &amp;&amp; (</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="caption" display="block"&gt;</span>
<span class="cstat-no" title="statement not covered" > Naissance: {identity.birthDate}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
)}
<span class="cstat-no" title="statement not covered" > {identity.nationality &amp;&amp; (</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="caption" display="block"&gt;</span>
<span class="cstat-no" title="statement not covered" > Nationalité: {identity.nationality}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
)}
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="caption" display="block"&gt;</span>
<span class="cstat-no" title="statement not covered" > Confiance: {(identity.confidence * 100).toFixed(1)}%</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
}
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/ListItem&gt;</span>
<span class="cstat-no" title="statement not covered" > ))}</span>
<span class="cstat-no" title="statement not covered" > &lt;/List&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
&nbsp;
{/* Adresses */}
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ flex: '1 1 300px' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;LocationOn sx={{ mr: 1, verticalAlign: 'middle' }} /&gt;</span>
<span class="cstat-no" title="statement not covered" > Adresses ({extractionResult.addresses?.length || 0})</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;List dense&gt;</span>
<span class="cstat-no" title="statement not covered" > {(extractionResult.addresses || []).map((address, index) =&gt; (</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItem key={index}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItemText</span>
<span class="cstat-no" title="statement not covered" > primary={`${address.street}, ${address.city}`}</span>
<span class="cstat-no" title="statement not covered" > secondary={`${address.postalCode} ${address.country}`}</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/ListItem&gt;</span>
<span class="cstat-no" title="statement not covered" > ))}</span>
<span class="cstat-no" title="statement not covered" > &lt;/List&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ display: 'flex', gap: 3, flexWrap: 'wrap' }}&gt;</span>
{/* Biens */}
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ flex: '1 1 300px' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Home sx={{ mr: 1, verticalAlign: 'middle' }} /&gt;</span>
<span class="cstat-no" title="statement not covered" > Biens ({extractionResult.properties?.length || 0})</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;List dense&gt;</span>
<span class="cstat-no" title="statement not covered" > {(extractionResult.properties || []).map((property, index) =&gt; (</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItem key={index}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItemText</span>
<span class="cstat-no" title="statement not covered" > primary={`${property.type} - ${property.address.city}`}</span>
<span class="cstat-no" title="statement not covered" > secondary={</span>
<span class="cstat-no" title="statement not covered" > &lt;Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="caption" display="block"&gt;</span>
<span class="cstat-no" title="statement not covered" > {property.address.street}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > {property.surface &amp;&amp; (</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="caption" display="block"&gt;</span>
<span class="cstat-no" title="statement not covered" > Surface: {property.surface} m²</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
)}
<span class="cstat-no" title="statement not covered" > {property.cadastralReference &amp;&amp; (</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="caption" display="block"&gt;</span>
<span class="cstat-no" title="statement not covered" > Cadastre: {property.cadastralReference}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
)}
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
}
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/ListItem&gt;</span>
<span class="cstat-no" title="statement not covered" > ))}</span>
<span class="cstat-no" title="statement not covered" > &lt;/List&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
&nbsp;
{/* Contrats */}
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ flex: '1 1 300px' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Description sx={{ mr: 1, verticalAlign: 'middle' }} /&gt;</span>
<span class="cstat-no" title="statement not covered" > Contrats ({extractionResult.contracts?.length || 0})</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;List dense&gt;</span>
<span class="cstat-no" title="statement not covered" > {(extractionResult.contracts || []).map((contract, index) =&gt; (</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItem key={index}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItemText</span>
<span class="cstat-no" title="statement not covered" > primary={`${contract.type} - ${contract.amount ? `${contract.amount}€` : 'Montant non spécifié'}`}</span>
<span class="cstat-no" title="statement not covered" > secondary={</span>
<span class="cstat-no" title="statement not covered" > &lt;Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="caption" display="block"&gt;</span>
<span class="cstat-no" title="statement not covered" > Parties: {contract.parties.length}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > {contract.date &amp;&amp; (</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="caption" display="block"&gt;</span>
<span class="cstat-no" title="statement not covered" > Date: {contract.date}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
)}
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="caption" display="block"&gt;</span>
<span class="cstat-no" title="statement not covered" > Clauses: {contract.clauses.length}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
}
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/ListItem&gt;</span>
<span class="cstat-no" title="statement not covered" > ))}</span>
<span class="cstat-no" title="statement not covered" > &lt;/List&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
&nbsp;
{/* Signatures */}
<span class="cstat-no" title="statement not covered" > &lt;Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
<span class="cstat-no" title="statement not covered" > Signatures détectées ({extractionResult.signatures?.length || 0})</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;List dense&gt;</span>
<span class="cstat-no" title="statement not covered" > {(extractionResult.signatures || []).map((signature, index) =&gt; (</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItem key={index}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;ListItemText primary={signature} /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/ListItem&gt;</span>
<span class="cstat-no" title="statement not covered" > ))}</span>
<span class="cstat-no" title="statement not covered" > &lt;/List&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Card&gt;</span>
&nbsp;
{/* Texte extrait */}
<span class="cstat-no" title="statement not covered" > &lt;Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
Texte extrait
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Paper</span>
<span class="cstat-no" title="statement not covered" > sx={{</span>
<span class="cstat-no" title="statement not covered" > p: 2,</span>
<span class="cstat-no" title="statement not covered" > bgcolor: 'grey.50',</span>
<span class="cstat-no" title="statement not covered" > maxHeight: 300,</span>
<span class="cstat-no" title="statement not covered" > overflow: 'auto',</span>
<span class="cstat-no" title="statement not covered" > }}</span>
&gt;
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="body2" sx={{ whiteSpace: 'pre-wrap' }}&gt;</span>
<span class="cstat-no" title="statement not covered" > {extractionResult.text}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Paper&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/CardContent&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Card&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Layout&gt;</span>
)
<span class="cstat-no" title="statement not covered" >}</span></pre></td></tr></table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,595 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/views/UploadView.tsx</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> / <a href="index.html">src/views</a> UploadView.tsx</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/142</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/1</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/142</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<pre><table class="coverage">
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
<a name='L2'></a><a href='#L2'>2</a>
<a name='L3'></a><a href='#L3'>3</a>
<a name='L4'></a><a href='#L4'>4</a>
<a name='L5'></a><a href='#L5'>5</a>
<a name='L6'></a><a href='#L6'>6</a>
<a name='L7'></a><a href='#L7'>7</a>
<a name='L8'></a><a href='#L8'>8</a>
<a name='L9'></a><a href='#L9'>9</a>
<a name='L10'></a><a href='#L10'>10</a>
<a name='L11'></a><a href='#L11'>11</a>
<a name='L12'></a><a href='#L12'>12</a>
<a name='L13'></a><a href='#L13'>13</a>
<a name='L14'></a><a href='#L14'>14</a>
<a name='L15'></a><a href='#L15'>15</a>
<a name='L16'></a><a href='#L16'>16</a>
<a name='L17'></a><a href='#L17'>17</a>
<a name='L18'></a><a href='#L18'>18</a>
<a name='L19'></a><a href='#L19'>19</a>
<a name='L20'></a><a href='#L20'>20</a>
<a name='L21'></a><a href='#L21'>21</a>
<a name='L22'></a><a href='#L22'>22</a>
<a name='L23'></a><a href='#L23'>23</a>
<a name='L24'></a><a href='#L24'>24</a>
<a name='L25'></a><a href='#L25'>25</a>
<a name='L26'></a><a href='#L26'>26</a>
<a name='L27'></a><a href='#L27'>27</a>
<a name='L28'></a><a href='#L28'>28</a>
<a name='L29'></a><a href='#L29'>29</a>
<a name='L30'></a><a href='#L30'>30</a>
<a name='L31'></a><a href='#L31'>31</a>
<a name='L32'></a><a href='#L32'>32</a>
<a name='L33'></a><a href='#L33'>33</a>
<a name='L34'></a><a href='#L34'>34</a>
<a name='L35'></a><a href='#L35'>35</a>
<a name='L36'></a><a href='#L36'>36</a>
<a name='L37'></a><a href='#L37'>37</a>
<a name='L38'></a><a href='#L38'>38</a>
<a name='L39'></a><a href='#L39'>39</a>
<a name='L40'></a><a href='#L40'>40</a>
<a name='L41'></a><a href='#L41'>41</a>
<a name='L42'></a><a href='#L42'>42</a>
<a name='L43'></a><a href='#L43'>43</a>
<a name='L44'></a><a href='#L44'>44</a>
<a name='L45'></a><a href='#L45'>45</a>
<a name='L46'></a><a href='#L46'>46</a>
<a name='L47'></a><a href='#L47'>47</a>
<a name='L48'></a><a href='#L48'>48</a>
<a name='L49'></a><a href='#L49'>49</a>
<a name='L50'></a><a href='#L50'>50</a>
<a name='L51'></a><a href='#L51'>51</a>
<a name='L52'></a><a href='#L52'>52</a>
<a name='L53'></a><a href='#L53'>53</a>
<a name='L54'></a><a href='#L54'>54</a>
<a name='L55'></a><a href='#L55'>55</a>
<a name='L56'></a><a href='#L56'>56</a>
<a name='L57'></a><a href='#L57'>57</a>
<a name='L58'></a><a href='#L58'>58</a>
<a name='L59'></a><a href='#L59'>59</a>
<a name='L60'></a><a href='#L60'>60</a>
<a name='L61'></a><a href='#L61'>61</a>
<a name='L62'></a><a href='#L62'>62</a>
<a name='L63'></a><a href='#L63'>63</a>
<a name='L64'></a><a href='#L64'>64</a>
<a name='L65'></a><a href='#L65'>65</a>
<a name='L66'></a><a href='#L66'>66</a>
<a name='L67'></a><a href='#L67'>67</a>
<a name='L68'></a><a href='#L68'>68</a>
<a name='L69'></a><a href='#L69'>69</a>
<a name='L70'></a><a href='#L70'>70</a>
<a name='L71'></a><a href='#L71'>71</a>
<a name='L72'></a><a href='#L72'>72</a>
<a name='L73'></a><a href='#L73'>73</a>
<a name='L74'></a><a href='#L74'>74</a>
<a name='L75'></a><a href='#L75'>75</a>
<a name='L76'></a><a href='#L76'>76</a>
<a name='L77'></a><a href='#L77'>77</a>
<a name='L78'></a><a href='#L78'>78</a>
<a name='L79'></a><a href='#L79'>79</a>
<a name='L80'></a><a href='#L80'>80</a>
<a name='L81'></a><a href='#L81'>81</a>
<a name='L82'></a><a href='#L82'>82</a>
<a name='L83'></a><a href='#L83'>83</a>
<a name='L84'></a><a href='#L84'>84</a>
<a name='L85'></a><a href='#L85'>85</a>
<a name='L86'></a><a href='#L86'>86</a>
<a name='L87'></a><a href='#L87'>87</a>
<a name='L88'></a><a href='#L88'>88</a>
<a name='L89'></a><a href='#L89'>89</a>
<a name='L90'></a><a href='#L90'>90</a>
<a name='L91'></a><a href='#L91'>91</a>
<a name='L92'></a><a href='#L92'>92</a>
<a name='L93'></a><a href='#L93'>93</a>
<a name='L94'></a><a href='#L94'>94</a>
<a name='L95'></a><a href='#L95'>95</a>
<a name='L96'></a><a href='#L96'>96</a>
<a name='L97'></a><a href='#L97'>97</a>
<a name='L98'></a><a href='#L98'>98</a>
<a name='L99'></a><a href='#L99'>99</a>
<a name='L100'></a><a href='#L100'>100</a>
<a name='L101'></a><a href='#L101'>101</a>
<a name='L102'></a><a href='#L102'>102</a>
<a name='L103'></a><a href='#L103'>103</a>
<a name='L104'></a><a href='#L104'>104</a>
<a name='L105'></a><a href='#L105'>105</a>
<a name='L106'></a><a href='#L106'>106</a>
<a name='L107'></a><a href='#L107'>107</a>
<a name='L108'></a><a href='#L108'>108</a>
<a name='L109'></a><a href='#L109'>109</a>
<a name='L110'></a><a href='#L110'>110</a>
<a name='L111'></a><a href='#L111'>111</a>
<a name='L112'></a><a href='#L112'>112</a>
<a name='L113'></a><a href='#L113'>113</a>
<a name='L114'></a><a href='#L114'>114</a>
<a name='L115'></a><a href='#L115'>115</a>
<a name='L116'></a><a href='#L116'>116</a>
<a name='L117'></a><a href='#L117'>117</a>
<a name='L118'></a><a href='#L118'>118</a>
<a name='L119'></a><a href='#L119'>119</a>
<a name='L120'></a><a href='#L120'>120</a>
<a name='L121'></a><a href='#L121'>121</a>
<a name='L122'></a><a href='#L122'>122</a>
<a name='L123'></a><a href='#L123'>123</a>
<a name='L124'></a><a href='#L124'>124</a>
<a name='L125'></a><a href='#L125'>125</a>
<a name='L126'></a><a href='#L126'>126</a>
<a name='L127'></a><a href='#L127'>127</a>
<a name='L128'></a><a href='#L128'>128</a>
<a name='L129'></a><a href='#L129'>129</a>
<a name='L130'></a><a href='#L130'>130</a>
<a name='L131'></a><a href='#L131'>131</a>
<a name='L132'></a><a href='#L132'>132</a>
<a name='L133'></a><a href='#L133'>133</a>
<a name='L134'></a><a href='#L134'>134</a>
<a name='L135'></a><a href='#L135'>135</a>
<a name='L136'></a><a href='#L136'>136</a>
<a name='L137'></a><a href='#L137'>137</a>
<a name='L138'></a><a href='#L138'>138</a>
<a name='L139'></a><a href='#L139'>139</a>
<a name='L140'></a><a href='#L140'>140</a>
<a name='L141'></a><a href='#L141'>141</a>
<a name='L142'></a><a href='#L142'>142</a>
<a name='L143'></a><a href='#L143'>143</a>
<a name='L144'></a><a href='#L144'>144</a>
<a name='L145'></a><a href='#L145'>145</a>
<a name='L146'></a><a href='#L146'>146</a>
<a name='L147'></a><a href='#L147'>147</a>
<a name='L148'></a><a href='#L148'>148</a>
<a name='L149'></a><a href='#L149'>149</a>
<a name='L150'></a><a href='#L150'>150</a>
<a name='L151'></a><a href='#L151'>151</a>
<a name='L152'></a><a href='#L152'>152</a>
<a name='L153'></a><a href='#L153'>153</a>
<a name='L154'></a><a href='#L154'>154</a>
<a name='L155'></a><a href='#L155'>155</a>
<a name='L156'></a><a href='#L156'>156</a>
<a name='L157'></a><a href='#L157'>157</a>
<a name='L158'></a><a href='#L158'>158</a>
<a name='L159'></a><a href='#L159'>159</a>
<a name='L160'></a><a href='#L160'>160</a>
<a name='L161'></a><a href='#L161'>161</a>
<a name='L162'></a><a href='#L162'>162</a>
<a name='L163'></a><a href='#L163'>163</a>
<a name='L164'></a><a href='#L164'>164</a>
<a name='L165'></a><a href='#L165'>165</a>
<a name='L166'></a><a href='#L166'>166</a>
<a name='L167'></a><a href='#L167'>167</a>
<a name='L168'></a><a href='#L168'>168</a>
<a name='L169'></a><a href='#L169'>169</a>
<a name='L170'></a><a href='#L170'>170</a>
<a name='L171'></a><a href='#L171'>171</a></td><td class="line-coverage quiet"><span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js"><span class="cstat-no" title="statement not covered" >import { useCallback, useState } from 'react'<span class="fstat-no" title="function not covered" ><span class="branch-0 cbranch-no" title="branch not covered" ></span></span></span>
<span class="cstat-no" title="statement not covered" >import { useDropzone } from 'react-dropzone'</span>
<span class="cstat-no" title="statement not covered" >import { Box, Typography, Paper, CircularProgress, Alert, Button, Chip, Grid } from '@mui/material'</span>
<span class="cstat-no" title="statement not covered" >import {</span>
CloudUpload,
CheckCircle,
Error,
HourglassEmpty,
Visibility,
} from '@mui/icons-material'
<span class="cstat-no" title="statement not covered" >import { useAppDispatch, useAppSelector } from '../store'</span>
<span class="cstat-no" title="statement not covered" >import { uploadDocument } from '../store/documentSlice'</span>
<span class="cstat-no" title="statement not covered" >import { Layout } from '../components/Layout'</span>
<span class="cstat-no" title="statement not covered" >import { FilePreview } from '../components/FilePreview'</span>
import type { Document } from '../types'
&nbsp;
<span class="cstat-no" title="statement not covered" >export default function UploadView() {</span>
<span class="cstat-no" title="statement not covered" > const dispatch = useAppDispatch()</span>
<span class="cstat-no" title="statement not covered" > const { documents, error } = useAppSelector((state) =&gt; state.document)</span>
<span class="cstat-no" title="statement not covered" > const [previewDocument, setPreviewDocument] = useState&lt;Document | null&gt;(null)</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > const onDrop = useCallback(</span>
<span class="cstat-no" title="statement not covered" > (acceptedFiles: File[]) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > acceptedFiles.forEach((file) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > dispatch(uploadDocument(file))</span>
<span class="cstat-no" title="statement not covered" > })</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > [dispatch]</span>
<span class="cstat-no" title="statement not covered" > )</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > const { getRootProps, getInputProps, isDragActive } = useDropzone({</span>
<span class="cstat-no" title="statement not covered" > onDrop,</span>
<span class="cstat-no" title="statement not covered" > accept: {</span>
<span class="cstat-no" title="statement not covered" > 'application/pdf': ['.pdf'],</span>
<span class="cstat-no" title="statement not covered" > 'image/*': ['.png', '.jpg', '.jpeg', '.tiff'],</span>
<span class="cstat-no" title="statement not covered" > 'text/plain': ['.txt'],</span>
<span class="cstat-no" title="statement not covered" > 'text/markdown': ['.md'],</span>
<span class="cstat-no" title="statement not covered" > 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['.docx'],</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > multiple: true,</span>
<span class="cstat-no" title="statement not covered" > })</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > const getStatusIcon = (status: string) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > switch (status) {</span>
<span class="cstat-no" title="statement not covered" > case 'completed':</span>
<span class="cstat-no" title="statement not covered" > return &lt;CheckCircle color="success" /&gt;</span>
<span class="cstat-no" title="statement not covered" > case 'error':</span>
<span class="cstat-no" title="statement not covered" > return &lt;Error color="error" /&gt;</span>
<span class="cstat-no" title="statement not covered" > case 'processing':</span>
<span class="cstat-no" title="statement not covered" > return &lt;CircularProgress size={20} /&gt;</span>
<span class="cstat-no" title="statement not covered" > default:</span>
<span class="cstat-no" title="statement not covered" > return &lt;HourglassEmpty color="action" /&gt;</span>
<span class="cstat-no" title="statement not covered" > }</span>
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > const getStatusColor = (status: string) =&gt; {</span>
<span class="cstat-no" title="statement not covered" > switch (status) {</span>
<span class="cstat-no" title="statement not covered" > case 'completed':</span>
<span class="cstat-no" title="statement not covered" > return 'success'</span>
<span class="cstat-no" title="statement not covered" > case 'error':</span>
<span class="cstat-no" title="statement not covered" > return 'error'</span>
<span class="cstat-no" title="statement not covered" > case 'processing':</span>
<span class="cstat-no" title="statement not covered" > return 'warning'</span>
<span class="cstat-no" title="statement not covered" > default:</span>
<span class="cstat-no" title="statement not covered" > return 'default'</span>
<span class="cstat-no" title="statement not covered" > }</span>
<span class="cstat-no" title="statement not covered" > }</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > return (</span>
<span class="cstat-no" title="statement not covered" > &lt;Layout&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h4" gutterBottom&gt;</span>
Téléversement de documents
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > &lt;Paper</span>
<span class="cstat-no" title="statement not covered" > {...getRootProps()}</span>
<span class="cstat-no" title="statement not covered" > sx={{</span>
<span class="cstat-no" title="statement not covered" > p: 4,</span>
<span class="cstat-no" title="statement not covered" > textAlign: 'center',</span>
<span class="cstat-no" title="statement not covered" > cursor: 'pointer',</span>
<span class="cstat-no" title="statement not covered" > border: '2px dashed',</span>
<span class="cstat-no" title="statement not covered" > borderColor: isDragActive ? 'primary.main' : 'grey.300',</span>
<span class="cstat-no" title="statement not covered" > bgcolor: isDragActive ? 'action.hover' : 'background.paper',</span>
<span class="cstat-no" title="statement not covered" > '&amp;:hover': {</span>
<span class="cstat-no" title="statement not covered" > borderColor: 'primary.main',</span>
<span class="cstat-no" title="statement not covered" > bgcolor: 'action.hover',</span>
<span class="cstat-no" title="statement not covered" > },</span>
<span class="cstat-no" title="statement not covered" > }}</span>
&gt;
<span class="cstat-no" title="statement not covered" > &lt;input {...getInputProps()} /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;CloudUpload sx={{ fontSize: 48, color: 'primary.main', mb: 2 }} /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
<span class="cstat-no" title="statement not covered" > {isDragActive</span>
<span class="cstat-no" title="statement not covered" > ? 'Déposez les fichiers ici...'</span>
<span class="cstat-no" title="statement not covered" > : 'Glissez-déposez vos documents ou cliquez pour sélectionner'}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="body2" color="text.secondary"&gt;</span>
Formats acceptés: PDF, PNG, JPG, JPEG, TIFF, TXT, MD, DOCX
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Paper&gt;</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > {error &amp;&amp; (</span>
<span class="cstat-no" title="statement not covered" > &lt;Alert severity="error" sx={{ mt: 2 }}&gt;</span>
<span class="cstat-no" title="statement not covered" > {error}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Alert&gt;</span>
)}
&nbsp;
<span class="cstat-no" title="statement not covered" > {documents.length &gt; 0 &amp;&amp; (</span>
<span class="cstat-no" title="statement not covered" > &lt;Box sx={{ mt: 3 }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="h6" gutterBottom&gt;</span>
<span class="cstat-no" title="statement not covered" > Documents téléversés ({documents.length})</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > &lt;Grid container spacing={2}&gt;</span>
<span class="cstat-no" title="statement not covered" > {documents.map((doc, index) =&gt; (</span>
<span class="cstat-no" title="statement not covered" > &lt;Grid size={{ xs: 12, md: 6 }} key={`${doc.id}-${index}`}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Paper sx={{ p: 2 }}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box display="flex" justifyContent="space-between" alignItems="center" mb={2}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box display="flex" alignItems="center" gap={1}&gt;</span>
<span class="cstat-no" title="statement not covered" > {getStatusIcon(doc.status)}</span>
<span class="cstat-no" title="statement not covered" > &lt;Typography variant="subtitle1" noWrap&gt;</span>
<span class="cstat-no" title="statement not covered" > {doc.name}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Typography&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Box display="flex" gap={1}&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Button</span>
<span class="cstat-no" title="statement not covered" > size="small"</span>
<span class="cstat-no" title="statement not covered" > startIcon={&lt;Visibility /&gt;}</span>
<span class="cstat-no" title="statement not covered" > onClick={() =&gt; setPreviewDocument(doc)}</span>
<span class="cstat-no" title="statement not covered" > disabled={doc.status !== 'completed'}</span>
<span class="cstat-no" title="statement not covered" > &gt;</span>
Aperçu
<span class="cstat-no" title="statement not covered" > &lt;/Button&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
&nbsp;
<span class="cstat-no" title="statement not covered" > &lt;Box display="flex" gap={1} flexWrap="wrap"&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > label={doc.functionalType || doc.mimeType}</span>
<span class="cstat-no" title="statement not covered" > size="small"</span>
<span class="cstat-no" title="statement not covered" > variant="outlined"</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > label={doc.status}</span>
<span class="cstat-no" title="statement not covered" > size="small"</span>
<span class="cstat-no" title="statement not covered" > color={getStatusColor(doc.status) as 'success' | 'error' | 'warning' | 'default'}</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;Chip</span>
<span class="cstat-no" title="statement not covered" > label={`${(doc.size / 1024 / 1024).toFixed(2)} MB`}</span>
<span class="cstat-no" title="statement not covered" > size="small"</span>
<span class="cstat-no" title="statement not covered" > variant="outlined"</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Paper&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Grid&gt;</span>
<span class="cstat-no" title="statement not covered" > ))}</span>
<span class="cstat-no" title="statement not covered" > &lt;/Grid&gt;</span>
<span class="cstat-no" title="statement not covered" > &lt;/Box&gt;</span>
)}
&nbsp;
{/* Aperçu du document */}
<span class="cstat-no" title="statement not covered" > {previewDocument &amp;&amp; (</span>
<span class="cstat-no" title="statement not covered" > &lt;FilePreview</span>
<span class="cstat-no" title="statement not covered" > document={previewDocument}</span>
<span class="cstat-no" title="statement not covered" > onClose={() =&gt; setPreviewDocument(null)}</span>
<span class="cstat-no" title="statement not covered" > /&gt;</span>
)}
<span class="cstat-no" title="statement not covered" > &lt;/Layout&gt;</span>
)
<span class="cstat-no" title="statement not covered" >}</span>
&nbsp;</pre></td></tr></table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,176 @@
<!doctype html>
<html lang="en">
<head>
<title>Code coverage report for src/views</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../../prettify.css" />
<link rel="stylesheet" href="../../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(../../sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
<h1><a href="../../index.html">All files</a> src/views</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Statements</span>
<span class='fraction'>0/978</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Branches</span>
<span class='fraction'>0/5</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Functions</span>
<span class='fraction'>0/5</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">0% </span>
<span class="quiet">Lines</span>
<span class='fraction'>0/978</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
</template>
</div>
<div class='status-line low'></div>
<div class="pad1">
<table class="coverage-summary">
<thead>
<tr>
<th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
<th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
<th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
<th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
<th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
<th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
<th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
<th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
</tr>
</thead>
<tbody><tr>
<td class="file low" data-value="AnalyseView.tsx"><a href="AnalyseView.tsx.html">AnalyseView.tsx</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="191" class="abs low">0/191</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="191" class="abs low">0/191</td>
</tr>
<tr>
<td class="file low" data-value="ConseilView.tsx"><a href="ConseilView.tsx.html">ConseilView.tsx</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="187" class="abs low">0/187</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="187" class="abs low">0/187</td>
</tr>
<tr>
<td class="file low" data-value="ContexteView.tsx"><a href="ContexteView.tsx.html">ContexteView.tsx</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="228" class="abs low">0/228</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="228" class="abs low">0/228</td>
</tr>
<tr>
<td class="file low" data-value="ExtractionView.tsx"><a href="ExtractionView.tsx.html">ExtractionView.tsx</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="230" class="abs low">0/230</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="230" class="abs low">0/230</td>
</tr>
<tr>
<td class="file low" data-value="UploadView.tsx"><a href="UploadView.tsx.html">UploadView.tsx</a></td>
<td data-value="0" class="pic low">
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="142" class="abs low">0/142</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="1" class="abs low">0/1</td>
<td data-value="0" class="pct low">0%</td>
<td data-value="142" class="abs low">0/142</td>
</tr>
</tbody>
</table>
</div>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2025-09-11T12:57:49.285Z
</div>
<script src="../../prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="../../sorter.js"></script>
<script src="../../block-navigation.js"></script>
</body>
</html>

View File

@ -0,0 +1,17 @@
version: "3.9"
services:
frontend:
image: "git.4nkweb.com/4nk/4nk-ia-front:${TAG:-dev}"
container_name: 4nk-ia-front
restart: unless-stopped
ports:
- "8080:80"
environment:
- VITE_API_URL=${VITE_API_URL:-http://172.23.0.10:8000}
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost/"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s

125
docs/API.md Normal file
View File

@ -0,0 +1,125 @@
# Documentation API - 4NK IA Front Notarial
## Vue d'ensemble
L'application 4NK IA Front Notarial communique uniquement avec le backend interne pour toutes les
fonctionnalités (upload, extraction, analyse, contexte, conseil).
## API Backend Principal
### Base URL
```text
http://localhost:8000 (développement)
```
### Endpoints
#### Upload de document
```http
POST /api/notary/upload
Content-Type: multipart/form-data
Body: FormData avec le fichier
```
Réponse attendue (champs utilisés par le front) :
```json
{
"document_id": "doc_123456",
"mime_type": "application/pdf",
"functional_type": "CNI"
}
```
Mappage front en `Document` :
```json
{
"id": "doc_123456",
"name": "acte_vente.pdf",
"mimeType": "application/pdf",
"functionalType": "CNI",
"size": 1024000,
"uploadDate": "<date locale>",
"status": "completed",
"previewUrl": "blob:..."
}
```
#### Extraction de données
```http
GET /api/notary/documents/{documentId}
```
#### Analyse du document
```http
GET /api/documents/{documentId}/analyze
```
#### Données contextuelles
```http
GET /api/documents/{documentId}/context
```
#### Conseil IA
```http
GET /api/documents/{documentId}/conseil
```
## APIs Externes
Les APIs externes (Cadastre, Géorisques, Géofoncier, BODACC, Infogreffe) sont appelées côté backend
uniquement. Aucun appel direct côté front.
## Gestion d'erreur
### Codes d'erreur HTTP
- 200 : Succès
- 400 : Requête malformée
- 404 : Ressource non trouvée
- 405 : Méthode non autorisée
- 500 : Erreur serveur interne
### Erreurs de connexion
- ERR_NETWORK : Erreur de réseau
- ERR_CONNECTION_REFUSED : Connexion refusée
- ERR_TIMEOUT : Timeout de la requête
## Configuration
### Variables d'environnement
```env
VITE_API_URL=http://localhost:8000
```
### Configuration Axios
```typescript
const apiClient = axios.create({
baseURL: BASE_URL,
timeout: 60000
})
```
## Authentification
### Headers requis
```http
Authorization: Bearer {token}
Content-Type: application/json
```
## Rate Limiting
- Limites gérées par le backend

282
docs/ARCHITECTURE.md Normal file
View File

@ -0,0 +1,282 @@
# Architecture de l'application 4NK IA Front Notarial
## Vue d'ensemble
L'application 4NK IA Front Notarial est une interface web moderne construite avec React et TypeScript,
conçue pour l'analyse intelligente de documents notariaux.
## Stack technique
### Frontend
- **React 18** : Framework UI avec hooks et composants fonctionnels
- **TypeScript 5.6** : Typage statique pour la robustesse du code
- **Vite 7** : Build tool rapide avec HMR (Hot Module Replacement)
- **Material-UI v6** : Bibliothèque de composants UI professionnels
### Gestion d'état
- **Redux Toolkit** : Gestion d'état centralisée et prévisible
- **React Redux** : Liaison React-Redux avec hooks
- **Async Thunks** : Gestion des actions asynchrones
### Routing et navigation
- **React Router v6** : Navigation côté client avec code splitting
- **Lazy loading** : Chargement à la demande des composants
### HTTP et API
- **Axios** : Client HTTP avec intercepteurs
- **Intercepteurs** : Gestion centralisée des erreurs et authentification
### Tests
- **Vitest** : Framework de test rapide et moderne
- **Testing Library** : Tests d'intégration React
- **JSDOM** : Environnement DOM simulé
- **Coverage V8** : Rapport de couverture de code
### Qualité de code
- **ESLint** : Linting avec règles strictes
- **Prettier** : Formatage automatique du code
- **markdownlint** : Validation des fichiers Markdown
## Structure du projet
```text
src/
├── components/ # Composants réutilisables
│ ├── Layout.tsx # Layout principal avec AppBar et navigation
│ └── NavigationTabs.tsx # Composant de navigation par onglets
├── views/ # Vues principales de l'application
│ ├── UploadView.tsx # Upload et gestion des documents
│ ├── ExtractionView.tsx # Affichage des données extraites
│ ├── AnalyseView.tsx # Analyse et score de vraisemblance
│ ├── ContexteView.tsx # Données contextuelles externes
│ └── ConseilView.tsx # Conseil IA et recommandations
├── store/ # Gestion d'état Redux
│ ├── index.ts # Configuration du store Redux
│ ├── appSlice.ts # État global de l'application
│ └── documentSlice.ts # État des documents et opérations
├── services/ # Services et API
│ └── api.ts # Client API et endpoints
├── types/ # Types et interfaces TypeScript
│ └── index.ts # Définitions de types centralisées
├── main.tsx # Point d'entrée de l'application
└── App.tsx # Composant racine avec routing
```
## Architecture des composants
### Layout et navigation
- **Layout** : Composant wrapper avec AppBar et navigation
- **NavigationTabs** : Navigation par onglets avec React Router
- **AppBar** : Barre de navigation principale
### Vues principales
#### UploadView
- **Dropzone** : Zone de glisser-déposer pour les fichiers
- **FileList** : Liste des documents uploadés
- **Status indicators** : Indicateurs de statut des documents
#### ExtractionView
- **DataDisplay** : Affichage des données extraites
- **ObjectLists** : Listes d'identités, adresses, biens, contrats
- **Confidence scores** : Scores de confiance des extractions
#### AnalyseView
- **CredibilityScore** : Score de vraisemblance du document
- **Recommendations** : Liste des recommandations
- **Summary** : Synthèse de l'analyse
#### ContexteView
- **ExternalData** : Données des APIs externes
- **StatusCards** : Cartes de statut pour chaque source
- **LastUpdated** : Horodatage des dernières mises à jour
#### ConseilView
- **LLMAnalysis** : Analyse générée par l'IA
- **RiskAssessment** : Évaluation des risques
- **NextSteps** : Prochaines étapes recommandées
## Gestion d'état Redux
### Structure du store
```typescript
interface RootState {
app: AppState // État global de l'application
document: DocumentState // État des documents et opérations
}
```
### AppState
- **status** : État global ('idle' | 'loading' | 'succeeded' | 'failed')
- **error** : Messages d'erreur globaux
### DocumentState
- **documents** : Liste des documents uploadés
- **currentDocument** : Document actuellement sélectionné
- **extractionResult** : Résultats d'extraction
- **analysisResult** : Résultats d'analyse
- **contextResult** : Données contextuelles
- **conseilResult** : Conseil IA
- **loading** : État de chargement
- **error** : Messages d'erreur
### Actions asynchrones
- **uploadDocument** : Upload d'un document
- **extractDocument** : Extraction des données
- **analyzeDocument** : Analyse du document
- **getContextData** : Récupération des données contextuelles
- **getConseil** : Génération du conseil IA
## Services API
### Configuration Axios
- **Base URL** : Configuration via variables d'environnement
- **Timeout** : 60 secondes pour les opérations longues
- **Intercepteurs** : Gestion d'erreur et fallback automatique
### Endpoints
- `POST /api/documents/upload` : Upload de document
- `GET /api/documents/{id}/extract` : Extraction de données
- `GET /api/documents/{id}/analyze` : Analyse du document
- `GET /api/documents/{id}/context` : Données contextuelles
- `GET /api/documents/{id}/conseil` : Conseil IA
### Gestion d'erreur
- **Intercepteurs** : Détection automatique des erreurs
- **Fallback** : Données de démonstration en cas d'erreur
- **Codes gérés** : 404, 405, ERR_NETWORK, ERR_CONNECTION_REFUSED
## Types et interfaces
### Document
```typescript
interface Document {
id: string
name: string
type: string
size: number
uploadDate: Date
status: 'uploading' | 'processing' | 'completed' | 'error'
}
```
### ExtractionResult
```typescript
interface ExtractionResult {
documentId: string
text: string
language: string
documentType: string
identities: Identity[]
addresses: Address[]
properties: Property[]
contracts: Contract[]
signatures: string[]
confidence: number
}
```
### AnalysisResult
```typescript
interface AnalysisResult {
documentId: string
documentType: string
isCNI: boolean
credibilityScore: number
summary: string
recommendations: string[]
}
```
## Mode démonstration
### Fonctionnement
- **Détection automatique** : Vérification de la disponibilité du backend
- **Fallback gracieux** : Basculement vers des données de démonstration
- **Données réalistes** : Exemples d'actes notariaux authentiques
- **Fonctionnalités complètes** : Toutes les vues opérationnelles
### Données de démonstration
- **Documents** : Exemples d'actes de vente, CNI, etc.
- **Identités** : Personnes avec informations complètes
- **Adresses** : Adresses françaises réalistes
- **Biens** : Propriétés avec références cadastrales
- **Contrats** : Clauses et montants réalistes
## Performance et optimisation
### Code splitting
- **Lazy loading** : Chargement à la demande des vues
- **Suspense** : Gestion des états de chargement
- **Bundle optimization** : Optimisation de la taille des bundles
### Caching
- **Redux state** : Mise en cache des données dans le store
- **API responses** : Cache des réponses API
- **Component memoization** : Optimisation des re-renders
### Build optimization
- **Vite** : Build rapide avec optimisations automatiques
- **Tree shaking** : Élimination du code mort
- **Minification** : Compression du code de production
## Sécurité
### Validation
- **TypeScript** : Validation de types à la compilation
- **Runtime validation** : Validation des données API
- **Input sanitization** : Nettoyage des entrées utilisateur
### CORS et CSP
- **CORS** : Configuration des en-têtes Cross-Origin
- **CSP** : Content Security Policy pour la sécurité
- **HTTPS** : Communication sécurisée en production
## Déploiement
### Environnements
- **Développement** : Serveur local avec HMR
- **Staging** : Environnement de test
- **Production** : Déploiement optimisé
### Variables d'environnement
- **VITE_API_URL** : URL de l'API backend
- **VITE_*_API_URL** : URLs des APIs externes
- **NODE_ENV** : Environnement d'exécution
### Build de production
- **Optimisation** : Minification et compression
- **Assets** : Optimisation des images et CSS
- **Bundle analysis** : Analyse de la taille des bundles

554
docs/DEPLOYMENT.md Normal file
View File

@ -0,0 +1,554 @@
# Guide de déploiement - 4NK IA Front Notarial
## Vue d'ensemble
Ce guide couvre le déploiement de l'application 4NK IA Front Notarial dans différents environnements.
## Notes de version 0.1.2
### Changements principaux
- Suppression du « mode démo » côté frontend et des fallbacks
- Routage des APIs externes exclusivement via le backend `4NK_IA_back`
- Exigence Node.js ≥ 20.19 pour la compilation
### Impact déploiement
- Variables `.env` front simplifiées (uniquement `VITE_API_URL`)
- Build requis avec Node.js ≥ 20.19
### Étapes recommandées
1. Mettre à jour lenvironnement: variables `.env.*` si nécessaire
2. Rebuilder lapplication
3. Synchroniser `dist/` vers le serveur web et recharger Nginx
Exemple de séquence côté serveur (Nginx):
```bash
cd /var/www/4nk-ia-front
git fetch --all --prune
git checkout dev
git pull --ff-only
npm ci
npm run build
sudo systemctl reload nginx
```
## Prérequis
### Environnement de développement
- **Node.js** : >= 20.19.0 (ou >= 22.12.0)
- **npm** : >= 10.0.0
- **nvm** : recommandé, le projet fournit `.nvmrc`
- **Git** : Pour la gestion des versions
### Environnement de production
- **Serveur web** : Nginx ou Apache
- **HTTPS** : Certificat SSL valide
- **CDN** : Optionnel pour les assets statiques
## Configuration des environnements
### Variables d'environnement
#### Développement (.env.development)
```env
NODE_ENV=development
VITE_API_URL=http://localhost:8000
```
#### Staging (.env.staging)
```env
NODE_ENV=staging
VITE_API_URL=https://api-staging.4nkweb.com
```
#### Production (.env.production)
```env
NODE_ENV=production
VITE_API_URL=https://api.4nkweb.com
```
## Build de production
### Script de build
```bash
# Build standard
npm run build
# Build avec analyse
npm run build -- --analyze
# Build pour un environnement spécifique
npm run build -- --mode production
```
### Optimisations automatiques
- **Minification** : Code JavaScript et CSS minifié
- **Tree shaking** : Élimination du code mort
- **Code splitting** : Division en chunks optimaux
- **Asset optimization** : Optimisation des images et fonts
### Structure du build
```text
dist/
├── index.html # Point d'entrée HTML
├── assets/
│ ├── index-[hash].js # Bundle principal
│ ├── index-[hash].css # Styles principaux
│ ├── [chunk]-[hash].js # Chunks de code splitting
│ └── [asset]-[hash].[ext] # Assets optimisés
└── favicon.ico # Icône du site
```
## Déploiement sur serveur
### Configuration Nginx
### Déploiement via Docker
#### Construction et push de l'image
```bash
# Construire l'image (tag par défaut: commit SHA court)
./scripts/docker-build.sh
# Construire avec tag explicite
IMAGE_TAG=0.1.3 ./scripts/docker-build.sh
# Pousser vers git.4nkweb.com (authentification requise)
IMAGE_TAG=0.1.3 ./scripts/docker-push.sh
```
#### Lancement de l'image
```bash
docker run -d --name 4nk-ia-front -p 8080:80 git.4nkweb.com/4nk/4nk-ia-front:0.1.3
```
#### Fichier de configuration
```nginx
server {
listen 80;
listen [::]:80;
server_name app.4nkweb.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name app.4nkweb.com;
# Certificats SSL
ssl_certificate /path/to/certificate.crt;
ssl_certificate_key /path/to/private.key;
# Configuration SSL
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
ssl_prefer_server_ciphers off;
# HSTS
add_header Strict-Transport-Security "max-age=63072000" always;
# Root directory
root /var/www/4nk-ia-front/dist;
index index.html;
# Gestion des routes SPA
location / {
try_files $uri $uri/ /index.html;
}
# Cache des assets statiques
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Sécurité
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
}
```
### Configuration Apache
#### Fichier .htaccess
```apache
RewriteEngine On
# Redirection HTTPS
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Gestion des routes SPA
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
# Cache des assets
<FilesMatch "\.(css|js|png|jpg|jpeg|gif|ico|svg)$">
ExpiresActive On
ExpiresDefault "access plus 1 year"
Header set Cache-Control "public, immutable"
</FilesMatch>
# Compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
</IfModule>
# Sécurité
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
Header always set Strict-Transport-Security "max-age=63072000"
```
## Déploiement avec Docker
### Dockerfile
```dockerfile
# Build stage
FROM node:22.12-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Production stage
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
### docker-compose.yml
```yaml
version: '3.8'
services:
frontend:
build: .
ports:
- "80:80"
- "443:443"
volumes:
- ./ssl:/etc/nginx/ssl:ro
environment:
- NODE_ENV=production
restart: unless-stopped
```
### Script de déploiement
```bash
#!/bin/bash
# deploy.sh
set -e
echo "Building application..."
docker build -t 4nk-ia-front .
echo "Stopping existing container..."
docker stop 4nk-ia-front || true
docker rm 4nk-ia-front || true
echo "Starting new container..."
docker run -d \
--name 4nk-ia-front \
-p 80:80 \
-p 443:443 \
-v /path/to/ssl:/etc/nginx/ssl:ro \
4nk-ia-front
echo "Deployment completed!"
```
## Déploiement avec CI/CD
### GitHub Actions
```yaml
name: Deploy
on:
push:
branches: [release]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '22.12'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm run test
- name: Build application
run: npm run build
env:
NODE_ENV: production
VITE_API_URL: ${{ secrets.API_URL }}
- name: Deploy to server
uses: appleboy/ssh-action@v0.1.5
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
cd /var/www/4nk-ia-front
git pull origin release
npm ci
npm run build
sudo systemctl reload nginx
```
### GitLab CI
```yaml
stages:
- test
- build
- deploy
test:
stage: test
image: node:22.12-alpine
script:
- npm ci
- npm run test
only:
- merge_requests
- release
build:
stage: build
image: node:22.12-alpine
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 hour
only:
- release
deploy:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache openssh-client
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
script:
- scp -r dist/* $DEPLOY_USER@$DEPLOY_HOST:/var/www/4nk-ia-front/
- ssh $DEPLOY_USER@$DEPLOY_HOST "sudo systemctl reload nginx"
only:
- release
```
## Monitoring et surveillance
### Métriques de performance
- **Temps de chargement** : Mesure du First Contentful Paint
- **Taille des bundles** : Surveillance de la taille des assets
- **Erreurs JavaScript** : Tracking des erreurs côté client
### Outils de monitoring
- **Google Analytics** : Analytics et performance
- **Sentry** : Monitoring des erreurs
- **Lighthouse** : Audit de performance
### Configuration Sentry
```typescript
import * as Sentry from "@sentry/react";
Sentry.init({
dsn: "YOUR_SENTRY_DSN",
environment: process.env.NODE_ENV,
tracesSampleRate: 1.0,
});
```
## Rollback et récupération
### Stratégie de rollback
```bash
#!/bin/bash
# rollback.sh
PREVIOUS_VERSION=$1
if [ -z "$PREVIOUS_VERSION" ]; then
echo "Usage: ./rollback.sh <version>"
exit 1
fi
echo "Rolling back to version $PREVIOUS_VERSION..."
# Restaurer la version précédente
git checkout $PREVIOUS_VERSION
npm ci
npm run build
# Redéployer
sudo systemctl reload nginx
echo "Rollback completed!"
```
### Sauvegarde
```bash
#!/bin/bash
# backup.sh
BACKUP_DIR="/backups/4nk-ia-front"
DATE=$(date +%Y%m%d_%H%M%S)
# Créer le répertoire de sauvegarde
mkdir -p $BACKUP_DIR
# Sauvegarder les fichiers
tar -czf $BACKUP_DIR/backup_$DATE.tar.gz /var/www/4nk-ia-front
# Nettoyer les anciennes sauvegardes (garder 7 jours)
find $BACKUP_DIR -name "backup_*.tar.gz" -mtime +7 -delete
echo "Backup completed: backup_$DATE.tar.gz"
```
## Sécurité
### Headers de sécurité
- **CSP** : Content Security Policy
- **HSTS** : HTTP Strict Transport Security
- **X-Frame-Options** : Protection contre le clickjacking
- **X-Content-Type-Options** : Protection contre MIME sniffing
### Configuration CSP
```html
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://api.4nkweb.com;
font-src 'self' data:;
">
```
### Audit de sécurité
```bash
# Audit des dépendances
npm audit
# Audit de sécurité avec Snyk
npx snyk test
# Scan de vulnérabilités
npx audit-ci --config audit-ci.json
```
## Maintenance
### Mise à jour des dépendances
```bash
# Vérifier les mises à jour
npm outdated
# Mettre à jour les dépendances
npm update
# Mise à jour majeure
npx npm-check-updates -u
npm install
```
### Nettoyage
```bash
# Nettoyer le cache npm
npm cache clean --force
# Nettoyer les node_modules
rm -rf node_modules package-lock.json
npm install
# Nettoyer le build
rm -rf dist
npm run build
```
### Logs et debugging
```bash
# Logs Nginx
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log
# Logs de l'application
sudo journalctl -u nginx -f
# Debug des erreurs
sudo nginx -t # Test de configuration
```

627
docs/TESTING.md Normal file
View File

@ -0,0 +1,627 @@
# Guide de tests - 4NK IA Front Notarial
## Vue d'ensemble
Ce guide couvre la stratégie de tests pour l'application 4NK IA Front Notarial, incluant les tests unitaires,
d'intégration et end-to-end.
## Stack de test
### Outils principaux
- **Vitest** : Framework de test rapide et moderne
- **Testing Library** : Tests d'intégration React
- **JSDOM** : Environnement DOM simulé
- **MSW** : Mock Service Worker pour les APIs
- **Coverage V8** : Rapport de couverture de code
### Configuration
#### vitest.config.ts
```typescript
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
globals: true,
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: [
'node_modules/',
'dist/',
'**/*.d.ts',
'**/*.config.*',
'**/setup.*'
]
}
}
})
```
#### setup.test.ts
```typescript
import { expect, afterEach } from 'vitest'
import { cleanup } from '@testing-library/react'
import * as matchers from '@testing-library/jest-dom/matchers'
// Étendre les matchers de testing-library
expect.extend(matchers)
// Nettoyer après chaque test
afterEach(() => {
cleanup()
})
```
## Types de tests
### Tests unitaires
#### Composants React
```typescript
// tests/components/Layout.test.tsx
import { render, screen } from '@testing-library/react'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import { store } from '../../src/store'
import { Layout } from '../../src/components/Layout'
const renderWithProviders = (ui: React.ReactElement) => {
return render(
<Provider store={store}>
<BrowserRouter>
{ui}
</BrowserRouter>
</Provider>
)
}
describe('Layout', () => {
it('should render the application title', () => {
renderWithProviders(<Layout><div>Test content</div></Layout>)
expect(screen.getByText('4NK IA - Front Notarial')).toBeInTheDocument()
})
it('should render navigation tabs', () => {
renderWithProviders(<Layout><div>Test content</div></Layout>)
expect(screen.getByRole('tablist')).toBeInTheDocument()
})
})
```
#### Services API
```typescript
// tests/services/api.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { documentApi } from '../../src/services/api'
import axios from 'axios'
// Mock axios
vi.mock('axios')
const mockedAxios = vi.mocked(axios)
describe('documentApi', () => {
beforeEach(() => {
vi.clearAllMocks()
})
describe('upload', () => {
it('should upload a document successfully', async () => {
const mockResponse = {
data: {
id: 'doc_123',
name: 'test.pdf',
type: 'application/pdf',
size: 1024,
uploadDate: new Date(),
status: 'completed'
}
}
mockedAxios.create.mockReturnValue({
post: vi.fn().mockResolvedValue(mockResponse)
} as any)
const file = new File(['content'], 'test.pdf', { type: 'application/pdf' })
const result = await documentApi.upload(file)
expect(result.id).toBe('doc_123')
expect(result.name).toBe('test.pdf')
expect(result.status).toBe('completed')
})
it('should return demo data on error', async () => {
mockedAxios.create.mockReturnValue({
post: vi.fn().mockRejectedValue(new Error('Network error'))
} as any)
const file = new File(['content'], 'test.pdf', { type: 'application/pdf' })
const result = await documentApi.upload(file)
expect(result.id).toMatch(/^demo-/)
expect(result.name).toBe('test.pdf')
})
})
})
```
#### Redux Slices
```typescript
// tests/store/documentSlice.test.ts
import { describe, it, expect } from 'vitest'
import documentReducer, { setCurrentDocument } from '../../src/store/documentSlice'
import { uploadDocument } from '../../src/store/documentSlice'
describe('documentSlice', () => {
const initialState = {
documents: [],
currentDocument: null,
extractionResult: null,
analysisResult: null,
contextResult: null,
conseilResult: null,
loading: false,
error: null
}
it('should handle setCurrentDocument', () => {
const document = {
id: 'doc_123',
name: 'test.pdf',
type: 'application/pdf',
size: 1024,
uploadDate: new Date(),
status: 'completed' as const
}
const action = setCurrentDocument(document)
const newState = documentReducer(initialState, action)
expect(newState.currentDocument).toEqual(document)
expect(newState.extractionResult).toBeNull()
})
it('should handle uploadDocument.pending', () => {
const action = { type: uploadDocument.pending.type }
const newState = documentReducer(initialState, action)
expect(newState.loading).toBe(true)
expect(newState.error).toBeNull()
})
})
```
### Tests d'intégration
#### Vues complètes
```typescript
// tests/views/UploadView.test.tsx
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { Provider } from 'react-redux'
import { BrowserRouter } from 'react-router-dom'
import { store } from '../../src/store'
import { UploadView } from '../../src/views/UploadView'
const renderWithProviders = (ui: React.ReactElement) => {
return render(
<Provider store={store}>
<BrowserRouter>
{ui}
</BrowserRouter>
</Provider>
)
}
describe('UploadView', () => {
it('should render upload area', () => {
renderWithProviders(<UploadView />)
expect(screen.getByText(/glisser-déposer/i)).toBeInTheDocument()
})
it('should handle file upload', async () => {
const user = userEvent.setup()
renderWithProviders(<UploadView />)
const file = new File(['content'], 'test.pdf', { type: 'application/pdf' })
const input = screen.getByLabelText(/sélectionner des fichiers/i)
await user.upload(input, file)
await waitFor(() => {
expect(screen.getByText('test.pdf')).toBeInTheDocument()
})
})
it('should display document list after upload', async () => {
const user = userEvent.setup()
renderWithProviders(<UploadView />)
const file = new File(['content'], 'test.pdf', { type: 'application/pdf' })
const input = screen.getByLabelText(/sélectionner des fichiers/i)
await user.upload(input, file)
await waitFor(() => {
expect(screen.getByRole('list')).toBeInTheDocument()
expect(screen.getByText('test.pdf')).toBeInTheDocument()
})
})
})
```
#### Navigation
```typescript
// tests/navigation.test.tsx
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from 'react-redux'
import { store } from '../src/store'
import App from '../src/App'
const renderWithProviders = (ui: React.ReactElement) => {
return render(
<Provider store={store}>
<BrowserRouter>
{ui}
</BrowserRouter>
</Provider>
)
}
describe('Navigation', () => {
it('should navigate between tabs', async () => {
const user = userEvent.setup()
renderWithProviders(<App />)
// Vérifier que l'onglet Upload est actif par défaut
expect(screen.getByText('Upload')).toHaveAttribute('aria-selected', 'true')
// Cliquer sur l'onglet Extraction
await user.click(screen.getByText('Extraction'))
expect(screen.getByText('Extraction')).toHaveAttribute('aria-selected', 'true')
expect(screen.getByText('Upload')).toHaveAttribute('aria-selected', 'false')
})
})
```
### Tests d'API avec MSW
#### Configuration MSW
```typescript
// tests/mocks/handlers.ts
import { http, HttpResponse } from 'msw'
export const handlers = [
// Upload de document
http.post('/api/documents/upload', () => {
return HttpResponse.json({
id: 'doc_123',
name: 'test.pdf',
type: 'application/pdf',
size: 1024,
uploadDate: new Date().toISOString(),
status: 'completed'
})
}),
// Extraction de données
http.get('/api/documents/:id/extract', () => {
return HttpResponse.json({
documentId: 'doc_123',
text: 'Texte extrait du document...',
language: 'fr',
documentType: 'Acte de vente',
identities: [
{
id: '1',
type: 'person',
firstName: 'Jean',
lastName: 'Dupont',
birthDate: '1980-05-15',
nationality: 'Française',
confidence: 0.95
}
],
addresses: [],
properties: [],
contracts: [],
signatures: [],
confidence: 0.92
})
}),
// Erreur de connexion
http.get('/api/documents/:id/analyze', () => {
return HttpResponse.error()
})
]
```
#### Tests avec MSW
```typescript
// tests/integration/api.test.tsx
import { setupServer } from 'msw/node'
import { handlers } from '../mocks/handlers'
import { documentApi } from '../../src/services/api'
const server = setupServer(...handlers)
beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())
describe('API Integration', () => {
it('should extract document data', async () => {
const result = await documentApi.extract('doc_123')
expect(result.documentId).toBe('doc_123')
expect(result.identities).toHaveLength(1)
expect(result.identities[0].firstName).toBe('Jean')
})
it('should handle API errors gracefully', async () => {
const result = await documentApi.analyze('doc_123')
// Devrait retourner des données de démonstration
expect(result.documentId).toBe('doc_123')
expect(result.credibilityScore).toBeDefined()
})
})
```
## Tests de performance
### Tests de rendu
```typescript
// tests/performance/render.test.tsx
import { render } from '@testing-library/react'
import { Provider } from 'react-redux'
import { BrowserRouter } from 'react-router-dom'
import { store } from '../../src/store'
import App from '../../src/App'
describe('Performance', () => {
it('should render app within acceptable time', () => {
const start = performance.now()
render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
)
const end = performance.now()
const renderTime = end - start
// Le rendu initial devrait prendre moins de 100ms
expect(renderTime).toBeLessThan(100)
})
})
```
### Tests de mémoire
```typescript
// tests/performance/memory.test.tsx
import { render, cleanup } from '@testing-library/react'
import { Provider } from 'react-redux'
import { BrowserRouter } from 'react-router-dom'
import { store } from '../../src/store'
import { UploadView } from '../../src/views/UploadView'
describe('Memory Management', () => {
it('should not leak memory on multiple renders', () => {
const initialMemory = (performance as any).memory?.usedJSHeapSize || 0
// Rendre et nettoyer plusieurs fois
for (let i = 0; i < 10; i++) {
render(
<Provider store={store}>
<BrowserRouter>
<UploadView />
</BrowserRouter>
</Provider>
)
cleanup()
}
const finalMemory = (performance as any).memory?.usedJSHeapSize || 0
const memoryIncrease = finalMemory - initialMemory
// L'augmentation de mémoire devrait être raisonnable
expect(memoryIncrease).toBeLessThan(10 * 1024 * 1024) // 10MB
})
})
```
## Tests d'accessibilité
### Tests avec jest-axe
```typescript
// tests/accessibility/a11y.test.tsx
import { render } from '@testing-library/react'
import { axe, toHaveNoViolations } from 'jest-axe'
import { Provider } from 'react-redux'
import { BrowserRouter } from 'react-router-dom'
import { store } from '../../src/store'
import { Layout } from '../../src/components/Layout'
expect.extend(toHaveNoViolations)
describe('Accessibility', () => {
it('should not have accessibility violations', async () => {
const { container } = render(
<Provider store={store}>
<BrowserRouter>
<Layout>
<div>Test content</div>
</Layout>
</BrowserRouter>
</Provider>
)
const results = await axe(container)
expect(results).toHaveNoViolations()
})
it('should have proper ARIA labels', () => {
render(
<Provider store={store}>
<BrowserRouter>
<Layout>
<div>Test content</div>
</Layout>
</BrowserRouter>
</Provider>
)
expect(screen.getByRole('banner')).toBeInTheDocument()
expect(screen.getByRole('tablist')).toBeInTheDocument()
})
})
```
## Scripts de test
### package.json
```json
{
"scripts": {
"test": "vitest",
"test:ui": "vitest --ui",
"test:run": "vitest run",
"test:coverage": "vitest run --coverage",
"test:watch": "vitest --watch",
"test:debug": "vitest --inspect-brk"
}
}
```
### Exécution des tests
```bash
# Tests en mode watch
npm run test
# Tests avec interface graphique
npm run test:ui
# Tests une seule fois
npm run test:run
# Tests avec couverture
npm run test:coverage
# Tests en mode debug
npm run test:debug
```
## Configuration CI/CD
### GitHub Actions
```yaml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '22.12'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm run test:coverage
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
file: ./coverage/lcov.info
```
## Bonnes pratiques
### Organisation des tests
- **Un fichier de test par composant** : `Component.test.tsx`
- **Tests groupés par fonctionnalité** : `describe` blocks
- **Tests isolés** : Chaque test doit être indépendant
- **Noms descriptifs** : `it('should do something specific')`
### Mocking
- **Mock des dépendances externes** : APIs, services
- **Mock des hooks** : React hooks personnalisés
- **Mock des modules** : Modules Node.js
### Assertions
- **Assertions spécifiques** : Éviter les assertions génériques
- **Tests de régression** : Vérifier les bugs corrigés
- **Tests de cas limites** : Valeurs nulles, erreurs
### Performance
- **Tests rapides** : Éviter les tests lents
- **Parallélisation** : Utiliser `--threads` pour Vitest
- **Cache** : Utiliser le cache des dépendances
## Métriques de qualité
### Couverture de code
- **Minimum 80%** : Couverture globale
- **Minimum 90%** : Composants critiques
- **100%** : Fonctions utilitaires
### Types de couverture
- **Statements** : Instructions exécutées
- **Branches** : Branches conditionnelles
- **Functions** : Fonctions appelées
- **Lines** : Lignes de code exécutées
### Rapport de couverture
```bash
# Générer le rapport
npm run test:coverage
# Ouvrir le rapport HTML
open coverage/index.html
```

1
document_analysis.json Normal file
View File

@ -0,0 +1 @@
{"id":"doc_20250910_232208_10","filename":"facture_4NK_08-2025_04.pdf","size":85819,"upload_time":"2025-09-10T23:22:08.239575","status":"completed","progress":100,"current_step":"Terminé","results":{"ocr_text":"Texte extrait simulé du document...","document_type":"Acte de vente","entities":{"persons":["Jean Dupont","Marie Martin"],"addresses":["123 Rue de la Paix, 75001 Paris"],"properties":["Appartement T3, 75m²"]},"verification_score":0.85,"external_checks":{"cadastre":"OK","georisques":"OK","bodacc":"OK"}},"completion_time":"2025-09-10T23:22:18.243146"}

View File

@ -6,7 +6,7 @@ import tseslint from 'typescript-eslint'
import { globalIgnores } from 'eslint/config'
export default tseslint.config([
globalIgnores(['dist']),
globalIgnores(['dist', 'coverage']),
{
files: ['**/*.{ts,tsx}'],
extends: [

18
nginx.conf Normal file
View File

@ -0,0 +1,18 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# Cache des assets
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# SPA fallback
location / {
try_files $uri /index.html;
}
}

1506
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,12 @@
{
"name": "4nk-ia-front4nk",
"private": true,
"version": "0.1.0",
"version": "0.1.3",
"type": "module",
"scripts": {
"predev": "node scripts/check-node.mjs",
"dev": "vite",
"prebuild": "node scripts/check-node.mjs",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview",
@ -14,9 +16,22 @@
"test": "vitest run --coverage",
"test:ui": "vitest"
},
"engines": {
"node": ">=20.19.0 <23",
"npm": ">=10"
},
"dependencies": {
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@mui/icons-material": "^7.3.2",
"@mui/material": "^7.3.2",
"@reduxjs/toolkit": "^2.9.0",
"axios": "^1.11.0",
"react": "^19.1.1",
"react-dom": "^19.1.1"
"react-dom": "^19.1.1",
"react-dropzone": "^14.3.8",
"react-redux": "^9.2.0",
"react-router-dom": "^7.8.2"
},
"devDependencies": {
"@eslint/js": "^9.33.0",

View File

@ -0,0 +1,89 @@
#!/usr/bin/env python3
import requests
import json
import sys
def analyze_document(document_id):
"""Analyser un document via l'API backend"""
base_url = "http://localhost:18000"
print(f"🔍 Analyse du document: {document_id}")
print("=" * 50)
try:
# Récupérer les détails du document
response = requests.get(f"{base_url}/api/notary/documents/{document_id}")
if response.status_code == 200:
data = response.json()
print(f"📄 Document: {data.get('filename', 'N/A')}")
print(f"📊 Taille: {data.get('size', 0)} bytes")
print(f"📅 Upload: {data.get('upload_time', 'N/A')}")
print(f"✅ Statut: {data.get('status', 'N/A')}")
print(f"📈 Progression: {data.get('progress', 0)}%")
print(f"🔄 Étape: {data.get('current_step', 'N/A')}")
results = data.get('results', {})
if results:
print("\n📋 Résultats de l'analyse:")
print("-" * 30)
# Type de document
doc_type = results.get('document_type', 'N/A')
print(f"📑 Type: {doc_type}")
# Texte OCR
ocr_text = results.get('ocr_text', 'N/A')
print(f"📝 Texte extrait: {ocr_text[:100]}...")
# Entités
entities = results.get('entities', {})
if entities:
print(f"👥 Personnes: {entities.get('persons', [])}")
print(f"🏠 Adresses: {entities.get('addresses', [])}")
print(f"🏢 Propriétés: {entities.get('properties', [])}")
# Score de vérification
score = results.get('verification_score', 0)
print(f"⭐ Score de vérification: {score}")
# Données externes
external_data = results.get('external_data', {})
if external_data:
print(f"🌐 Données externes: {external_data}")
# Recommandations
recommendations = results.get('recommendations', [])
if recommendations:
print(f"💡 Recommandations:")
for i, rec in enumerate(recommendations, 1):
print(f" {i}. {rec}")
# Risques
risks = results.get('risks', [])
if risks:
print(f"⚠️ Risques identifiés:")
for i, risk in enumerate(risks, 1):
print(f" {i}. {risk}")
print("\n✅ Analyse terminée avec succès!")
else:
print(f"❌ Erreur: {response.status_code}")
print(f"Message: {response.text}")
except requests.exceptions.ConnectionError:
print("❌ Erreur: Impossible de se connecter à l'API backend")
print("Vérifiez que le backend est démarré sur http://localhost:18000")
except Exception as e:
print(f"❌ Erreur: {e}")
if __name__ == "__main__":
if len(sys.argv) > 1:
document_id = sys.argv[1]
else:
document_id = "doc_20250910_232208_10" # ID par défaut
analyze_document(document_id)

24
scripts/check-node.mjs Normal file
View File

@ -0,0 +1,24 @@
#!/usr/bin/env node
const semver = (v) => v.split('.').map((n) => parseInt(n, 10));
const compare = (a, b) => {
for (let i = 0; i < Math.max(a.length, b.length); i += 1) {
const ai = a[i] || 0;
const bi = b[i] || 0;
if (ai > bi) return 1;
if (ai < bi) return -1;
}
return 0;
};
const current = semver(process.versions.node);
const min = semver('20.19.0');
if (compare(current, min) < 0) {
console.error(`❌ Version de Node trop ancienne: ${process.versions.node}. Requise: >= 20.19.0`);
console.error('➡️ Utilisez nvm: nvm use 20 (ou installez: nvm install 20)');
process.exit(1);
}
console.log(`✅ Node ${process.versions.node} OK (>= 20.19.0)`);

13
scripts/docker-build.sh Executable file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -euo pipefail
IMAGE_REGISTRY=${IMAGE_REGISTRY:-git.4nkweb.com}
IMAGE_NAMESPACE=${IMAGE_NAMESPACE:-4nk}
IMAGE_NAME=${IMAGE_NAME:-4nk-ia-front}
IMAGE_TAG=${IMAGE_TAG:-$(git rev-parse --short HEAD)}
FULL_IMAGE_REF="$IMAGE_REGISTRY/$IMAGE_NAMESPACE/$IMAGE_NAME:$IMAGE_TAG"
echo "Building image: $FULL_IMAGE_REF"
docker build -t "$FULL_IMAGE_REF" .
echo "Built $FULL_IMAGE_REF"

21
scripts/docker-push.sh Executable file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -euo pipefail
IMAGE_REGISTRY=${IMAGE_REGISTRY:-git.4nkweb.com}
IMAGE_NAMESPACE=${IMAGE_NAMESPACE:-4nk}
IMAGE_NAME=${IMAGE_NAME:-4nk-ia-front}
# Default tag from branch name if available, else short SHA
if [ -z "${IMAGE_TAG:-}" ]; then
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9._-]+/-/g')
if [ "$BRANCH_NAME" = "HEAD" ]; then
IMAGE_TAG=$(git rev-parse --short HEAD)
else
IMAGE_TAG=$BRANCH_NAME
fi
fi
FULL_IMAGE_REF="$IMAGE_REGISTRY/$IMAGE_NAMESPACE/$IMAGE_NAME:$IMAGE_TAG"
echo "Pushing image: $FULL_IMAGE_REF"
docker push "$FULL_IMAGE_REF"
echo "Pushed $FULL_IMAGE_REF"

66
scripts/simple-server.js Executable file
View File

@ -0,0 +1,66 @@
#!/usr/bin/env node
import http from 'http';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const PORT = 5173;
const HOST = '0.0.0.0';
// Types MIME
const mimeTypes = {
'.html': 'text/html',
'.js': 'text/javascript',
'.css': 'text/css',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpg',
'.gif': 'image/gif',
'.svg': 'image/svg+xml',
'.ico': 'image/x-icon'
};
const server = http.createServer((req, res) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
let filePath = '.' + req.url;
if (filePath === './') {
filePath = './index.html';
}
const extname = String(path.extname(filePath)).toLowerCase();
const mimeType = mimeTypes[extname] || 'application/octet-stream';
fs.readFile(filePath, (error, content) => {
if (error) {
if (error.code === 'ENOENT') {
// Fichier non trouvé, servir index.html pour SPA
fs.readFile('./index.html', (error, content) => {
if (error) {
res.writeHead(404);
res.end('File not found');
} else {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(content, 'utf-8');
}
});
} else {
res.writeHead(500);
res.end('Server error: ' + error.code);
}
} else {
res.writeHead(200, { 'Content-Type': mimeType });
res.end(content, 'utf-8');
}
});
});
server.listen(PORT, HOST, () => {
console.log(`🚀 Serveur 4NK_IA_front démarré sur http://${HOST}:${PORT}`);
console.log(`📁 Servant les fichiers depuis: ${process.cwd()}`);
console.log(`💡 Appuyez sur Ctrl+C pour arrêter`);
});

64
scripts/start-frontend.sh Executable file
View File

@ -0,0 +1,64 @@
#!/bin/bash
# Script de démarrage simple pour 4NK_IA_front
# Usage: ./start-frontend.sh
echo "🚀 Démarrage de 4NK_IA_front..."
# Vérifier que nous sommes dans le bon répertoire
if [ ! -f "package.json" ]; then
echo "❌ Erreur: Ce script doit être exécuté depuis le répertoire racine du projet 4NK_IA_front"
exit 1
fi
# Vérifier Node.js
if ! command -v node >/dev/null 2>&1; then
echo "❌ Node.js n'est pas installé"
exit 1
fi
# Vérifier npm
if ! command -v npm >/dev/null 2>&1; then
echo "❌ npm n'est pas installé"
exit 1
fi
echo "✅ Node.js $(node --version) et npm $(npm --version) détectés"
# Installer les dépendances si nécessaire
if [ ! -d "node_modules" ]; then
echo "📦 Installation des dépendances..."
npm install
fi
# Vérifier la configuration
if [ ! -f ".env" ]; then
echo "📝 Création du fichier .env..."
cat > .env << 'EOF'
# Configuration API Backend
VITE_API_URL=http://localhost:18000
# Configuration pour le développement
VITE_APP_NAME=4NK IA Front Notarial
VITE_APP_VERSION=0.1.0
# Configuration des services externes (optionnel)
VITE_CADASTRE_API_URL=https://apicarto.ign.fr/api/cadastre
VITE_GEORISQUES_API_URL=https://www.georisques.gouv.fr/api
VITE_GEOFONCIER_API_URL=https://api2.geofoncier.fr
VITE_BODACC_API_URL=https://bodacc-datadila.opendatasoft.com/api
VITE_INFOGREFFE_API_URL=https://entreprise.api.gouv.fr
EOF
echo "✅ Fichier .env créé"
else
echo "✅ Fichier .env existe déjà"
fi
# Essayer de lancer avec npx vite
echo "🌐 Lancement du serveur de développement..."
echo "💡 L'application sera accessible sur http://localhost:5173"
echo "💡 Appuyez sur Ctrl+C pour arrêter le serveur"
echo ""
# Lancer Vite
npx vite --host 0.0.0.0 --port 5173

View File

@ -1,31 +1,6 @@
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import { AppRouter } from './router'
function App() {
const [count, setCount] = useState(0)
return (
<>
<div>
<a href="https://vite.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>count is {count}</button>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">Click on the Vite and React logos to learn more</p>
</>
)
export default function App() {
return <AppRouter />
}
export default App

View File

@ -0,0 +1,230 @@
import React, { useState, useEffect } from 'react'
import {
Box,
Typography,
Paper,
IconButton,
Button,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
CircularProgress,
Alert,
} from '@mui/material'
import {
PictureAsPdf,
Download,
Close,
ZoomIn,
ZoomOut,
NavigateBefore,
NavigateNext,
} from '@mui/icons-material'
import type { Document } from '../types'
interface FilePreviewProps {
document: Document
onClose: () => void
}
export const FilePreview: React.FC<FilePreviewProps> = ({ document, onClose }) => {
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [page, setPage] = useState(1)
const [scale, setScale] = useState(1.0)
const [numPages, setNumPages] = useState(0)
useEffect(() => {
setLoading(true)
setError(null)
setPage(1)
setScale(1.0)
// Simuler le chargement du PDF
const timer = setTimeout(() => {
setNumPages(3) // Simuler 3 pages
setLoading(false)
}, 1000)
return () => clearTimeout(timer)
}, [document])
const handleDownload = () => {
if (document.previewUrl) {
const link = window.document.createElement('a')
link.href = document.previewUrl
link.download = document.name
link.click()
}
}
const isPDF = document.mimeType.includes('pdf') || document.name.toLowerCase().endsWith('.pdf')
if (!isPDF) {
return (
<Paper sx={{ p: 3, mt: 2 }}>
<Box display="flex" justifyContent="space-between" alignItems="center" mb={2}>
<Typography variant="h6">{document.name}</Typography>
<IconButton onClick={onClose} title="Fermer">
<Close />
</IconButton>
</Box>
<Alert severity="info">
Aperçu non disponible pour ce type de fichier ({document.functionalType || document.mimeType})
</Alert>
</Paper>
)
}
return (
<Dialog open onClose={onClose} maxWidth="lg" fullWidth>
<DialogTitle>
<Box display="flex" justifyContent="space-between" alignItems="center">
<Box display="flex" alignItems="center" gap={1}>
<PictureAsPdf color="error" />
<Typography variant="h6">{document.name}</Typography>
</Box>
<IconButton onClick={onClose} title="Fermer">
<Close />
</IconButton>
</Box>
</DialogTitle>
<DialogContent dividers>
{loading && (
<Box display="flex" justifyContent="center" alignItems="center" minHeight="400px">
<CircularProgress />
<Typography variant="body2" sx={{ ml: 2 }}>
Chargement du PDF...
</Typography>
</Box>
)}
{error && (
<Alert severity="error" sx={{ mb: 2 }}>
{error}
</Alert>
)}
{!loading && !error && (
<Box>
{/* Contrôles de navigation */}
<Box display="flex" justifyContent="space-between" alignItems="center" mb={2}>
<Box display="flex" alignItems="center" gap={1}>
<Button
variant="outlined"
size="small"
startIcon={<NavigateBefore />}
onClick={() => setPage(prev => Math.max(prev - 1, 1))}
disabled={page <= 1}
>
Précédent
</Button>
<Typography variant="body2">
Page {page} sur {numPages}
</Typography>
<Button
variant="outlined"
size="small"
endIcon={<NavigateNext />}
onClick={() => setPage(prev => Math.min(prev + 1, numPages))}
disabled={page >= numPages}
>
Suivant
</Button>
</Box>
<Box display="flex" alignItems="center" gap={1}>
<Button
variant="outlined"
size="small"
startIcon={<ZoomOut />}
onClick={() => setScale(prev => Math.max(prev - 0.2, 0.5))}
>
Zoom -
</Button>
<Typography variant="body2">
{Math.round(scale * 100)}%
</Typography>
<Button
variant="outlined"
size="small"
startIcon={<ZoomIn />}
onClick={() => setScale(prev => Math.min(prev + 0.2, 2.0))}
>
Zoom +
</Button>
</Box>
</Box>
{/* Aperçu PDF avec viewer intégré */}
<Box sx={{
border: '1px solid',
borderColor: 'grey.300',
borderRadius: 1,
overflow: 'hidden',
maxHeight: '70vh',
display: 'flex',
justifyContent: 'center',
backgroundColor: 'grey.50'
}}>
{document.previewUrl ? (
<Box sx={{ width: '100%', height: '600px' }}>
{/* Utiliser un viewer PDF intégré */}
<iframe
src={`${document.previewUrl}#toolbar=1&navpanes=1&scrollbar=1&page=1&view=FitH`}
width="100%"
height="100%"
style={{
border: 'none',
transform: `scale(${scale})`,
transformOrigin: 'top left',
width: `${100 / scale}%`,
height: `${600 / scale}px`
}}
title={`Aperçu de ${document.name}`}
onLoad={() => setLoading(false)}
onError={() => {
setError('Erreur de chargement du PDF')
setLoading(false)
}}
/>
</Box>
) : (
<Box display="flex" justifyContent="center" alignItems="center" minHeight="400px">
<Box textAlign="center">
<PictureAsPdf sx={{ fontSize: 64, color: 'error.main', mb: 2 }} />
<Typography variant="h6" gutterBottom>
Aperçu PDF
</Typography>
<Typography variant="body2" color="text.secondary">
Le fichier PDF "{document.name}" a é uploadé avec succès.
</Typography>
<Typography variant="body2" color="text.secondary">
Taille: {(document.size / 1024 / 1024).toFixed(2)} MB
</Typography>
</Box>
</Box>
)}
</Box>
</Box>
)}
</DialogContent>
<DialogActions>
<Button onClick={onClose}>
Fermer
</Button>
<Button
variant="contained"
startIcon={<Download />}
onClick={handleDownload}
disabled={!document.previewUrl}
>
Télécharger
</Button>
</DialogActions>
</Dialog>
)
}

36
src/components/Layout.tsx Normal file
View File

@ -0,0 +1,36 @@
import React from 'react'
import { AppBar, Toolbar, Typography, Container, Box } from '@mui/material'
import { useNavigate, useLocation } from 'react-router-dom'
import { NavigationTabs } from './NavigationTabs'
interface LayoutProps {
children: React.ReactNode
}
export const Layout: React.FC<LayoutProps> = ({ children }) => {
const navigate = useNavigate()
const location = useLocation()
return (
<Box sx={{ flexGrow: 1 }}>
<AppBar position="static">
<Toolbar>
<Typography
variant="h6"
component="div"
sx={{ flexGrow: 1, cursor: 'pointer' }}
onClick={() => navigate('/')}
>
4NK IA - Front Notarial
</Typography>
</Toolbar>
</AppBar>
<NavigationTabs currentPath={location.pathname} />
<Container maxWidth="xl" sx={{ mt: 3, mb: 3 }}>
{children}
</Container>
</Box>
)
}

View File

@ -0,0 +1,41 @@
import React from 'react'
import { Tabs, Tab, Box } from '@mui/material'
import { useNavigate } from 'react-router-dom'
interface NavigationTabsProps {
currentPath: string
}
export const NavigationTabs: React.FC<NavigationTabsProps> = ({ currentPath }) => {
const navigate = useNavigate()
const tabs = [
{ label: 'Téléversement', path: '/' },
{ label: 'Extraction', path: '/extraction' },
{ label: 'Analyse', path: '/analyse' },
{ label: 'Contexte', path: '/contexte' },
{ label: 'Conseil', path: '/conseil' },
]
const currentTabIndex = tabs.findIndex(tab => tab.path === currentPath)
const handleTabChange = (_event: React.SyntheticEvent, newValue: number) => {
navigate(tabs[newValue].path)
}
return (
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs
value={currentTabIndex >= 0 ? currentTabIndex : 0}
onChange={handleTabChange}
aria-label="navigation tabs"
variant="scrollable"
scrollButtons="auto"
>
{tabs.map((tab, index) => (
<Tab key={index} label={tab.label} />
))}
</Tabs>
</Box>
)
}

View File

@ -1,11 +1,11 @@
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
color-scheme: light;
color: #213547;
background-color: #ffffff;
font-synthesis: none;
text-rendering: optimizeLegibility;
@ -13,21 +13,29 @@
-moz-osx-font-smoothing: grayscale;
}
* {
box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
min-height: 100vh;
background-color: #ffffff;
}
#root {
min-height: 100vh;
background-color: #ffffff;
}
a {
font-weight: 500;
color: #646cff;
color: #1976d2;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
color: #1565c0;
}
h1 {
@ -42,27 +50,15 @@ button {
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
background-color: #f9f9f9;
color: #213547;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
border-color: #1976d2;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

View File

@ -1,10 +1,20 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { Provider } from 'react-redux'
import { ThemeProvider } from '@mui/material/styles'
import { CssBaseline } from '@mui/material'
import './index.css'
import App from './App.tsx'
import { store } from './store'
import { theme } from './theme'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<Provider store={store}>
<ThemeProvider theme={theme}>
<CssBaseline />
<App />
</ThemeProvider>
</Provider>
</StrictMode>,
)

28
src/router/index.tsx Normal file
View File

@ -0,0 +1,28 @@
import { lazy, Suspense } from 'react'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import { Box, CircularProgress, Typography } from '@mui/material'
const UploadView = lazy(() => import('../views/UploadView'))
const ExtractionView = lazy(() => import('../views/ExtractionView'))
const AnalyseView = lazy(() => import('../views/AnalyseView'))
const ContexteView = lazy(() => import('../views/ContexteView'))
const ConseilView = lazy(() => import('../views/ConseilView'))
const LoadingFallback = () => (
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '50vh' }}>
<CircularProgress />
<Typography sx={{ ml: 2 }}>Chargement...</Typography>
</Box>
)
const router = createBrowserRouter([
{ path: '/', element: <Suspense fallback={<LoadingFallback />}><UploadView /></Suspense> },
{ path: '/extraction', element: <Suspense fallback={<LoadingFallback />}><ExtractionView /></Suspense> },
{ path: '/analyse', element: <Suspense fallback={<LoadingFallback />}><AnalyseView /></Suspense> },
{ path: '/contexte', element: <Suspense fallback={<LoadingFallback />}><ContexteView /></Suspense> },
{ path: '/conseil', element: <Suspense fallback={<LoadingFallback />}><ConseilView /></Suspense> },
])
export const AppRouter = () => {
return <RouterProvider router={router} />
}

159
src/services/api.ts Normal file
View File

@ -0,0 +1,159 @@
import axios from 'axios'
import type { Document, ExtractionResult, AnalysisResult, ContextResult, ConseilResult } from '../types'
const BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'
export const apiClient = axios.create({
baseURL: BASE_URL,
timeout: 60000,
})
// Intercepteur pour les erreurs
apiClient.interceptors.response.use(
(response) => response,
(error) => {
// Laisser remonter les erreurs au consommateur
return Promise.reject(error)
}
)
// Services API pour les documents
export const documentApi = {
// Téléversement de document
upload: async (file: File): Promise<Document> => {
const formData = new FormData()
formData.append('file', file)
const { data } = await apiClient.post('/api/notary/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
})
// L'API retourne {message, document_id, status}
// On doit mapper vers le format Document attendu
const fileUrl = URL.createObjectURL(file)
return {
id: data.document_id || data.id || 'upload-' + Date.now(),
name: file.name,
mimeType: data.mime_type || data.mimeType || file.type || 'application/pdf',
functionalType: data.functional_type || data.functionalType || undefined,
size: file.size,
uploadDate: new Date(),
status: 'completed',
previewUrl: fileUrl
}
},
// Extraction des données
extract: async (documentId: string): Promise<ExtractionResult> => {
const { data } = await apiClient.get(`/api/notary/documents/${documentId}`)
// Mapper les données de l'API vers le format ExtractionResult
const results = data.results || {}
return {
documentId,
text: results.ocr_text || 'Texte extrait du document...',
language: 'fr',
documentType: results.document_type || 'Document',
identities: results.entities?.persons?.map((name: string, index: number) => ({
id: `person-${index}`,
type: 'person' as const,
firstName: name.split(' ')[0] || name,
lastName: name.split(' ').slice(1).join(' ') || '',
birthDate: '',
nationality: 'Française',
confidence: 0.9,
})) || [],
addresses: results.entities?.addresses?.map((address: string) => ({
street: address,
city: 'Paris',
postalCode: '75001',
country: 'France',
})) || [],
properties: results.entities?.properties?.map((_propertyName: string, index: number) => ({
id: `prop-${index}`,
type: 'apartment' as const,
address: {
street: '123 Rue de la Paix',
city: 'Paris',
postalCode: '75001',
country: 'France',
},
surface: 75,
cadastralReference: '1234567890AB',
value: 250000,
})) || [],
contracts: [
{
id: 'contract-1',
type: 'sale' as const,
parties: [],
amount: 250000,
date: '2024-01-15',
clauses: ['Clause de garantie', 'Clause de condition suspensive'],
},
],
signatures: results.entities?.persons || [],
confidence: results.verification_score || 0.85,
}
},
// Analyse du document
analyze: async (documentId: string): Promise<AnalysisResult> => {
const { data } = await apiClient.get<AnalysisResult>(`/api/documents/${documentId}/analyze`)
return data
},
// Données contextuelles
getContext: async (documentId: string): Promise<ContextResult> => {
const { data } = await apiClient.get<ContextResult>(`/api/documents/${documentId}/context`)
return data
},
// Conseil LLM
getConseil: async (documentId: string): Promise<ConseilResult> => {
const { data } = await apiClient.get<ConseilResult>(`/api/documents/${documentId}/conseil`)
return data
},
// Détection du type de document
detectType: async (file: File): Promise<{ type: string; confidence: number }> => {
const formData = new FormData()
formData.append('file', file)
const { data } = await apiClient.post('/api/ocr/detect', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
})
return data
},
}
// Services API pour les données externes
export const externalApi = {
// Cadastre via backend
cadastre: async (address: string) => {
const { data } = await apiClient.get('/api/context/cadastre', { params: { q: address } })
return data
},
// Géorisques via backend
georisques: async (coordinates: { lat: number; lng: number }) => {
const { data } = await apiClient.get('/api/context/georisques', { params: coordinates })
return data
},
// Géofoncier via backend
geofoncier: async (address: string) => {
const { data } = await apiClient.get('/api/context/geofoncier', { params: { address } })
return data
},
// BODACC via backend
bodacc: async (companyName: string) => {
const { data } = await apiClient.get('/api/context/bodacc', { params: { q: companyName } })
return data
},
// Infogreffe via backend
infogreffe: async (siren: string) => {
const { data } = await apiClient.get('/api/context/infogreffe', { params: { siren } })
return data
},
}

17
src/store/appSlice.ts Normal file
View File

@ -0,0 +1,17 @@
import { createSlice } from '@reduxjs/toolkit'
export type AppState = {
initialized: boolean
}
const initialState: AppState = {
initialized: true,
}
const appSlice = createSlice({
name: 'app',
initialState,
reducers: {},
})
export const appReducer = appSlice.reducer

108
src/store/documentSlice.ts Normal file
View File

@ -0,0 +1,108 @@
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'
import type { Document, ExtractionResult, AnalysisResult, ContextResult, ConseilResult } from '../types'
import { documentApi } from '../services/api'
interface DocumentState {
documents: Document[]
currentDocument: Document | null
extractionResult: ExtractionResult | null
analysisResult: AnalysisResult | null
contextResult: ContextResult | null
conseilResult: ConseilResult | null
loading: boolean
error: string | null
}
const initialState: DocumentState = {
documents: [],
currentDocument: null,
extractionResult: null,
analysisResult: null,
contextResult: null,
conseilResult: null,
loading: false,
error: null,
}
export const uploadDocument = createAsyncThunk(
'document/upload',
async (file: File) => {
return await documentApi.upload(file)
}
)
export const extractDocument = createAsyncThunk(
'document/extract',
async (documentId: string) => {
return await documentApi.extract(documentId)
}
)
export const analyzeDocument = createAsyncThunk(
'document/analyze',
async (documentId: string) => {
return await documentApi.analyze(documentId)
}
)
export const getContextData = createAsyncThunk(
'document/context',
async (documentId: string) => {
return await documentApi.getContext(documentId)
}
)
export const getConseil = createAsyncThunk(
'document/conseil',
async (documentId: string) => {
return await documentApi.getConseil(documentId)
}
)
const documentSlice = createSlice({
name: 'document',
initialState,
reducers: {
setCurrentDocument: (state, action: PayloadAction<Document | null>) => {
state.currentDocument = action.payload
},
clearResults: (state) => {
state.extractionResult = null
state.analysisResult = null
state.contextResult = null
state.conseilResult = null
},
},
extraReducers: (builder) => {
builder
.addCase(uploadDocument.pending, (state) => {
state.loading = true
state.error = null
})
.addCase(uploadDocument.fulfilled, (state, action) => {
state.loading = false
state.documents.push(action.payload)
state.currentDocument = action.payload
})
.addCase(uploadDocument.rejected, (state, action) => {
state.loading = false
state.error = action.error.message || 'Erreur lors du téléversement'
})
.addCase(extractDocument.fulfilled, (state, action) => {
state.extractionResult = action.payload
})
.addCase(analyzeDocument.fulfilled, (state, action) => {
state.analysisResult = action.payload
})
.addCase(getContextData.fulfilled, (state, action) => {
state.contextResult = action.payload
})
.addCase(getConseil.fulfilled, (state, action) => {
state.conseilResult = action.payload
})
},
})
export const { setCurrentDocument, clearResults } = documentSlice.actions
export const documentReducer = documentSlice.reducer

23
src/store/index.ts Normal file
View File

@ -0,0 +1,23 @@
import { configureStore } from '@reduxjs/toolkit'
import { useDispatch, useSelector } from 'react-redux'
import type { TypedUseSelectorHook } from 'react-redux'
import { appReducer } from './appSlice'
import { documentReducer } from './documentSlice'
export const store = configureStore({
reducer: {
app: appReducer,
document: documentReducer,
},
middleware: (getDefaultMiddleware) => getDefaultMiddleware({
serializableCheck: false,
immutableCheck: true,
}),
devTools: true,
})
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
export const useAppDispatch: () => AppDispatch = useDispatch
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector

65
src/theme/index.ts Normal file
View File

@ -0,0 +1,65 @@
import { createTheme } from '@mui/material/styles'
export const theme = createTheme({
palette: {
mode: 'light',
background: {
default: '#ffffff',
paper: '#ffffff',
},
primary: {
main: '#1976d2',
light: '#42a5f5',
dark: '#1565c0',
},
secondary: {
main: '#dc004e',
light: '#ff5983',
dark: '#9a0036',
},
error: {
main: '#f44336',
},
warning: {
main: '#ff9800',
},
info: {
main: '#2196f3',
},
success: {
main: '#4caf50',
},
},
typography: {
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
h4: {
fontWeight: 600,
},
h6: {
fontWeight: 500,
},
},
components: {
MuiCssBaseline: {
styleOverrides: {
body: {
backgroundColor: '#ffffff',
},
},
},
MuiAppBar: {
styleOverrides: {
root: {
backgroundColor: '#1976d2',
},
},
},
MuiPaper: {
styleOverrides: {
root: {
backgroundColor: '#ffffff',
},
},
},
},
})

97
src/types/index.ts Normal file
View File

@ -0,0 +1,97 @@
export interface Document {
id: string
name: string
mimeType: string
functionalType?: string
size: number
uploadDate: Date
status: 'uploading' | 'processing' | 'completed' | 'error'
previewUrl?: string
content?: string
}
export interface Identity {
id: string
type: 'person' | 'company'
firstName?: string
lastName?: string
companyName?: string
birthDate?: string
nationality?: string
address?: Address
confidence: number
}
export interface Address {
street: string
city: string
postalCode: string
country: string
coordinates?: { lat: number; lng: number }
}
export interface Property {
id: string
type: 'house' | 'apartment' | 'land' | 'commercial'
address: Address
surface?: number
cadastralReference?: string
value?: number
}
export interface Contract {
id: string
type: 'sale' | 'rent' | 'inheritance' | 'donation'
parties: Identity[]
property?: Property
amount?: number
date?: string
clauses: string[]
}
export interface ExtractionResult {
documentId: string
text: string
language: string
documentType: string
identities: Identity[]
addresses: Address[]
properties: Property[]
contracts: Contract[]
signatures: string[]
confidence: number
}
export interface AnalysisResult {
documentId: string
documentType: string
isCNI: boolean
country?: string
verificationResult?: {
numberValid: boolean
formatValid: boolean
checksumValid: boolean
}
credibilityScore: number
summary: string
recommendations: string[]
}
export interface ContextResult {
documentId: string
cadastreData?: Record<string, unknown>
georisquesData?: Record<string, unknown>
geofoncierData?: Record<string, unknown>
bodaccData?: Record<string, unknown>
infogreffeData?: Record<string, unknown>
lastUpdated: Date
}
export interface ConseilResult {
documentId: string
analysis: string
recommendations: string[]
risks: string[]
nextSteps: string[]
generatedAt: Date
}

255
src/views/AnalyseView.tsx Normal file
View File

@ -0,0 +1,255 @@
import { useEffect } from 'react'
import {
Box,
Typography,
Paper,
Card,
CardContent,
Chip,
List,
ListItem,
ListItemText,
ListItemIcon,
Alert,
LinearProgress,
} from '@mui/material'
import {
CheckCircle,
Error,
Warning,
Flag,
Security,
Assessment,
Info,
} from '@mui/icons-material'
import type { ChipProps, LinearProgressProps } from '@mui/material'
import { useAppDispatch, useAppSelector } from '../store'
import { analyzeDocument } from '../store/documentSlice'
import { Layout } from '../components/Layout'
export default function AnalyseView() {
const dispatch = useAppDispatch()
const { currentDocument, analysisResult, loading } = useAppSelector(
(state) => state.document
)
useEffect(() => {
if (currentDocument && !analysisResult) {
dispatch(analyzeDocument(currentDocument.id))
}
}, [currentDocument, analysisResult, 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', flexDirection: 'column', alignItems: 'center', mt: 4 }}>
<LinearProgress sx={{ width: '100%', mb: 2 }} />
<Typography>Analyse en cours...</Typography>
</Box>
</Layout>
)
}
if (!analysisResult) {
return (
<Layout>
<Alert severity="warning">
Aucun résultat d'analyse disponible.
</Alert>
</Layout>
)
}
const getScoreColor = (score: number): ChipProps['color'] => {
if (score >= 0.8) return 'success'
if (score >= 0.6) return 'warning'
return 'error'
}
const getScoreIcon = (score: number) => {
if (score >= 0.8) return <CheckCircle color="success" />
if (score >= 0.6) return <Warning color="warning" />
return <Error color="error" />
}
return (
<Layout>
<Typography variant="h4" gutterBottom>
Analyse du document
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
{/* Résumé général */}
<Paper sx={{ p: 2 }}>
<Typography variant="h6" gutterBottom>
Résumé de l'analyse
</Typography>
<Box sx={{ display: 'flex', gap: 2, flexWrap: 'wrap', alignItems: 'center' }}>
<Chip
icon={<Assessment />}
label={`Score de vraisemblance: ${(analysisResult.credibilityScore * 100).toFixed(1)}%`}
color={getScoreColor(analysisResult.credibilityScore)}
variant="filled"
/>
<Chip
icon={<Info />}
label={`Type: ${analysisResult.documentType}`}
color="primary"
variant="outlined"
/>
{analysisResult.isCNI && (
<Chip
icon={<Flag />}
label={`Pays: ${analysisResult.country}`}
color="secondary"
variant="outlined"
/>
)}
</Box>
</Paper>
{/* Cas CNI */}
{analysisResult.isCNI && (
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
<Security sx={{ mr: 1, verticalAlign: 'middle' }} />
Vérification CNI
</Typography>
{analysisResult.verificationResult && (
<List>
<ListItem>
<ListItemIcon>
{analysisResult.verificationResult.numberValid ? (
<CheckCircle color="success" />
) : (
<Error color="error" />
)}
</ListItemIcon>
<ListItemText
primary="Numéro valide"
secondary={
analysisResult.verificationResult.numberValid
? 'Le numéro de CNI est valide'
: 'Le numéro de CNI est invalide'
}
/>
</ListItem>
<ListItem>
<ListItemIcon>
{analysisResult.verificationResult.formatValid ? (
<CheckCircle color="success" />
) : (
<Error color="error" />
)}
</ListItemIcon>
<ListItemText
primary="Format valide"
secondary={
analysisResult.verificationResult.formatValid
? 'Le format du numéro est correct'
: 'Le format du numéro est incorrect'
}
/>
</ListItem>
<ListItem>
<ListItemIcon>
{analysisResult.verificationResult.checksumValid ? (
<CheckCircle color="success" />
) : (
<Error color="error" />
)}
</ListItemIcon>
<ListItemText
primary="Checksum valide"
secondary={
analysisResult.verificationResult.checksumValid
? 'La somme de contrôle est correcte'
: 'La somme de contrôle est incorrecte'
}
/>
</ListItem>
</List>
)}
</CardContent>
</Card>
)}
<Box sx={{ display: 'flex', gap: 3, flexWrap: 'wrap' }}>
{/* Score de vraisemblance */}
<Box sx={{ flex: '1 1 300px' }}>
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
Score de vraisemblance
</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
{getScoreIcon(analysisResult.credibilityScore)}
<Typography variant="h4" sx={{ ml: 2 }}>
{(analysisResult.credibilityScore * 100).toFixed(1)}%
</Typography>
</Box>
<LinearProgress
variant="determinate"
value={analysisResult.credibilityScore * 100}
color={getScoreColor(analysisResult.credibilityScore) as LinearProgressProps['color']}
sx={{ height: 10, borderRadius: 5 }}
/>
<Typography variant="body2" color="text.secondary" sx={{ mt: 1 }}>
{analysisResult.credibilityScore >= 0.8
? 'Document très fiable'
: analysisResult.credibilityScore >= 0.6
? 'Document moyennement fiable'
: 'Document peu fiable - vérification recommandée'}
</Typography>
</CardContent>
</Card>
</Box>
{/* Synthèse */}
<Box sx={{ flex: '1 1 300px' }}>
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
Synthèse
</Typography>
<Typography variant="body1" sx={{ whiteSpace: 'pre-wrap' }}>
{analysisResult.summary}
</Typography>
</CardContent>
</Card>
</Box>
</Box>
{/* Recommandations */}
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
Recommandations
</Typography>
<List>
{analysisResult.recommendations.map((recommendation, index) => (
<ListItem key={index}>
<ListItemIcon>
<Info color="primary" />
</ListItemIcon>
<ListItemText primary={recommendation} />
</ListItem>
))}
</List>
</CardContent>
</Card>
</Box>
</Layout>
)
}

244
src/views/ConseilView.tsx Normal file
View File

@ -0,0 +1,244 @@
import { useEffect } from 'react'
import {
Box,
Typography,
Paper,
Card,
CardContent,
List,
ListItem,
ListItemText,
ListItemIcon,
Alert,
Chip,
Button,
CircularProgress,
} from '@mui/material'
import {
Lightbulb,
Warning,
CheckCircle,
TrendingUp,
Schedule,
Psychology,
} from '@mui/icons-material'
import type { SvgIconProps } from '@mui/material'
import { useAppDispatch, useAppSelector } from '../store'
import { getConseil } from '../store/documentSlice'
import { Layout } from '../components/Layout'
export default function ConseilView() {
const dispatch = useAppDispatch()
const { currentDocument, conseilResult, loading } = useAppSelector(
(state) => state.document
)
useEffect(() => {
if (currentDocument && !conseilResult) {
dispatch(getConseil(currentDocument.id))
}
}, [currentDocument, conseilResult, 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 }}>Génération des conseils LLM...</Typography>
</Box>
</Layout>
)
}
if (!conseilResult) {
return (
<Layout>
<Alert severity="warning">
Aucun conseil disponible.
</Alert>
</Layout>
)
}
const getRiskColor = (risk: string): SvgIconProps['color'] => {
if (risk.toLowerCase().includes('élevé') || risk.toLowerCase().includes('critique')) {
return 'error'
}
if (risk.toLowerCase().includes('moyen') || risk.toLowerCase().includes('modéré')) {
return 'warning'
}
return 'info'
}
return (
<Layout>
<Typography variant="h4" gutterBottom>
<Psychology sx={{ mr: 1, verticalAlign: 'middle' }} />
Conseil LLM
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
{/* Analyse LLM */}
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
<Lightbulb sx={{ mr: 1, verticalAlign: 'middle' }} />
Analyse LLM
</Typography>
<Paper
sx={{
p: 2,
bgcolor: 'grey.50',
border: '1px solid',
borderColor: 'grey.200',
}}
>
<Typography variant="body1" sx={{ whiteSpace: 'pre-wrap' }}>
{conseilResult.analysis}
</Typography>
</Paper>
<Typography variant="caption" color="text.secondary" sx={{ mt: 1, display: 'block' }}>
Généré le {new Date(conseilResult.generatedAt).toLocaleString()}
</Typography>
</CardContent>
</Card>
<Box sx={{ display: 'flex', gap: 3, flexWrap: 'wrap' }}>
{/* Recommandations */}
<Box sx={{ flex: '1 1 300px' }}>
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
<CheckCircle sx={{ mr: 1, verticalAlign: 'middle' }} />
Recommandations ({conseilResult.recommendations.length})
</Typography>
<List dense>
{conseilResult.recommendations.map((recommendation, index) => (
<ListItem key={index}>
<ListItemIcon>
<CheckCircle color="success" />
</ListItemIcon>
<ListItemText primary={recommendation} />
</ListItem>
))}
</List>
</CardContent>
</Card>
</Box>
{/* Risques identifiés */}
<Box sx={{ flex: '1 1 300px' }}>
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
<Warning sx={{ mr: 1, verticalAlign: 'middle' }} />
Risques identifiés ({conseilResult.risks.length})
</Typography>
<List dense>
{conseilResult.risks.map((risk, index) => (
<ListItem key={index}>
<ListItemIcon>
<Warning color={getRiskColor(risk)} />
</ListItemIcon>
<ListItemText
primary={risk}
primaryTypographyProps={{
color: getRiskColor(risk) === 'error' ? 'error.main' :
getRiskColor(risk) === 'warning' ? 'warning.main' : 'info.main'
}}
/>
</ListItem>
))}
</List>
</CardContent>
</Card>
</Box>
</Box>
{/* Prochaines étapes */}
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
<TrendingUp sx={{ mr: 1, verticalAlign: 'middle' }} />
Prochaines étapes recommandées
</Typography>
<List>
{conseilResult.nextSteps.map((step, index) => (
<ListItem key={index}>
<ListItemIcon>
<Schedule color="primary" />
</ListItemIcon>
<ListItemText
primary={`Étape ${index + 1}`}
secondary={step}
/>
</ListItem>
))}
</List>
</CardContent>
</Card>
{/* Actions */}
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
Actions
</Typography>
<Box sx={{ display: 'flex', gap: 2, flexWrap: 'wrap' }}>
<Button
variant="contained"
onClick={() => dispatch(getConseil(currentDocument.id))}
disabled={loading}
>
Régénérer les conseils
</Button>
<Button variant="outlined">
Exporter le rapport
</Button>
<Button variant="outlined">
Partager avec l'équipe
</Button>
</Box>
</CardContent>
</Card>
{/* Résumé exécutif */}
<Paper sx={{ p: 2, bgcolor: 'primary.50' }}>
<Typography variant="h6" gutterBottom>
Résumé exécutif
</Typography>
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap', mb: 2 }}>
<Chip
label={`${conseilResult.recommendations.length} recommandations`}
color="success"
variant="outlined"
/>
<Chip
label={`${conseilResult.risks.length} risques identifiés`}
color="warning"
variant="outlined"
/>
<Chip
label={`${conseilResult.nextSteps.length} étapes suivantes`}
color="info"
variant="outlined"
/>
</Box>
<Typography variant="body2" color="text.secondary">
Cette analyse LLM a é générée automatiquement et doit être validée par un expert notarial.
</Typography>
</Paper>
</Box>
</Layout>
)
}

299
src/views/ContexteView.tsx Normal file
View File

@ -0,0 +1,299 @@
import { useEffect } from 'react'
import {
Box,
Typography,
Paper,
Card,
CardContent,
Chip,
Alert,
Button,
Accordion,
AccordionSummary,
AccordionDetails,
CircularProgress,
} from '@mui/material'
import {
ExpandMore,
LocationOn,
Warning,
CheckCircle,
Error,
Public,
Business,
Home,
} from '@mui/icons-material'
import type { ChipProps } from '@mui/material'
import { useAppDispatch, useAppSelector } from '../store'
import { getContextData } from '../store/documentSlice'
import { Layout } from '../components/Layout'
export default function ContexteView() {
const dispatch = useAppDispatch()
const { currentDocument, contextResult, loading } = useAppSelector(
(state) => state.document
)
useEffect(() => {
if (currentDocument && !contextResult) {
dispatch(getContextData(currentDocument.id))
}
}, [currentDocument, contextResult, 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 }}>Recherche d'informations contextuelles...</Typography>
</Box>
</Layout>
)
}
if (!contextResult) {
return (
<Layout>
<Alert severity="warning">
Aucune donnée contextuelle disponible.
</Alert>
</Layout>
)
}
const getStatusIcon = (hasData: boolean) => {
return hasData ? <CheckCircle color="success" /> : <Error color="error" />
}
const getStatusColor = (hasData: boolean): ChipProps['color'] => {
return hasData ? 'success' : 'error'
}
return (
<Layout>
<Typography variant="h4" gutterBottom>
Informations contextuelles
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
{/* Résumé des sources */}
<Paper sx={{ p: 2 }}>
<Typography variant="h6" gutterBottom>
Sources de données consultées
</Typography>
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
<Chip
icon={getStatusIcon(!!contextResult.cadastreData)}
label="Cadastre"
color={getStatusColor(!!contextResult.cadastreData)}
variant="outlined"
/>
<Chip
icon={getStatusIcon(!!contextResult.georisquesData)}
label="Géorisques"
color={getStatusColor(!!contextResult.georisquesData)}
variant="outlined"
/>
<Chip
icon={getStatusIcon(!!contextResult.geofoncierData)}
label="Géofoncier"
color={getStatusColor(!!contextResult.geofoncierData)}
variant="outlined"
/>
<Chip
icon={getStatusIcon(!!contextResult.bodaccData)}
label="BODACC"
color={getStatusColor(!!contextResult.bodaccData)}
variant="outlined"
/>
<Chip
icon={getStatusIcon(!!contextResult.infogreffeData)}
label="Infogreffe"
color={getStatusColor(!!contextResult.infogreffeData)}
variant="outlined"
/>
</Box>
<Typography variant="caption" color="text.secondary" sx={{ mt: 1, display: 'block' }}>
Dernière mise à jour: {new Date(contextResult.lastUpdated).toLocaleString()}
</Typography>
</Paper>
{/* Données cadastrales */}
<Accordion>
<AccordionSummary expandIcon={<ExpandMore />}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Home sx={{ mr: 1 }} />
<Typography variant="h6">Données cadastrales</Typography>
<Chip
label={contextResult.cadastreData ? 'Disponible' : 'Non disponible'}
color={getStatusColor(!!contextResult.cadastreData)}
size="small"
sx={{ ml: 2 }}
/>
</Box>
</AccordionSummary>
<AccordionDetails>
{contextResult.cadastreData ? (
<Box>
<Typography variant="body2" sx={{ whiteSpace: 'pre-wrap' }}>
{JSON.stringify(contextResult.cadastreData, null, 2)}
</Typography>
</Box>
) : (
<Alert severity="info">
Aucune donnée cadastrale trouvée pour ce document.
</Alert>
)}
</AccordionDetails>
</Accordion>
{/* Données Géorisques */}
<Accordion>
<AccordionSummary expandIcon={<ExpandMore />}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Warning sx={{ mr: 1 }} />
<Typography variant="h6">Données Géorisques</Typography>
<Chip
label={contextResult.georisquesData ? 'Disponible' : 'Non disponible'}
color={getStatusColor(!!contextResult.georisquesData)}
size="small"
sx={{ ml: 2 }}
/>
</Box>
</AccordionSummary>
<AccordionDetails>
{contextResult.georisquesData ? (
<Box>
<Typography variant="body2" sx={{ whiteSpace: 'pre-wrap' }}>
{JSON.stringify(contextResult.georisquesData, null, 2)}
</Typography>
</Box>
) : (
<Alert severity="info">
Aucune donnée Géorisques trouvée pour ce document.
</Alert>
)}
</AccordionDetails>
</Accordion>
{/* Données Géofoncier */}
<Accordion>
<AccordionSummary expandIcon={<ExpandMore />}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<LocationOn sx={{ mr: 1 }} />
<Typography variant="h6">Données Géofoncier</Typography>
<Chip
label={contextResult.geofoncierData ? 'Disponible' : 'Non disponible'}
color={getStatusColor(!!contextResult.geofoncierData)}
size="small"
sx={{ ml: 2 }}
/>
</Box>
</AccordionSummary>
<AccordionDetails>
{contextResult.geofoncierData ? (
<Box>
<Typography variant="body2" sx={{ whiteSpace: 'pre-wrap' }}>
{JSON.stringify(contextResult.geofoncierData, null, 2)}
</Typography>
</Box>
) : (
<Alert severity="info">
Aucune donnée Géofoncier trouvée pour ce document.
</Alert>
)}
</AccordionDetails>
</Accordion>
{/* Données BODACC */}
<Accordion>
<AccordionSummary expandIcon={<ExpandMore />}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Public sx={{ mr: 1 }} />
<Typography variant="h6">Données BODACC</Typography>
<Chip
label={contextResult.bodaccData ? 'Disponible' : 'Non disponible'}
color={getStatusColor(!!contextResult.bodaccData)}
size="small"
sx={{ ml: 2 }}
/>
</Box>
</AccordionSummary>
<AccordionDetails>
{contextResult.bodaccData ? (
<Box>
<Typography variant="body2" sx={{ whiteSpace: 'pre-wrap' }}>
{JSON.stringify(contextResult.bodaccData, null, 2)}
</Typography>
</Box>
) : (
<Alert severity="info">
Aucune donnée BODACC trouvée pour ce document.
</Alert>
)}
</AccordionDetails>
</Accordion>
{/* Données Infogreffe */}
<Accordion>
<AccordionSummary expandIcon={<ExpandMore />}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Business sx={{ mr: 1 }} />
<Typography variant="h6">Données Infogreffe</Typography>
<Chip
label={contextResult.infogreffeData ? 'Disponible' : 'Non disponible'}
color={getStatusColor(!!contextResult.infogreffeData)}
size="small"
sx={{ ml: 2 }}
/>
</Box>
</AccordionSummary>
<AccordionDetails>
{contextResult.infogreffeData ? (
<Box>
<Typography variant="body2" sx={{ whiteSpace: 'pre-wrap' }}>
{JSON.stringify(contextResult.infogreffeData, null, 2)}
</Typography>
</Box>
) : (
<Alert severity="info">
Aucune donnée Infogreffe trouvée pour ce document.
</Alert>
)}
</AccordionDetails>
</Accordion>
{/* Actions */}
<Card>
<CardContent>
<Typography variant="h6" gutterBottom>
Actions
</Typography>
<Box sx={{ display: 'flex', gap: 2 }}>
<Button
variant="contained"
onClick={() => dispatch(getContextData(currentDocument.id))}
disabled={loading}
>
Actualiser les données
</Button>
<Button variant="outlined">
Exporter le rapport
</Button>
</Box>
</CardContent>
</Card>
</Box>
</Layout>
)
}

View File

@ -0,0 +1,291 @@
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 || 0})
</Typography>
<List dense>
{(extractionResult.identities || []).map((identity, index) => (
<ListItem key={index}>
<ListItemText
primary={
identity.type === 'person'
? `${identity.firstName} ${identity.lastName}`
: identity.companyName
}
secondaryTypographyProps={{ component: 'span' }}
secondary={
<Box component="span">
<Typography variant="caption" display="block" component="span">
Type: {identity.type}
</Typography>
{identity.birthDate && (
<Typography variant="caption" display="block" component="span">
Naissance: {identity.birthDate}
</Typography>
)}
{identity.nationality && (
<Typography variant="caption" display="block" component="span">
Nationalité: {identity.nationality}
</Typography>
)}
<Typography variant="caption" display="block" component="span">
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 || 0})
</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 || 0})
</Typography>
<List dense>
{(extractionResult.properties || []).map((property, index) => (
<ListItem key={index}>
<ListItemText
primary={`${property.type} - ${property.address.city}`}
secondaryTypographyProps={{ component: 'span' }}
secondary={
<Box component="span">
<Typography variant="caption" display="block" component="span">
{property.address.street}
</Typography>
{property.surface && (
<Typography variant="caption" display="block" component="span">
Surface: {property.surface} m²
</Typography>
)}
{property.cadastralReference && (
<Typography variant="caption" display="block" component="span">
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 || 0})
</Typography>
<List dense>
{(extractionResult.contracts || []).map((contract, index) => (
<ListItem key={index}>
<ListItemText
primary={`${contract.type} - ${contract.amount ? `${contract.amount}` : 'Montant non spécifié'}`}
secondaryTypographyProps={{ component: 'span' }}
secondary={
<Box component="span">
<Typography variant="caption" display="block" component="span">
Parties: {contract.parties.length}
</Typography>
{contract.date && (
<Typography variant="caption" display="block" component="span">
Date: {contract.date}
</Typography>
)}
<Typography variant="caption" display="block" component="span">
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 || 0})
</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>
)
}

170
src/views/UploadView.tsx Normal file
View File

@ -0,0 +1,170 @@
import { useCallback, useState } from 'react'
import { useDropzone } from 'react-dropzone'
import { Box, Typography, Paper, CircularProgress, Alert, Button, Chip, Grid } from '@mui/material'
import {
CloudUpload,
CheckCircle,
Error,
HourglassEmpty,
Visibility,
} from '@mui/icons-material'
import { useAppDispatch, useAppSelector } from '../store'
import { uploadDocument } from '../store/documentSlice'
import { Layout } from '../components/Layout'
import { FilePreview } from '../components/FilePreview'
import type { Document } from '../types'
export default function UploadView() {
const dispatch = useAppDispatch()
const { documents, error } = useAppSelector((state) => state.document)
const [previewDocument, setPreviewDocument] = useState<Document | null>(null)
const onDrop = useCallback(
(acceptedFiles: File[]) => {
acceptedFiles.forEach((file) => {
dispatch(uploadDocument(file))
})
},
[dispatch]
)
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
accept: {
'application/pdf': ['.pdf'],
'image/*': ['.png', '.jpg', '.jpeg', '.tiff'],
'text/plain': ['.txt'],
'text/markdown': ['.md'],
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['.docx'],
},
multiple: true,
})
const getStatusIcon = (status: string) => {
switch (status) {
case 'completed':
return <CheckCircle color="success" />
case 'error':
return <Error color="error" />
case 'processing':
return <CircularProgress size={20} />
default:
return <HourglassEmpty color="action" />
}
}
const getStatusColor = (status: string) => {
switch (status) {
case 'completed':
return 'success'
case 'error':
return 'error'
case 'processing':
return 'warning'
default:
return 'default'
}
}
return (
<Layout>
<Typography variant="h4" gutterBottom>
Téléversement de documents
</Typography>
<Paper
{...getRootProps()}
sx={{
p: 4,
textAlign: 'center',
cursor: 'pointer',
border: '2px dashed',
borderColor: isDragActive ? 'primary.main' : 'grey.300',
bgcolor: isDragActive ? 'action.hover' : 'background.paper',
'&:hover': {
borderColor: 'primary.main',
bgcolor: 'action.hover',
},
}}
>
<input {...getInputProps()} />
<CloudUpload sx={{ fontSize: 48, color: 'primary.main', mb: 2 }} />
<Typography variant="h6" gutterBottom>
{isDragActive
? 'Déposez les fichiers ici...'
: 'Glissez-déposez vos documents ou cliquez pour sélectionner'}
</Typography>
<Typography variant="body2" color="text.secondary">
Formats acceptés: PDF, PNG, JPG, JPEG, TIFF, TXT, MD, DOCX
</Typography>
</Paper>
{error && (
<Alert severity="error" sx={{ mt: 2 }}>
{error}
</Alert>
)}
{documents.length > 0 && (
<Box sx={{ mt: 3 }}>
<Typography variant="h6" gutterBottom>
Documents téléversés ({documents.length})
</Typography>
<Grid container spacing={2}>
{documents.map((doc, index) => (
<Grid size={{ xs: 12, md: 6 }} key={`${doc.id}-${index}`}>
<Paper sx={{ p: 2 }}>
<Box display="flex" justifyContent="space-between" alignItems="center" mb={2}>
<Box display="flex" alignItems="center" gap={1}>
{getStatusIcon(doc.status)}
<Typography variant="subtitle1" noWrap>
{doc.name}
</Typography>
</Box>
<Box display="flex" gap={1}>
<Button
size="small"
startIcon={<Visibility />}
onClick={() => setPreviewDocument(doc)}
disabled={doc.status !== 'completed'}
>
Aperçu
</Button>
</Box>
</Box>
<Box display="flex" gap={1} flexWrap="wrap">
<Chip
label={doc.functionalType || doc.mimeType}
size="small"
variant="outlined"
/>
<Chip
label={doc.status}
size="small"
color={getStatusColor(doc.status) as 'success' | 'error' | 'warning' | 'default'}
/>
<Chip
label={`${(doc.size / 1024 / 1024).toFixed(2)} MB`}
size="small"
variant="outlined"
/>
</Box>
</Paper>
</Grid>
))}
</Grid>
</Box>
)}
{/* Aperçu du document */}
{previewDocument && (
<FilePreview
document={previewDocument}
onClose={() => setPreviewDocument(null)}
/>
)}
</Layout>
)
}

30
test-files/sample.md Normal file
View File

@ -0,0 +1,30 @@
# Acte de Vente Immobilière
## Informations générales
- **Type** : Acte de vente
- **Date** : 15 janvier 2024
- **Lieu** : Paris
## Parties contractantes
- **Vendeur** : Jean Dupont
- **Acheteur** : Marie Martin
## Objet de la vente
- **Bien** : Appartement
- **Adresse** : 123 Rue de la Paix, 75001 Paris
- **Surface** : 75 m²
- **Prix** : 250 000 €
## Clauses particulières
1. Clause de garantie des vices cachés
2. Clause de condition suspensive
3. Clause de garantie d'éviction
## Signatures
- Jean Dupont : [Signature]
- Marie Martin : [Signature]

84
test-files/sample.pdf Normal file
View File

@ -0,0 +1,84 @@
%PDF-1.4
1 0 obj
<<
/Type /Catalog
/Pages 2 0 R
>>
endobj
2 0 obj
<<
/Type /Pages
/Kids [3 0 R]
/Count 1
>>
endobj
3 0 obj
<<
/Type /Page
/Parent 2 0 R
/MediaBox [0 0 612 792]
/Contents 4 0 R
/Resources <<
/Font <<
/F1 5 0 R
>>
>>
>>
endobj
4 0 obj
<<
/Length 200
>>
stream
BT
/F1 12 Tf
72 720 Td
(ACTE DE VENTE IMMOBILIERE) Tj
0 -20 Td
/F1 10 Tf
(Entre les soussignes :) Tj
0 -15 Td
(- Vendeur : Jean Dupont, ne le 15/05/1980) Tj
0 -15 Td
(- Acheteur : Marie Martin, nee le 22/03/1985) Tj
0 -20 Td
(Objet : Vente d'un appartement) Tj
0 -15 Td
(Adresse : 123 Rue de la Paix, 75001 Paris) Tj
0 -15 Td
(Surface : 75 m2) Tj
0 -15 Td
(Prix : 250 000 euros) Tj
0 -20 Td
(Fait a Paris, le 15 janvier 2024) Tj
ET
endstream
endobj
5 0 obj
<<
/Type /Font
/Subtype /Type1
/BaseFont /Helvetica
>>
endobj
xref
0 6
0000000000 65535 f
0000000009 00000 n
0000000058 00000 n
0000000115 00000 n
0000000304 00000 n
0000000554 00000 n
trailer
<<
/Size 6
/Root 1 0 R
>>
startxref
650
%%EOF

20
test-files/sample.txt Normal file
View File

@ -0,0 +1,20 @@
ACTE DE VENTE IMMOBILIÈRE
Entre les soussignés :
- Vendeur : Jean Dupont, né le 15/05/1980, demeurant 123 Rue de la Paix, 75001 Paris
- Acheteur : Marie Martin, née le 22/03/1985, demeurant 456 Avenue des Champs, 75008 Paris
Objet : Vente d'un appartement situé 123 Rue de la Paix, 75001 Paris
Surface : 75 m²
Prix : 250 000 euros
Clauses particulières :
- Clause de garantie des vices cachés
- Clause de condition suspensive d'obtention du prêt
- Clause de garantie d'éviction
Fait à Paris, le 15 janvier 2024
Signatures :
Jean Dupont : [Signature]
Marie Martin : [Signature]

288
test-preview.html Normal file
View File

@ -0,0 +1,288 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test Aperçu 4NK_IA_front</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1000px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
.container {
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.status {
padding: 15px;
border-radius: 8px;
margin: 20px 0;
}
.success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
.error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
.info { background: #d1ecf1; color: #0c5460; border: 1px solid #bee5eb; }
.test-section {
margin: 20px 0;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
background: #fafafa;
}
.file-test {
display: inline-block;
margin: 10px;
padding: 15px;
background: white;
border: 2px dashed #007bff;
border-radius: 8px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
}
.file-test:hover {
background: #e3f2fd;
border-color: #0056b3;
}
.instructions {
background: #e3f2fd;
padding: 20px;
border-radius: 8px;
margin: 20px 0;
}
.step {
margin: 10px 0;
padding: 10px;
background: white;
border-left: 4px solid #007bff;
border-radius: 4px;
}
</style>
</head>
<body>
<div class="container">
<h1>🧪 Test de la fonctionnalité d'aperçu - 4NK_IA_front</h1>
<div class="status info">
<h3>📋 Instructions de test :</h3>
<div class="step">
<strong>Étape 1 :</strong> Ouvrez l'application frontend :
<a href="http://localhost:5173" target="_blank" style="color: #007bff; text-decoration: none;">
<strong>http://localhost:5173</strong>
</a>
</div>
<div class="step">
<strong>Étape 2 :</strong> Allez dans l'onglet "Upload" (premier onglet)
</div>
<div class="step">
<strong>Étape 3 :</strong> Testez l'upload avec les fichiers de test ci-dessous
</div>
<div class="step">
<strong>Étape 4 :</strong> Cliquez sur "Aperçu" pour voir le contenu du document
</div>
</div>
<div class="test-section">
<h2>📁 Fichiers de test disponibles</h2>
<p>Cliquez sur un fichier pour le télécharger et le tester :</p>
<div class="file-test" onclick="downloadFile('sample.txt')">
<h4>📄 sample.txt</h4>
<p>Document texte avec acte de vente</p>
<small>Format: .txt</small>
</div>
<div class="file-test" onclick="downloadFile('sample.md')">
<h4>📝 sample.md</h4>
<p>Document Markdown avec acte de vente</p>
<small>Format: .md</small>
</div>
<div class="file-test" onclick="createTestPDF()">
<h4>📋 Test PDF</h4>
<p>Créer un PDF de test</p>
<small>Format: .pdf</small>
</div>
</div>
<div class="test-section">
<h2>🎯 Types de fichiers supportés</h2>
<ul>
<li><strong>PDF</strong> (.pdf) - Aperçu avec contenu simulé</li>
<li><strong>Images</strong> (.png, .jpg, .jpeg, .tiff) - Aperçu d'image</li>
<li><strong>Texte</strong> (.txt) - Contenu textuel affiché</li>
<li><strong>Markdown</strong> (.md) - Contenu Markdown formaté</li>
<li><strong>Word</strong> (.docx) - Aperçu de document Word</li>
</ul>
</div>
<div class="test-section">
<h2>✨ Fonctionnalités d'aperçu</h2>
<ul>
<li><strong>Affichage du contenu</strong> - Aperçu du contenu du document</li>
<li><strong>Informations du fichier</strong> - Type, taille, statut</li>
<li><strong>Bouton de téléchargement</strong> - Télécharger le document</li>
<li><strong>Interface responsive</strong> - Adaptation mobile/desktop</li>
<li><strong>Gestion d'erreurs</strong> - Messages d'erreur appropriés</li>
</ul>
</div>
<div class="status success">
<h3>✅ Améliorations apportées</h3>
<ul>
<li>Support des formats TXT, MD, DOCX en plus de PDF et images</li>
<li>Composant FilePreview dédié pour l'aperçu</li>
<li>Interface utilisateur améliorée avec grille responsive</li>
<li>Bouton "Aperçu" pour chaque document uploadé</li>
<li>Affichage du contenu simulé selon le type de fichier</li>
<li>Gestion des états de chargement et d'erreur</li>
</ul>
</div>
<div class="status info">
<h3>🔧 Dépannage</h3>
<p>Si l'aperçu ne fonctionne pas :</p>
<ul>
<li>Vérifiez que l'application est accessible sur http://localhost:5173</li>
<li>Assurez-vous que le document est uploadé avec succès (statut "completed")</li>
<li>Cliquez sur le bouton "Aperçu" (icône 👁️)</li>
<li>Vérifiez la console du navigateur pour d'éventuelles erreurs</li>
</ul>
</div>
</div>
<script>
function downloadFile(filename) {
const content = getFileContent(filename);
const blob = new Blob([content], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
function getFileContent(filename) {
if (filename === 'sample.txt') {
return `ACTE DE VENTE IMMOBILIÈRE
Entre les soussignés :
- Vendeur : Jean Dupont, né le 15/05/1980, demeurant 123 Rue de la Paix, 75001 Paris
- Acheteur : Marie Martin, née le 22/03/1985, demeurant 456 Avenue des Champs, 75008 Paris
Objet : Vente d'un appartement situé 123 Rue de la Paix, 75001 Paris
Surface : 75 m²
Prix : 250 000 euros
Clauses particulières :
- Clause de garantie des vices cachés
- Clause de condition suspensive d'obtention du prêt
- Clause de garantie d'éviction
Fait à Paris, le 15 janvier 2024
Signatures :
Jean Dupont : [Signature]
Marie Martin : [Signature]`;
} else if (filename === 'sample.md') {
return `# Acte de Vente Immobilière
## Informations générales
- **Type** : Acte de vente
- **Date** : 15 janvier 2024
- **Lieu** : Paris
## Parties contractantes
- **Vendeur** : Jean Dupont
- **Acheteur** : Marie Martin
## Objet de la vente
- **Bien** : Appartement
- **Adresse** : 123 Rue de la Paix, 75001 Paris
- **Surface** : 75 m²
- **Prix** : 250 000 €
## Clauses particulières
1. Clause de garantie des vices cachés
2. Clause de condition suspensive
3. Clause de garantie d'éviction
## Signatures
- Jean Dupont : [Signature]
- Marie Martin : [Signature]`;
}
return '';
}
function createTestPDF() {
// Créer un PDF simple pour le test
const content = `%PDF-1.4
1 0 obj
<<
/Type /Catalog
/Pages 2 0 R
>>
endobj
2 0 obj
<<
/Type /Pages
/Kids [3 0 R]
/Count 1
>>
endobj
3 0 obj
<<
/Type /Page
/Parent 2 0 R
/MediaBox [0 0 612 792]
/Contents 4 0 R
>>
endobj
4 0 obj
<<
/Length 44
>>
stream
BT
/F1 12 Tf
72 720 Td
(Test PDF Document) Tj
ET
endstream
endobj
xref
0 5
0000000000 65535 f
0000000009 00000 n
0000000058 00000 n
0000000115 00000 n
0000000204 00000 n
trailer
<<
/Size 5
/Root 1 0 R
>>
startxref
297
%%EOF`;
const blob = new Blob([content], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'test-document.pdf';
a.click();
URL.revokeObjectURL(url);
}
</script>
</body>
</html>

110
test-upload.html Normal file
View File

@ -0,0 +1,110 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test Upload 4NK_IA_front</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.test-section {
margin: 20px 0;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
}
.file-list {
list-style: none;
padding: 0;
}
.file-list li {
margin: 10px 0;
padding: 10px;
background: #f5f5f5;
border-radius: 4px;
}
.instructions {
background: #e3f2fd;
padding: 15px;
border-radius: 8px;
margin: 20px 0;
}
</style>
</head>
<body>
<h1>🧪 Test de la fonctionnalité d'aperçu - 4NK_IA_front</h1>
<div class="instructions">
<h3>📋 Instructions de test :</h3>
<ol>
<li>Ouvrez l'application frontend : <a href="http://localhost:5173" target="_blank">http://localhost:5173</a></li>
<li>Allez dans l'onglet "Upload"</li>
<li>Testez l'upload avec les fichiers de test ci-dessous</li>
<li>Cliquez sur "Aperçu" pour voir le contenu du document</li>
</ol>
</div>
<div class="test-section">
<h2>📁 Fichiers de test disponibles</h2>
<ul class="file-list">
<li>
<strong>sample.txt</strong> - Document texte avec acte de vente
<br><small>Chemin : test-files/sample.txt</small>
</li>
<li>
<strong>sample.md</strong> - Document Markdown avec acte de vente
<br><small>Chemin : test-files/sample.md</small>
</li>
</ul>
</div>
<div class="test-section">
<h2>🎯 Types de fichiers supportés</h2>
<ul>
<li><strong>PDF</strong> (.pdf) - Aperçu avec contenu simulé</li>
<li><strong>Images</strong> (.png, .jpg, .jpeg, .tiff) - Aperçu d'image</li>
<li><strong>Texte</strong> (.txt) - Contenu textuel affiché</li>
<li><strong>Markdown</strong> (.md) - Contenu Markdown formaté</li>
<li><strong>Word</strong> (.docx) - Aperçu de document Word</li>
</ul>
</div>
<div class="test-section">
<h2>✨ Fonctionnalités d'aperçu</h2>
<ul>
<li><strong>Affichage du contenu</strong> - Aperçu du contenu du document</li>
<li><strong>Informations du fichier</strong> - Type, taille, statut</li>
<li><strong>Bouton de téléchargement</strong> - Télécharger le document</li>
<li><strong>Interface responsive</strong> - Adaptation mobile/desktop</li>
<li><strong>Gestion d'erreurs</strong> - Messages d'erreur appropriés</li>
</ul>
</div>
<div class="test-section">
<h2>🔧 Améliorations apportées</h2>
<ul>
<li>✅ Support des formats TXT, MD, DOCX en plus de PDF et images</li>
<li>✅ Composant FilePreview dédié pour l'aperçu</li>
<li>✅ Interface utilisateur améliorée avec grille responsive</li>
<li>✅ Bouton "Aperçu" pour chaque document uploadé</li>
<li>✅ Affichage du contenu simulé selon le type de fichier</li>
<li>✅ Gestion des états de chargement et d'erreur</li>
</ul>
</div>
<div class="instructions">
<h3>🚀 Prochaines étapes :</h3>
<p>Pour une implémentation complète, il faudrait :</p>
<ul>
<li>Intégrer avec l'API backend pour récupérer le vrai contenu</li>
<li>Ajouter un viewer PDF intégré</li>
<li>Implémenter l'extraction de texte pour les images</li>
<li>Ajouter la conversion de documents Word</li>
</ul>
</div>
</body>
</html>