style: aligne le balayage de re-extraction sur le design system
All checks were successful
Build and Publish Docker Image / Build App Image (push) Successful in 1m16s
Build and Publish Docker Image / Build Summary (push) Successful in 3s

La page et le panneau de differences avaient ete ecrits avant que main ne pose
un design system unique : classes Tailwind brutes, et surtout un detail
depliable reste sur fond clair alors que son contenu venait de passer en sombre.

Reprend les classes du systeme (page, card, table, btn, badge, subcard,
empty-state, spinner) et ramene le detail au fond sombre. Les avertissements de
reecriture passent en amber-400, illisibles jusqu'ici sur fond sombre.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-26 07:09:46 +02:00
parent 92cf21309f
commit 41107c984c
2 changed files with 45 additions and 58 deletions

View File

@@ -30,7 +30,7 @@
class="flex-1 min-w-0 px-1.5 py-0.5 rounded border border-gray-600 bg-gray-900 text-green-400 font-medium disabled:opacity-40 disabled:line-through focus:ring-1 focus:ring-amber-500 focus:border-amber-500 focus:outline-none" class="flex-1 min-w-0 px-1.5 py-0.5 rounded border border-gray-600 bg-gray-900 text-green-400 font-medium disabled:opacity-40 disabled:line-through focus:ring-1 focus:ring-amber-500 focus:border-amber-500 focus:outline-none"
/> />
</div> </div>
<p v-if="overrideLabel(c.id)" class="pl-6 text-orange-700"> {{ overrideLabel(c.id) }}</p> <p v-if="overrideLabel(c.id)" class="pl-6 text-amber-400"> {{ overrideLabel(c.id) }}</p>
</div> </div>
</div> </div>
</div> </div>
@@ -53,7 +53,7 @@
</span> </span>
<span class="font-medium text-gray-200">{{ loc.title }}</span> <span class="font-medium text-gray-200">{{ loc.title }}</span>
</div> </div>
<p v-if="loc.id && overrideLabel(loc.id)" class="pl-5 text-orange-700 mb-0.5"> {{ overrideLabel(loc.id) }}</p> <p v-if="loc.id && overrideLabel(loc.id)" class="pl-5 text-amber-400 mb-0.5"> {{ overrideLabel(loc.id) }}</p>
<div v-for="c in loc.changes" :key="c.id"> <div v-for="c in loc.changes" :key="c.id">
<!-- Champ identitaire : cochable et modifiable individuellement --> <!-- Champ identitaire : cochable et modifiable individuellement -->
@@ -83,7 +83,7 @@
class="flex-1 min-w-0 px-1.5 py-0.5 rounded border border-gray-600 bg-gray-900 text-green-400 font-medium disabled:opacity-40 disabled:line-through focus:ring-1 focus:ring-amber-500 focus:border-amber-500 focus:outline-none" class="flex-1 min-w-0 px-1.5 py-0.5 rounded border border-gray-600 bg-gray-900 text-green-400 font-medium disabled:opacity-40 disabled:line-through focus:ring-1 focus:ring-amber-500 focus:border-amber-500 focus:outline-none"
/> />
</div> </div>
<p v-if="overrideLabel(c.id)" class="pl-6 text-orange-700"> {{ overrideLabel(c.id) }}</p> <p v-if="overrideLabel(c.id)" class="pl-6 text-amber-400"> {{ overrideLabel(c.id) }}</p>
</div> </div>
<!-- Montants et lignes : un seul bloc, pour que les sommes restent justes --> <!-- Montants et lignes : un seul bloc, pour que les sommes restent justes -->
@@ -107,7 +107,7 @@
<span class="text-green-400 font-medium break-all" :class="!accepted(c.id) && 'line-through'">{{ f.after }}</span> <span class="text-green-400 font-medium break-all" :class="!accepted(c.id) && 'line-through'">{{ f.after }}</span>
</div> </div>
</div> </div>
<p v-if="overrideLabel(c.id)" class="pl-5 text-orange-700"> {{ overrideLabel(c.id) }}</p> <p v-if="overrideLabel(c.id)" class="pl-5 text-amber-400"> {{ overrideLabel(c.id) }}</p>
</div> </div>
</div> </div>
</div> </div>
@@ -136,7 +136,7 @@
<div class="text-red-400 break-all" :class="accepted(op.id) && 'line-through'">{{ op.before }}</div> <div class="text-red-400 break-all" :class="accepted(op.id) && 'line-through'">{{ op.before }}</div>
<div class="text-green-400 font-medium break-all" :class="!accepted(op.id) && 'line-through'">{{ op.after }}</div> <div class="text-green-400 font-medium break-all" :class="!accepted(op.id) && 'line-through'">{{ op.after }}</div>
</div> </div>
<p v-if="overrideLabel(op.id)" class="pl-2 text-orange-700"> {{ overrideLabel(op.id) }}</p> <p v-if="overrideLabel(op.id)" class="pl-2 text-amber-400"> {{ overrideLabel(op.id) }}</p>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,12 +1,12 @@
<template> <template>
<div class="flex-1 overflow-auto bg-gray-800"> <div class="page">
<div class="max-w-6xl mx-auto p-6 space-y-6 pb-28"> <div class="page-content pb-28">
<!-- Header --> <!-- Header -->
<div class="flex items-start justify-between gap-6"> <div class="page-header">
<div> <div>
<h1 class="text-xl font-semibold text-white">-extraction de la base</h1> <h1 class="page-title">-extraction de la base</h1>
<p class="text-sm text-gray-400 mt-1 max-w-2xl"> <p class="page-subtitle max-w-2xl">
Rejoue l'extraction de chaque PDF stocké après une amélioration des parsers. Rejoue l'extraction de chaque PDF stocké après une amélioration des parsers.
Rien n'est écrit en base tant que vous n'avez pas validé : les documents dont Rien n'est écrit en base tant que vous n'avez pas validé : les documents dont
l'extraction change sont listés ci-dessous, à appliquer en une fois. l'extraction change sont listés ci-dessous, à appliquer en une fois.
@@ -18,26 +18,22 @@
v-if="!isScanning" v-if="!isScanning"
@click="startScan" @click="startScan"
:disabled="isLoading || isApplying || documents.length === 0" :disabled="isLoading || isApplying || documents.length === 0"
class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50 flex items-center gap-2" class="btn btn-primary"
> >
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" /> d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg> </svg>
{{ hasScanned ? 'Relancer le balayage' : 'Lancer le balayage' }} {{ hasScanned ? 'Relancer le balayage' : 'Lancer le balayage' }}
</button> </button>
<button <button v-else @click="stopScan" class="btn btn-secondary">
v-else
@click="stopScan"
class="px-4 py-2 bg-gray-700 text-white rounded-lg hover:bg-gray-600 transition-colors"
>
Arrêter Arrêter
</button> </button>
</div> </div>
</div> </div>
<!-- Progression du balayage --> <!-- Progression du balayage -->
<div v-if="isScanning || hasScanned" class="bg-gray-900 rounded-lg border border-gray-700 p-4 space-y-3"> <div v-if="isScanning || hasScanned" class="card card-body space-y-3">
<div class="flex items-center justify-between text-sm"> <div class="flex items-center justify-between text-sm">
<span class="text-white"> <span class="text-white">
<template v-if="isScanning"> <template v-if="isScanning">
@@ -71,13 +67,13 @@
</div> </div>
<!-- Chargement initial --> <!-- Chargement initial -->
<div v-if="isLoading" class="bg-gray-900 rounded-lg p-12 text-center"> <div v-if="isLoading" class="card empty-state">
<div class="inline-block animate-spin rounded-full h-8 w-8 border-2 border-gray-600 border-t-blue-400"></div> <div class="spinner h-8 w-8"></div>
<p class="text-gray-400 mt-4">Chargement des documents...</p> <p class="mt-4">Chargement des documents...</p>
</div> </div>
<!-- Invitation initiale --> <!-- Invitation initiale -->
<div v-else-if="!hasScanned && !isScanning" class="bg-gray-900 rounded-lg border border-gray-700 p-8 text-center"> <div v-else-if="!hasScanned && !isScanning" class="card empty-state">
<p class="text-gray-300"> <p class="text-gray-300">
{{ documents.length }} document{{ documents.length > 1 ? 's' : '' }} en base, {{ documents.length }} document{{ documents.length > 1 ? 's' : '' }} en base,
dont {{ documents.filter((d) => d.has_pdf).length }} avec PDF stocké. dont {{ documents.filter((d) => d.has_pdf).length }} avec PDF stocké.
@@ -89,8 +85,8 @@
</div> </div>
<!-- Documents modifiés --> <!-- Documents modifiés -->
<div v-if="changedItems.length" class="bg-gray-900 rounded-lg border border-gray-700 overflow-hidden"> <div v-if="changedItems.length" class="card">
<div class="flex items-center justify-between px-4 py-3 bg-gray-800 border-b border-gray-700"> <div class="card-header bg-gray-800">
<label class="flex items-center gap-2 text-sm text-white cursor-pointer"> <label class="flex items-center gap-2 text-sm text-white cursor-pointer">
<input <input
type="checkbox" type="checkbox"
@@ -126,18 +122,18 @@
<!-- Résumé des différences --> <!-- Résumé des différences -->
<div class="flex items-center gap-1.5 flex-shrink-0"> <div class="flex items-center gap-1.5 flex-shrink-0">
<span v-if="item.diff.summary.metadata" class="px-2 py-0.5 text-xs rounded bg-purple-500/20 text-purple-300"> <span v-if="item.diff.summary.metadata" class="badge badge-neutral">
{{ item.diff.summary.metadata }} métadonnée{{ item.diff.summary.metadata > 1 ? 's' : '' }} {{ item.diff.summary.metadata }} métadonnée{{ item.diff.summary.metadata > 1 ? 's' : '' }}
</span> </span>
<span v-if="item.diff.summary.locataires" class="px-2 py-0.5 text-xs rounded bg-blue-500/20 text-blue-300"> <span v-if="item.diff.summary.locataires" class="badge badge-info">
{{ item.diff.summary.locataires }} locataire{{ item.diff.summary.locataires > 1 ? 's' : '' }} {{ item.diff.summary.locataires }} locataire{{ item.diff.summary.locataires > 1 ? 's' : '' }}
</span> </span>
<span v-if="item.diff.summary.operations" class="px-2 py-0.5 text-xs rounded bg-amber-500/20 text-amber-300"> <span v-if="item.diff.summary.operations" class="badge badge-warning">
{{ item.diff.summary.operations }} opération{{ item.diff.summary.operations > 1 ? 's' : '' }} {{ item.diff.summary.operations }} opération{{ item.diff.summary.operations > 1 ? 's' : '' }}
</span> </span>
<span <span
v-if="item.counts.accepted < item.counts.total" v-if="item.counts.accepted < item.counts.total"
class="px-2 py-0.5 text-xs rounded bg-orange-500/20 text-orange-300" class="badge badge-warning"
title="Une partie des modifications a été écartée" title="Une partie des modifications a été écartée"
> >
{{ item.counts.accepted }}/{{ item.counts.total }} retenues {{ item.counts.accepted }}/{{ item.counts.total }} retenues
@@ -153,22 +149,19 @@
<!-- Actions --> <!-- Actions -->
<div class="flex items-center gap-2 flex-shrink-0"> <div class="flex items-center gap-2 flex-shrink-0">
<span v-if="item.status === 'applique'" class="px-2 py-1 text-xs rounded bg-green-500/20 text-green-400"> <span v-if="item.status === 'applique'" class="badge badge-success">
Appliqué Appliqué
</span> </span>
<span v-else-if="item.status === 'echec'" class="px-2 py-1 text-xs rounded bg-red-500/20 text-red-400" :title="item.error"> <span v-else-if="item.status === 'echec'" class="badge badge-danger" :title="item.error">
Échec Échec
</span> </span>
<button <button @click="toggleDetail(item.id)" class="btn btn-sm btn-secondary">
@click="toggleDetail(item.id)"
class="px-3 py-1.5 text-xs rounded bg-gray-700 text-gray-300 hover:bg-gray-600 transition-colors"
>
{{ openDetails[item.id] ? 'Masquer' : 'Détail' }} {{ openDetails[item.id] ? 'Masquer' : 'Détail' }}
</button> </button>
<a <a
:href="`/api/documents/${item.id}/pdf`" :href="`/api/documents/${item.id}/pdf`"
target="_blank" target="_blank"
class="px-2 py-1.5 text-xs rounded bg-red-500/20 text-red-400 hover:bg-red-500/30 transition-colors" class="btn btn-sm btn-ghost"
title="Ouvrir le PDF" title="Ouvrir le PDF"
> >
PDF PDF
@@ -177,23 +170,17 @@
</div> </div>
<!-- Détail : différences (cochables) + tags à confirmer --> <!-- Détail : différences (cochables) + tags à confirmer -->
<div v-if="openDetails[item.id]" class="bg-amber-50 border-t border-amber-300 px-4 py-3 space-y-4"> <div v-if="openDetails[item.id]" class="bg-gray-950 border-t border-gray-700 px-4 py-3 space-y-4">
<div class="flex items-center justify-between text-xs"> <div class="flex items-center justify-between text-xs">
<span class="text-amber-800"> <span class="text-gray-400">
{{ item.counts.accepted }} modification{{ item.counts.accepted > 1 ? 's' : '' }} {{ item.counts.accepted }} modification{{ item.counts.accepted > 1 ? 's' : '' }}
retenue{{ item.counts.accepted > 1 ? 's' : '' }} sur {{ item.counts.total }} retenue{{ item.counts.accepted > 1 ? 's' : '' }} sur {{ item.counts.total }}
</span> </span>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<button <button @click="setAllChanges(item, true)" class="btn btn-sm btn-ghost">
@click="setAllChanges(item, true)"
class="px-2 py-1 rounded text-amber-700 hover:bg-amber-100 transition-colors"
>
Tout retenir Tout retenir
</button> </button>
<button <button @click="setAllChanges(item, false)" class="btn btn-sm btn-ghost">
@click="setAllChanges(item, false)"
class="px-2 py-1 rounded text-amber-700 hover:bg-amber-100 transition-colors"
>
Tout écarter Tout écarter
</button> </button>
</div> </div>
@@ -207,17 +194,17 @@
/> />
<div v-if="item.tagsToReview.length"> <div v-if="item.tagsToReview.length">
<div class="font-semibold text-amber-800 text-xs mb-2"> <div class="font-semibold text-amber-300 text-xs mb-2">
Tags à confirmer ({{ item.tagStats.reported }} reporté{{ item.tagStats.reported > 1 ? 's' : '' }} automatiquement) Tags à confirmer ({{ item.tagStats.reported }} reporté{{ item.tagStats.reported > 1 ? 's' : '' }} automatiquement)
</div> </div>
<div class="space-y-2"> <div class="space-y-2">
<div <div
v-for="opIndex in item.tagsToReview" v-for="opIndex in item.tagsToReview"
:key="opIndex" :key="opIndex"
class="flex items-center gap-3 bg-white/70 rounded p-2" class="flex items-center gap-3 subcard p-2"
> >
<div class="flex-1 min-w-0 text-xs"> <div class="flex-1 min-w-0 text-xs">
<div class="font-medium text-gray-800 truncate"> <div class="font-medium text-gray-200 truncate">
{{ item.newData.recapitulatif_operations[opIndex].fournisseur || 'Fournisseur inconnu' }} {{ item.newData.recapitulatif_operations[opIndex].fournisseur || 'Fournisseur inconnu' }}
</div> </div>
<div class="text-gray-500 truncate"> <div class="text-gray-500 truncate">
@@ -225,7 +212,7 @@
|| item.newData.recapitulatif_operations[opIndex].description || '-' }} || item.newData.recapitulatif_operations[opIndex].description || '-' }}
· {{ formatAmount(item.newData.recapitulatif_operations[opIndex].montants?.debit) }} · {{ formatAmount(item.newData.recapitulatif_operations[opIndex].montants?.debit) }}
</div> </div>
<div v-if="item.tags[opIndex]?.origin === 'predit'" class="text-blue-600 mt-0.5"> <div v-if="item.tags[opIndex]?.origin === 'predit'" class="text-blue-400 mt-0.5">
Prédit ({{ item.tags[opIndex].confidence }}%) {{ item.tags[opIndex].reason }} Prédit ({{ item.tags[opIndex].confidence }}%) {{ item.tags[opIndex].reason }}
</div> </div>
</div> </div>
@@ -241,7 +228,7 @@
</div> </div>
</div> </div>
</div> </div>
<p v-else class="text-xs text-amber-800"> <p v-else class="text-xs text-gray-400">
Les {{ item.tagStats.reported }} tag{{ item.tagStats.reported > 1 ? 's' : '' }} du document Les {{ item.tagStats.reported }} tag{{ item.tagStats.reported > 1 ? 's' : '' }} du document
{{ item.tagStats.reported > 1 ? 'sont reportés' : 'est reporté' }} sur la nouvelle extraction. {{ item.tagStats.reported > 1 ? 'sont reportés' : 'est reporté' }} sur la nouvelle extraction.
</p> </p>
@@ -251,9 +238,9 @@
</div> </div>
<!-- Documents en erreur --> <!-- Documents en erreur -->
<div v-if="problemItems.length" class="bg-gray-900 rounded-lg border border-gray-700 overflow-hidden"> <div v-if="problemItems.length" class="card">
<div class="px-4 py-3 bg-gray-800 border-b border-gray-700 text-sm text-white"> <div class="card-header bg-gray-800">
Non -extraits ({{ problemItems.length }}) <span class="card-title">Non -extraits ({{ problemItems.length }})</span>
</div> </div>
<div class="divide-y divide-gray-800"> <div class="divide-y divide-gray-800">
<div v-for="item in problemItems" :key="item.id" class="flex items-center gap-3 px-4 py-2.5"> <div v-for="item in problemItems" :key="item.id" class="flex items-center gap-3 px-4 py-2.5">
@@ -269,10 +256,10 @@
</div> </div>
<!-- Documents inchangés --> <!-- Documents inchangés -->
<div v-if="identicalItems.length" class="bg-gray-900 rounded-lg border border-gray-700 overflow-hidden"> <div v-if="identicalItems.length" class="card">
<button <button
@click="showIdentical = !showIdentical" @click="showIdentical = !showIdentical"
class="w-full flex items-center justify-between px-4 py-3 bg-gray-800 text-sm text-gray-300 hover:text-white transition-colors" class="card-header w-full bg-gray-800 text-sm text-gray-300 hover:text-white transition-colors"
> >
<span>{{ identicalItems.length }} document{{ identicalItems.length > 1 ? 's' : '' }} inchangé{{ identicalItems.length > 1 ? 's' : '' }}</span> <span>{{ identicalItems.length }} document{{ identicalItems.length > 1 ? 's' : '' }} inchangé{{ identicalItems.length > 1 ? 's' : '' }}</span>
<span class="text-xs">{{ showIdentical ? 'Masquer' : 'Voir' }}</span> <span class="text-xs">{{ showIdentical ? 'Masquer' : 'Voir' }}</span>
@@ -281,7 +268,7 @@
<div v-for="item in identicalItems" :key="item.id" class="flex items-center gap-3 px-4 py-2 text-xs"> <div v-for="item in identicalItems" :key="item.id" class="flex items-center gap-3 px-4 py-2 text-xs">
<span class="font-mono text-gray-300">{{ item.reference }}</span> <span class="font-mono text-gray-300">{{ item.reference }}</span>
<span class="text-gray-500">{{ formatDate(item.date) }}</span> <span class="text-gray-500">{{ formatDate(item.date) }}</span>
<span class="text-gray-600 truncate">{{ item.immeuble_adresse || '-' }}</span> <span class="text-gray-500 truncate">{{ item.immeuble_adresse || '-' }}</span>
</div> </div>
</div> </div>
</div> </div>
@@ -293,7 +280,7 @@
v-if="changedItems.length" v-if="changedItems.length"
class="fixed bottom-0 left-0 right-0 bg-gray-900 border-t border-gray-700 px-6 py-3" class="fixed bottom-0 left-0 right-0 bg-gray-900 border-t border-gray-700 px-6 py-3"
> >
<div class="max-w-6xl mx-auto flex items-center justify-between gap-4"> <div class="flex items-center justify-between gap-4">
<div class="text-sm" :class="applyError && !isApplying ? 'text-red-400' : 'text-gray-400'"> <div class="text-sm" :class="applyError && !isApplying ? 'text-red-400' : 'text-gray-400'">
<template v-if="isApplying"> <template v-if="isApplying">
Enregistrement {{ applyDone }} / {{ applyTotal }}... Enregistrement {{ applyDone }} / {{ applyTotal }}...
@@ -309,7 +296,7 @@
<button <button
@click="applySelection" @click="applySelection"
:disabled="isApplying || isScanning || selectedItems.length === 0" :disabled="isApplying || isScanning || selectedItems.length === 0"
class="px-5 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors font-medium disabled:opacity-50 disabled:cursor-not-allowed" class="btn btn-primary font-medium"
> >
Appliquer la sélection ({{ selectedItems.length }}) Appliquer la sélection ({{ selectedItems.length }})
</button> </button>