refactor(ui): passe les pages en pleine largeur
Chaque page plafonnait sa largeur differemment (max-w-4xl pour Config, 5xl pour IA, 6xl pour l'accueil et les documents, 7xl pour les revenus et les depenses) : sur un ecran large, les donnees se tassaient au centre. Les pages n'ont plus de plafond, le contenu occupe l'ecran avec une marge laterale fixe. Pour tirer parti de la place gagnee : Config affiche ses deux sections cote a cote, et le split apercu PDF / donnees passe de 50-50 a 40-60 en faveur des donnees. Pendant le tagging, le bouton « Annuler » de l'en-tete d'edition est masque : il faisait doublon avec celui du bandeau de tagging, qui a un sens different (retour a l'edition, et non abandon). La sortie vers la liste reste le fil d'Ariane. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex-1 overflow-auto p-6 bg-gray-800">
|
<div class="page">
|
||||||
<div class="max-w-7xl mx-auto space-y-6">
|
<div class="page-content">
|
||||||
|
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="flex items-center justify-between">
|
<div class="page-header">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-xl font-semibold text-white">Analyse des depenses</h1>
|
<h1 class="page-title">Analyse des depenses</h1>
|
||||||
<p class="text-sm text-gray-400 mt-1">
|
<p class="page-subtitle">
|
||||||
Visualisez et filtrez les depenses de vos immeubles
|
Visualisez et filtrez les depenses de vos immeubles
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,114 +1,128 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex-1 overflow-y-auto p-6">
|
<div class="page">
|
||||||
<div class="max-w-4xl mx-auto space-y-6">
|
<div class="page-content">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div>
|
<div class="page-header">
|
||||||
<h1 class="text-2xl font-bold text-white">Configuration</h1>
|
<div>
|
||||||
<p class="text-gray-400 text-sm mt-1">Paramètres LLM et gestion des tags</p>
|
<h1 class="page-title">Configuration</h1>
|
||||||
|
<p class="page-subtitle">Paramètres LLM et gestion des tags</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Section LLM -->
|
<!-- Les deux sections cote a cote des que la largeur le permet -->
|
||||||
<div class="bg-gray-800 rounded-lg border border-gray-700 p-6">
|
<div class="grid grid-cols-1 xl:grid-cols-2 gap-6 items-start">
|
||||||
<h2 class="text-lg font-semibold text-white mb-4">Configuration LLM (Ollama)</h2>
|
|
||||||
|
|
||||||
<div class="space-y-4">
|
<!-- Section LLM -->
|
||||||
<div v-for="field in settingsFields" :key="field.key" class="flex items-end gap-3">
|
<div class="card">
|
||||||
<div class="flex-1">
|
<div class="card-header">
|
||||||
<div class="flex items-center gap-2 mb-1">
|
<h2 class="card-title">Configuration LLM (Ollama)</h2>
|
||||||
<label class="text-sm font-medium text-gray-300">{{ field.label }}</label>
|
</div>
|
||||||
<span
|
<div class="card-body space-y-4">
|
||||||
class="text-xs px-1.5 py-0.5 rounded"
|
<div v-for="field in settingsFields" :key="field.key" class="flex items-end gap-3">
|
||||||
:class="sourceBadgeClass(settingsSources[field.key])"
|
<div class="flex-1">
|
||||||
>{{ settingsSources[field.key] || 'default' }}</span>
|
<div class="flex items-center gap-2 mb-1">
|
||||||
|
<label class="form-label mb-0">{{ field.label }}</label>
|
||||||
|
<span class="badge" :class="sourceBadgeClass(settingsSources[field.key])">
|
||||||
|
{{ settingsSources[field.key] || 'default' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
v-model="settingsForm[field.key]"
|
||||||
|
:type="field.type || 'text'"
|
||||||
|
:placeholder="field.placeholder"
|
||||||
|
class="input"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<button
|
||||||
v-model="settingsForm[field.key]"
|
@click="resetSetting(field.key)"
|
||||||
:type="field.type || 'text'"
|
class="btn btn-secondary"
|
||||||
:placeholder="field.placeholder"
|
title="Reset au défaut"
|
||||||
class="w-full bg-gray-900 border border-gray-700 rounded-lg px-4 py-2 text-white placeholder-gray-500 focus:outline-none focus:border-blue-500 transition-colors"
|
>Reset</button>
|
||||||
/>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center gap-3 pt-2">
|
||||||
|
<button
|
||||||
|
@click="saveSettings"
|
||||||
|
:disabled="savingSettings"
|
||||||
|
class="btn btn-primary"
|
||||||
|
>{{ savingSettings ? 'Enregistrement...' : 'Enregistrer' }}</button>
|
||||||
|
<span v-if="settingsMessage" class="text-sm" :class="settingsMessageError ? 'text-red-400' : 'text-green-400'">{{ settingsMessage }}</span>
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
@click="resetSetting(field.key)"
|
|
||||||
class="px-3 py-2 text-sm text-gray-400 border border-gray-600 rounded-lg hover:text-white hover:border-gray-500 transition-colors"
|
|
||||||
title="Reset au défaut"
|
|
||||||
>Reset</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-4 flex items-center gap-3">
|
<!-- Section Tags -->
|
||||||
<button
|
<div class="card">
|
||||||
@click="saveSettings"
|
<div class="card-header">
|
||||||
:disabled="savingSettings"
|
<h2 class="card-title">Gestion des tags</h2>
|
||||||
class="px-5 py-2 bg-blue-600 text-white rounded-lg font-medium transition-colors disabled:opacity-50 hover:bg-blue-700"
|
<span class="text-xs text-gray-500">{{ tags.length }} tags</span>
|
||||||
>{{ savingSettings ? 'Enregistrement...' : 'Enregistrer' }}</button>
|
</div>
|
||||||
<span v-if="settingsMessage" class="text-sm" :class="settingsMessageError ? 'text-red-400' : 'text-green-400'">{{ settingsMessage }}</span>
|
|
||||||
|
<div class="table-wrap">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="w-16">ID</th>
|
||||||
|
<th>Nom</th>
|
||||||
|
<th class="text-right">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="tag in tags" :key="tag.id">
|
||||||
|
<td class="text-gray-500">{{ tag.id }}</td>
|
||||||
|
<td>
|
||||||
|
<input
|
||||||
|
v-if="editingTagId === tag.id"
|
||||||
|
v-model="editingTagNom"
|
||||||
|
class="input py-1"
|
||||||
|
@keydown.enter="confirmRenameTag(tag.id)"
|
||||||
|
@keydown.escape="cancelEditTag"
|
||||||
|
ref="editInput"
|
||||||
|
/>
|
||||||
|
<span v-else class="text-white">{{ tag.nom }}</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-right whitespace-nowrap">
|
||||||
|
<template v-if="editingTagId === tag.id">
|
||||||
|
<button
|
||||||
|
@click="confirmRenameTag(tag.id)"
|
||||||
|
class="text-green-400 hover:text-green-300 text-sm mr-2"
|
||||||
|
>Valider</button>
|
||||||
|
<button
|
||||||
|
@click="cancelEditTag"
|
||||||
|
class="text-gray-400 hover:text-white text-sm"
|
||||||
|
>Annuler</button>
|
||||||
|
</template>
|
||||||
|
<button
|
||||||
|
v-else
|
||||||
|
@click="startEditTag(tag)"
|
||||||
|
class="text-blue-400 hover:text-blue-300 text-sm"
|
||||||
|
>Renommer</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Ajout d'un nouveau tag -->
|
||||||
|
<div class="card-body border-t border-gray-700">
|
||||||
|
<div class="flex gap-3">
|
||||||
|
<input
|
||||||
|
v-model="newTagNom"
|
||||||
|
type="text"
|
||||||
|
placeholder="Nouveau tag..."
|
||||||
|
class="input"
|
||||||
|
@keydown.enter="createTag"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
@click="createTag"
|
||||||
|
:disabled="!newTagNom.trim()"
|
||||||
|
class="btn btn-primary whitespace-nowrap"
|
||||||
|
>Ajouter</button>
|
||||||
|
</div>
|
||||||
|
<p v-if="tagMessage" class="mt-2 text-sm" :class="tagMessageError ? 'text-red-400' : 'text-green-400'">{{ tagMessage }}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Section Tags -->
|
|
||||||
<div class="bg-gray-800 rounded-lg border border-gray-700 p-6">
|
|
||||||
<h2 class="text-lg font-semibold text-white mb-4">Gestion des tags</h2>
|
|
||||||
|
|
||||||
<table class="w-full text-sm">
|
|
||||||
<thead>
|
|
||||||
<tr class="border-b border-gray-700">
|
|
||||||
<th class="text-left py-2 text-gray-400 font-medium">ID</th>
|
|
||||||
<th class="text-left py-2 text-gray-400 font-medium">Nom</th>
|
|
||||||
<th class="text-right py-2 text-gray-400 font-medium">Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr v-for="tag in tags" :key="tag.id" class="border-b border-gray-700/50">
|
|
||||||
<td class="py-2 text-gray-500">{{ tag.id }}</td>
|
|
||||||
<td class="py-2">
|
|
||||||
<input
|
|
||||||
v-if="editingTagId === tag.id"
|
|
||||||
v-model="editingTagNom"
|
|
||||||
class="bg-gray-900 border border-blue-500 rounded px-2 py-1 text-white text-sm focus:outline-none"
|
|
||||||
@keydown.enter="confirmRenameTag(tag.id)"
|
|
||||||
@keydown.escape="cancelEditTag"
|
|
||||||
ref="editInput"
|
|
||||||
/>
|
|
||||||
<span v-else class="text-white">{{ tag.nom }}</span>
|
|
||||||
</td>
|
|
||||||
<td class="py-2 text-right">
|
|
||||||
<template v-if="editingTagId === tag.id">
|
|
||||||
<button
|
|
||||||
@click="confirmRenameTag(tag.id)"
|
|
||||||
class="text-green-400 hover:text-green-300 text-sm mr-2"
|
|
||||||
>Valider</button>
|
|
||||||
<button
|
|
||||||
@click="cancelEditTag"
|
|
||||||
class="text-gray-400 hover:text-gray-300 text-sm"
|
|
||||||
>Annuler</button>
|
|
||||||
</template>
|
|
||||||
<button
|
|
||||||
v-else
|
|
||||||
@click="startEditTag(tag)"
|
|
||||||
class="text-blue-400 hover:text-blue-300 text-sm"
|
|
||||||
>Renommer</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<!-- Ajout d'un nouveau tag -->
|
|
||||||
<div class="mt-4 flex gap-3">
|
|
||||||
<input
|
|
||||||
v-model="newTagNom"
|
|
||||||
type="text"
|
|
||||||
placeholder="Nouveau tag..."
|
|
||||||
class="flex-1 bg-gray-900 border border-gray-700 rounded-lg px-4 py-2 text-white placeholder-gray-500 focus:outline-none focus:border-blue-500 transition-colors text-sm"
|
|
||||||
@keydown.enter="createTag"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
@click="createTag"
|
|
||||||
:disabled="!newTagNom.trim()"
|
|
||||||
class="px-4 py-2 bg-green-600 text-white rounded-lg text-sm font-medium transition-colors disabled:opacity-50 hover:bg-green-700"
|
|
||||||
>Ajouter</button>
|
|
||||||
</div>
|
|
||||||
<p v-if="tagMessage" class="mt-2 text-sm" :class="tagMessageError ? 'text-red-400' : 'text-green-400'">{{ tagMessage }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -187,9 +201,9 @@ async function resetSetting(key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function sourceBadgeClass(source) {
|
function sourceBadgeClass(source) {
|
||||||
if (source === 'database') return 'bg-blue-600/30 text-blue-400'
|
if (source === 'database') return 'badge-info'
|
||||||
if (source === 'env') return 'bg-yellow-600/30 text-yellow-400'
|
if (source === 'env') return 'badge-warning'
|
||||||
return 'bg-gray-600/30 text-gray-400'
|
return 'badge-neutral'
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex-1 overflow-auto p-6 bg-gray-800">
|
<div class="page">
|
||||||
<div class="max-w-6xl mx-auto space-y-6">
|
<div class="page-content">
|
||||||
|
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="flex items-center justify-between">
|
<div class="page-header">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-xl font-semibold text-white">Documents importes</h1>
|
<h1 class="page-title">Documents importes</h1>
|
||||||
<p class="text-sm text-gray-400 mt-1">
|
<p class="page-subtitle">
|
||||||
{{ documents.length }} document{{ documents.length > 1 ? 's' : '' }} en base
|
{{ documents.length }} document{{ documents.length > 1 ? 's' : '' }} en base
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Import button -->
|
<!-- Import button -->
|
||||||
<label class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors cursor-pointer flex items-center gap-2">
|
<label class="btn btn-primary cursor-pointer">
|
||||||
<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" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" />
|
||||||
</svg>
|
</svg>
|
||||||
Importer un PDF
|
Importer un PDF
|
||||||
@@ -27,13 +27,13 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Loading state -->
|
<!-- Loading state -->
|
||||||
<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="text-gray-400 mt-4">Chargement des documents...</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Empty state -->
|
<!-- Empty state -->
|
||||||
<div v-else-if="documents.length === 0" class="bg-gray-900 rounded-lg p-12 text-center">
|
<div v-else-if="documents.length === 0" class="card empty-state">
|
||||||
<svg class="mx-auto h-16 w-16 text-gray-600 mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg class="mx-auto h-16 w-16 text-gray-600 mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
@@ -42,26 +42,26 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Documents table -->
|
<!-- Documents table -->
|
||||||
<div v-else class="bg-gray-900 rounded-lg overflow-hidden border border-gray-700">
|
<div v-else class="card">
|
||||||
<table class="w-full">
|
<div class="table-wrap">
|
||||||
<thead class="bg-gray-800 border-b border-gray-700">
|
<table class="table">
|
||||||
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Document</th>
|
<th>Document</th>
|
||||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Immeuble</th>
|
<th>Immeuble</th>
|
||||||
<th class="px-4 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider">Date</th>
|
<th>Date</th>
|
||||||
<th class="px-4 py-3 text-right text-xs font-medium text-gray-400 uppercase tracking-wider">Solde</th>
|
<th class="text-right">Solde</th>
|
||||||
<th class="px-4 py-3 text-center text-xs font-medium text-gray-400 uppercase tracking-wider">Fichiers</th>
|
<th class="text-center">Fichiers</th>
|
||||||
<th class="px-4 py-3 text-right text-xs font-medium text-gray-400 uppercase tracking-wider">Actions</th>
|
<th class="text-right">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="divide-y divide-gray-800">
|
<tbody>
|
||||||
<tr
|
<tr
|
||||||
v-for="doc in documents"
|
v-for="doc in documents"
|
||||||
:key="doc.id"
|
:key="doc.id"
|
||||||
class="hover:bg-gray-800/50 transition-colors"
|
|
||||||
>
|
>
|
||||||
<!-- Document info -->
|
<!-- Document info -->
|
||||||
<td class="px-4 py-4">
|
<td>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<div class="flex-shrink-0 w-10 h-10 bg-red-500/20 rounded-lg flex items-center justify-center">
|
<div class="flex-shrink-0 w-10 h-10 bg-red-500/20 rounded-lg flex items-center justify-center">
|
||||||
<svg class="w-5 h-5 text-red-400" fill="currentColor" viewBox="0 0 24 24">
|
<svg class="w-5 h-5 text-red-400" fill="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -77,19 +77,19 @@
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<!-- Immeuble -->
|
<!-- Immeuble -->
|
||||||
<td class="px-4 py-4">
|
<td>
|
||||||
<div class="text-sm text-white">{{ doc.immeuble_adresse || '-' }}</div>
|
<div class="text-sm text-white">{{ doc.immeuble_adresse || '-' }}</div>
|
||||||
<div class="text-xs text-gray-500 font-mono">{{ doc.immeuble_code }}</div>
|
<div class="text-xs text-gray-500 font-mono">{{ doc.immeuble_code }}</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<!-- Date -->
|
<!-- Date -->
|
||||||
<td class="px-4 py-4">
|
<td>
|
||||||
<div class="text-sm text-white">{{ formatDate(doc.date) }}</div>
|
<div class="text-sm text-white">{{ formatDate(doc.date) }}</div>
|
||||||
<div class="text-xs text-gray-500">Importe le {{ formatDateTime(doc.created_at) }}</div>
|
<div class="text-xs text-gray-500">Importe le {{ formatDateTime(doc.created_at) }}</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<!-- Solde -->
|
<!-- Solde -->
|
||||||
<td class="px-4 py-4 text-right">
|
<td class="text-right">
|
||||||
<span
|
<span
|
||||||
:class="[
|
:class="[
|
||||||
'text-sm font-medium',
|
'text-sm font-medium',
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<!-- Files status -->
|
<!-- Files status -->
|
||||||
<td class="px-4 py-4">
|
<td>
|
||||||
<div class="flex items-center justify-center gap-2">
|
<div class="flex items-center justify-center gap-2">
|
||||||
<!-- PDF download -->
|
<!-- PDF download -->
|
||||||
<a
|
<a
|
||||||
@@ -143,12 +143,12 @@
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<!-- Actions -->
|
<!-- Actions -->
|
||||||
<td class="px-4 py-4 text-right">
|
<td class="text-right">
|
||||||
<div class="flex items-center justify-end gap-2">
|
<div class="flex items-center justify-end gap-2">
|
||||||
<!-- Edit & reimport button -->
|
<!-- Edit & reimport button -->
|
||||||
<button
|
<button
|
||||||
@click="router.push(`/documents/${doc.id}/edit`)"
|
@click="router.push(`/documents/${doc.id}/edit`)"
|
||||||
class="px-3 py-1.5 text-xs rounded bg-blue-600/20 text-blue-400 hover:bg-blue-600 hover:text-white transition-colors flex items-center gap-1"
|
class="btn btn-sm btn-secondary"
|
||||||
title="Editer et reimporter"
|
title="Editer et reimporter"
|
||||||
>
|
>
|
||||||
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -161,6 +161,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,27 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex-1 flex flex-col overflow-hidden">
|
<div class="page-fixed">
|
||||||
<!-- Header avec breadcrumb et actions -->
|
<!-- Header avec breadcrumb et actions -->
|
||||||
<div class="flex-shrink-0 px-6 py-4 bg-gray-800 border-b border-gray-700">
|
<div class="flex-shrink-0 px-6 py-4 bg-gray-900 border-b border-gray-700">
|
||||||
<div class="flex items-center justify-between">
|
<div class="page-header">
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center gap-2 text-sm text-gray-400 mb-1">
|
<div class="flex items-center gap-2 text-sm text-gray-400 mb-1">
|
||||||
<router-link to="/documents" class="hover:text-white transition-colors">Documents</router-link>
|
<router-link to="/documents" class="hover:text-white transition-colors">Documents</router-link>
|
||||||
<span>›</span>
|
<span>›</span>
|
||||||
<span class="text-white">{{ documentData?.reference || 'Chargement...' }}</span>
|
<span class="text-white">{{ documentData?.reference || 'Chargement...' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<h1 class="text-xl font-semibold text-white">
|
<h1 class="page-title">
|
||||||
Édition du document {{ documentData?.reference }}
|
Édition du document {{ documentData?.reference }}
|
||||||
</h1>
|
</h1>
|
||||||
<p v-if="documentData" class="text-sm text-gray-400 mt-1">
|
<p v-if="documentData" class="page-subtitle">
|
||||||
{{ documentData.immeuble?.adresse || '-' }} - {{ formatDate(documentData.date) }}
|
{{ documentData.immeuble?.adresse || '-' }} - {{ formatDate(documentData.date) }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
|
<!-- Masque pendant le tagging : celui du bandeau de tagging fait foi. -->
|
||||||
<button
|
<button
|
||||||
|
v-if="!showTagging"
|
||||||
@click="cancel"
|
@click="cancel"
|
||||||
:disabled="isSaving"
|
:disabled="isSaving"
|
||||||
class="px-4 py-2 text-sm text-gray-400 hover:text-white transition-colors disabled:opacity-50"
|
class="btn btn-ghost"
|
||||||
>
|
>
|
||||||
Annuler
|
Annuler
|
||||||
</button>
|
</button>
|
||||||
@@ -30,7 +32,7 @@
|
|||||||
@click="reExtract"
|
@click="reExtract"
|
||||||
:disabled="isSaving || isReExtracting || !documentData?.has_pdf"
|
:disabled="isSaving || isReExtracting || !documentData?.has_pdf"
|
||||||
:title="documentData?.has_pdf ? 'Relancer l\'extraction depuis le PDF stocké' : 'PDF non disponible pour ce document'"
|
:title="documentData?.has_pdf ? 'Relancer l\'extraction depuis le PDF stocké' : 'PDF non disponible pour ce document'"
|
||||||
class="px-4 py-2 text-sm bg-amber-600 text-white rounded-lg hover:bg-amber-700 transition-colors disabled:opacity-50 flex items-center gap-2"
|
class="btn btn-warning"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class="w-4 h-4"
|
class="w-4 h-4"
|
||||||
@@ -46,7 +48,7 @@
|
|||||||
v-if="!showTagging && extractedData"
|
v-if="!showTagging && extractedData"
|
||||||
@click="goToTagging"
|
@click="goToTagging"
|
||||||
:disabled="isSaving"
|
:disabled="isSaving"
|
||||||
class="px-4 py-2 text-sm 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-4 h-4" 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" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
|
||||||
@@ -58,9 +60,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Loading state -->
|
<!-- Loading state -->
|
||||||
<div v-if="isLoading" class="flex-1 flex items-center justify-center bg-gray-800">
|
<div v-if="isLoading" class="flex-1 flex items-center justify-center bg-gray-950">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="inline-block animate-spin rounded-full h-12 w-12 border-4 border-gray-600 border-t-blue-400 mb-4"></div>
|
<div class="spinner h-12 w-12 mb-4"></div>
|
||||||
<p class="text-gray-400">Chargement du document...</p>
|
<p class="text-gray-400">Chargement du document...</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -68,9 +70,9 @@
|
|||||||
<!-- Corps : Split view PDF + Données -->
|
<!-- Corps : Split view PDF + Données -->
|
||||||
<div v-else class="flex-1 flex overflow-hidden">
|
<div v-else class="flex-1 flex overflow-hidden">
|
||||||
<!-- Gauche : PDF Preview -->
|
<!-- Gauche : PDF Preview -->
|
||||||
<div class="w-1/2 border-r border-gray-700 flex flex-col">
|
<div class="w-2/5 border-r border-gray-700 flex flex-col">
|
||||||
<div class="flex-shrink-0 px-4 py-2 bg-gray-700/50 border-b border-gray-700">
|
<div class="flex-shrink-0 px-4 py-2 bg-gray-900 border-b border-gray-700">
|
||||||
<span class="text-sm text-gray-300">Aperçu PDF</span>
|
<span class="card-title">Aperçu PDF</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 overflow-hidden">
|
<div class="flex-1 overflow-hidden">
|
||||||
<PdfPreview
|
<PdfPreview
|
||||||
@@ -79,7 +81,7 @@
|
|||||||
:file-name="documentData?.source_file || 'document.pdf'"
|
:file-name="documentData?.source_file || 'document.pdf'"
|
||||||
class="h-full"
|
class="h-full"
|
||||||
/>
|
/>
|
||||||
<div v-else class="h-full flex items-center justify-center bg-gray-800 text-gray-500">
|
<div v-else class="h-full flex items-center justify-center bg-gray-950 text-gray-500">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<svg class="mx-auto h-16 w-16 text-gray-600 mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg class="mx-auto h-16 w-16 text-gray-600 mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||||
@@ -92,9 +94,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Droite : Édition ou Tagging -->
|
<!-- Droite : Édition ou Tagging -->
|
||||||
<div class="w-1/2 flex flex-col bg-gray-50">
|
<div class="w-3/5 flex flex-col bg-gray-950">
|
||||||
<!-- Bandeau erreur de re-extraction -->
|
<!-- Bandeau erreur de re-extraction -->
|
||||||
<div v-if="reExtractError" class="flex-shrink-0 px-4 py-2 bg-red-500/10 border-b border-red-500/30 text-sm text-red-600">
|
<div v-if="reExtractError" class="flex-shrink-0 px-4 py-2 bg-red-500/10 border-b border-red-500/30 text-sm text-red-400">
|
||||||
{{ reExtractError }}
|
{{ reExtractError }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex-1 flex overflow-hidden">
|
<div class="flex-1 flex overflow-hidden">
|
||||||
<!-- Left panel: Upload or PDF Preview -->
|
<!-- Left panel: Upload or PDF Preview -->
|
||||||
<div class="w-1/2 flex flex-col border-r border-gray-700">
|
<div class="w-2/5 flex flex-col border-r border-gray-700">
|
||||||
<!-- Upload zone when no file -->
|
<!-- Upload zone when no file -->
|
||||||
<div v-if="!pdfFile" class="flex-1 flex items-center justify-center p-8 bg-gray-800">
|
<div v-if="!pdfFile" class="flex-1 flex items-center justify-center p-8 bg-gray-950">
|
||||||
<div class="max-w-md w-full">
|
<div class="max-w-md w-full">
|
||||||
<div
|
<div
|
||||||
class="border-2 border-dashed border-gray-600 rounded-lg p-8 text-center hover:border-blue-500 transition-colors cursor-pointer bg-gray-900/50"
|
class="border-2 border-dashed border-gray-700 rounded-lg p-8 text-center hover:border-blue-500 transition-colors cursor-pointer bg-gray-900/50"
|
||||||
@click="triggerFileInput"
|
@click="triggerFileInput"
|
||||||
@dragover.prevent="isDragging = true"
|
@dragover.prevent="isDragging = true"
|
||||||
@dragleave.prevent="isDragging = false"
|
@dragleave.prevent="isDragging = false"
|
||||||
@@ -39,22 +39,22 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Right panel: JSON Viewer or Tagging Step -->
|
<!-- Right panel: JSON Viewer or Tagging Step -->
|
||||||
<div class="w-1/2 flex flex-col bg-gray-50">
|
<div class="w-3/5 flex flex-col bg-gray-950">
|
||||||
<!-- Extract button bar -->
|
<!-- Extract button bar -->
|
||||||
<div v-if="pdfFile && !extractedData && !isExtracting && !showTagging" class="flex-shrink-0 p-4 bg-white border-b border-gray-200">
|
<div v-if="pdfFile && !extractedData && !isExtracting && !showTagging" class="flex-shrink-0 p-4 bg-gray-900 border-b border-gray-700">
|
||||||
<button
|
<button
|
||||||
@click="extractCurrentFile"
|
@click="extractCurrentFile"
|
||||||
class="w-full px-4 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-medium"
|
class="btn btn-primary w-full py-2.5"
|
||||||
>
|
>
|
||||||
Extraire les donnees du PDF
|
Extraire les donnees du PDF
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Continue to tagging button (when data is extracted) -->
|
<!-- Continue to tagging button (when data is extracted) -->
|
||||||
<div v-if="extractedData && !isExtracting && !showTagging" class="flex-shrink-0 p-4 bg-white border-b border-gray-200">
|
<div v-if="extractedData && !isExtracting && !showTagging" class="flex-shrink-0 p-4 bg-gray-900 border-b border-gray-700">
|
||||||
<!-- Duplicate warning -->
|
<!-- Duplicate warning -->
|
||||||
<div v-if="saveMessage && saveMessage.type === 'duplicate'" class="mb-3">
|
<div v-if="saveMessage && saveMessage.type === 'duplicate'" class="mb-3">
|
||||||
<div class="px-4 py-3 rounded-lg text-sm bg-yellow-100 text-yellow-800 border border-yellow-200">
|
<div class="px-4 py-3 rounded-lg text-sm bg-amber-500/10 text-amber-400 border border-amber-500/30">
|
||||||
<div class="font-medium flex items-center gap-2">
|
<div class="font-medium flex items-center gap-2">
|
||||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
@click="goToTagging"
|
@click="goToTagging"
|
||||||
class="w-full px-4 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-medium"
|
class="btn btn-primary w-full py-2.5"
|
||||||
>
|
>
|
||||||
Continuer vers le tagging
|
Continuer vers le tagging
|
||||||
</button>
|
</button>
|
||||||
@@ -84,13 +84,13 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Save feedback message -->
|
<!-- Save feedback message -->
|
||||||
<div v-if="saveMessage && showTagging" class="flex-shrink-0 p-4 bg-white border-t border-gray-200">
|
<div v-if="saveMessage && showTagging" class="flex-shrink-0 p-4 bg-gray-900 border-t border-gray-700">
|
||||||
<div
|
<div
|
||||||
:class="[
|
:class="[
|
||||||
'px-4 py-3 rounded-lg text-sm',
|
'px-4 py-3 rounded-lg text-sm border',
|
||||||
saveMessage.type === 'success' ? 'bg-green-100 text-green-800 border border-green-200' : '',
|
saveMessage.type === 'success' ? 'bg-green-500/10 text-green-400 border-green-500/30' : '',
|
||||||
saveMessage.type === 'duplicate' ? 'bg-yellow-100 text-yellow-800 border border-yellow-200' : '',
|
saveMessage.type === 'duplicate' ? 'bg-amber-500/10 text-amber-400 border-amber-500/30' : '',
|
||||||
saveMessage.type === 'error' ? 'bg-red-100 text-red-800 border border-red-200' : ''
|
saveMessage.type === 'error' ? 'bg-red-500/10 text-red-400 border-red-500/30' : ''
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<div class="font-medium">{{ saveMessage.title }}</div>
|
<div class="font-medium">{{ saveMessage.title }}</div>
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Placeholder when no file -->
|
<!-- Placeholder when no file -->
|
||||||
<div v-if="!pdfFile && !showTagging" class="flex-1 flex items-center justify-center text-gray-400">
|
<div v-if="!pdfFile && !showTagging" class="flex-1 flex items-center justify-center text-gray-500">
|
||||||
<p>Selectionnez un PDF pour commencer</p>
|
<p>Selectionnez un PDF pour commencer</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex-1 overflow-auto p-6 bg-gray-800">
|
<div class="page">
|
||||||
<div class="max-w-6xl mx-auto space-y-6">
|
<div class="page-content">
|
||||||
|
|
||||||
<!-- Zone d'upload PDF visible -->
|
<!-- Zone d'upload PDF visible -->
|
||||||
<div
|
<div
|
||||||
class="border-2 border-dashed border-gray-600 rounded-lg p-6 text-center hover:border-blue-500 transition-colors cursor-pointer bg-gray-900/50"
|
class="border-2 border-dashed border-gray-700 rounded-lg p-6 text-center hover:border-blue-500 transition-colors cursor-pointer bg-gray-900/50"
|
||||||
@click="triggerFileInput"
|
@click="triggerFileInput"
|
||||||
@dragover.prevent="isDragging = true"
|
@dragover.prevent="isDragging = true"
|
||||||
@dragleave.prevent="isDragging = false"
|
@dragleave.prevent="isDragging = false"
|
||||||
@@ -54,14 +54,14 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Derniers documents -->
|
<!-- Derniers documents -->
|
||||||
<div id="recent-documents" class="bg-gray-900 rounded-lg overflow-hidden border border-gray-700">
|
<div id="recent-documents" class="card">
|
||||||
<div class="px-4 py-3 border-b border-gray-700 flex items-center justify-between">
|
<div class="card-header">
|
||||||
<h2 class="text-sm font-medium text-gray-300">Derniers documents importes</h2>
|
<h2 class="card-title">Derniers documents importes</h2>
|
||||||
<span class="text-xs text-gray-500">{{ documents.length }} documents</span>
|
<span class="text-xs text-gray-500">{{ documents.length }} documents</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="isLoadingDocs" class="p-8 text-center text-gray-500">
|
<div v-if="isLoadingDocs" class="p-8 text-center text-gray-500">
|
||||||
<div class="inline-block animate-spin rounded-full h-6 w-6 border-2 border-gray-600 border-t-blue-400"></div>
|
<div class="spinner h-6 w-6"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="documents.length === 0" class="p-8 text-center text-gray-500">
|
<div v-else-if="documents.length === 0" class="p-8 text-center text-gray-500">
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex-1 overflow-y-auto p-6">
|
<div class="page">
|
||||||
<div class="max-w-5xl mx-auto space-y-6">
|
<div class="page-content">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="flex items-center justify-between">
|
<div class="page-header">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-2xl font-bold text-white">Assistant IA</h1>
|
<h1 class="page-title">Assistant IA</h1>
|
||||||
<p class="text-gray-400 text-sm mt-1">Posez des questions sur vos données en langage naturel</p>
|
<p class="page-subtitle">Posez des questions sur vos données en langage naturel</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span
|
<span
|
||||||
@@ -19,19 +19,19 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Formulaire -->
|
<!-- Formulaire -->
|
||||||
<div class="bg-gray-800 rounded-lg border border-gray-700 p-4">
|
<div class="card card-body">
|
||||||
<div class="flex gap-3">
|
<div class="flex gap-3">
|
||||||
<input
|
<input
|
||||||
v-model="question"
|
v-model="question"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Posez votre question..."
|
placeholder="Posez votre question..."
|
||||||
class="flex-1 bg-gray-900 border border-gray-700 rounded-lg px-4 py-2.5 text-white placeholder-gray-500 focus:outline-none focus:border-blue-500 transition-colors"
|
class="input"
|
||||||
@keydown.enter="askQuestion"
|
@keydown.enter="askQuestion"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
/>
|
/>
|
||||||
<select
|
<select
|
||||||
v-model="chartType"
|
v-model="chartType"
|
||||||
class="bg-gray-900 border border-gray-700 rounded-lg px-3 py-2.5 text-white focus:outline-none focus:border-blue-500 transition-colors"
|
class="input w-auto"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
>
|
>
|
||||||
<option value="auto">Auto</option>
|
<option value="auto">Auto</option>
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
<button
|
<button
|
||||||
@click="askQuestion"
|
@click="askQuestion"
|
||||||
:disabled="!question.trim() || loading"
|
:disabled="!question.trim() || loading"
|
||||||
class="px-5 py-2.5 bg-blue-600 text-white rounded-lg font-medium transition-colors disabled:opacity-50 disabled:cursor-not-allowed hover:bg-blue-700"
|
class="btn btn-primary whitespace-nowrap"
|
||||||
>
|
>
|
||||||
{{ loading ? 'Analyse...' : 'Demander' }}
|
{{ loading ? 'Analyse...' : 'Demander' }}
|
||||||
</button>
|
</button>
|
||||||
@@ -51,14 +51,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Exemples de questions -->
|
<!-- Exemples de questions -->
|
||||||
<div v-if="!result && !loading && !error" class="bg-gray-800 rounded-lg border border-gray-700 p-4">
|
<div v-if="!result && !loading && !error" class="card card-body">
|
||||||
<p class="text-gray-400 text-sm mb-3">Exemples de questions :</p>
|
<p class="text-gray-400 text-sm mb-3">Exemples de questions :</p>
|
||||||
<div class="flex flex-wrap gap-2">
|
<div class="flex flex-wrap gap-2">
|
||||||
<button
|
<button
|
||||||
v-for="example in examples"
|
v-for="example in examples"
|
||||||
:key="example"
|
:key="example"
|
||||||
@click="question = example; askQuestion()"
|
@click="question = example; askQuestion()"
|
||||||
class="px-3 py-1.5 text-sm bg-gray-900 border border-gray-700 rounded-lg text-gray-300 hover:border-blue-500 hover:text-white transition-colors"
|
class="btn btn-secondary"
|
||||||
>
|
>
|
||||||
{{ example }}
|
{{ example }}
|
||||||
</button>
|
</button>
|
||||||
@@ -66,8 +66,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Spinner -->
|
<!-- Spinner -->
|
||||||
<div v-if="loading" class="bg-gray-800 rounded-lg border border-gray-700 p-8 text-center">
|
<div v-if="loading" class="card empty-state">
|
||||||
<div class="inline-block w-8 h-8 border-2 border-gray-600 border-t-blue-500 rounded-full animate-spin mb-3"></div>
|
<div class="spinner w-8 h-8 mb-3"></div>
|
||||||
<p class="text-gray-400">{{ loadingMessage }}</p>
|
<p class="text-gray-400">{{ loadingMessage }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -77,23 +77,33 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Résultat -->
|
<!-- Résultat -->
|
||||||
<div v-if="result" class="space-y-4">
|
<div v-if="result" class="space-y-6">
|
||||||
<!-- Réponse texte -->
|
<!-- Réponse texte -->
|
||||||
<div class="bg-gray-800 rounded-lg border border-gray-700 p-4">
|
<div class="card">
|
||||||
<h2 class="text-sm font-medium text-gray-400 mb-2">Réponse</h2>
|
<div class="card-header">
|
||||||
<p class="text-white whitespace-pre-wrap">{{ result.answer }}</p>
|
<h2 class="card-title">Réponse</h2>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p class="text-white whitespace-pre-wrap">{{ result.answer }}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Requête SQL -->
|
<!-- Requête SQL -->
|
||||||
<div v-if="result.sql_query" class="bg-gray-800 rounded-lg border border-gray-700 p-4">
|
<div v-if="result.sql_query" class="card">
|
||||||
<h2 class="text-sm font-medium text-gray-400 mb-2">Requête SQL exécutée</h2>
|
<div class="card-header">
|
||||||
<pre class="text-xs text-gray-300 font-mono bg-gray-900 rounded p-3 overflow-x-auto">{{ result.sql_query }}</pre>
|
<h2 class="card-title">Requête SQL exécutée</h2>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<pre class="text-xs text-gray-300 font-mono bg-gray-950 border border-gray-700 rounded p-3 overflow-x-auto">{{ result.sql_query }}</pre>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Graphique -->
|
<!-- Graphique -->
|
||||||
<div v-if="result.chart_config && chartComponent" class="bg-gray-800 rounded-lg border border-gray-700 p-4">
|
<div v-if="result.chart_config && chartComponent" class="card">
|
||||||
<h2 class="text-sm font-medium text-gray-400 mb-3">Graphique</h2>
|
<div class="card-header">
|
||||||
<div class="bg-gray-900 rounded-lg p-4">
|
<h2 class="card-title">Graphique</h2>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
<component
|
<component
|
||||||
:is="chartComponent"
|
:is="chartComponent"
|
||||||
:data="result.chart_config.data"
|
:data="result.chart_config.data"
|
||||||
@@ -103,34 +113,22 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Tableau de données -->
|
<!-- Tableau de données -->
|
||||||
<div v-if="result.data && result.data.columns && result.data.rows && result.data.rows.length > 0" class="bg-gray-800 rounded-lg border border-gray-700 p-4">
|
<div v-if="result.data && result.data.columns && result.data.rows && result.data.rows.length > 0" class="card">
|
||||||
<h2 class="text-sm font-medium text-gray-400 mb-3">
|
<div class="card-header">
|
||||||
Données ({{ result.data.row_count }} ligne{{ result.data.row_count > 1 ? 's' : '' }})
|
<h2 class="card-title">
|
||||||
</h2>
|
Données ({{ result.data.row_count }} ligne{{ result.data.row_count > 1 ? 's' : '' }})
|
||||||
<div class="overflow-x-auto">
|
</h2>
|
||||||
<table class="w-full text-sm">
|
</div>
|
||||||
|
<div class="table-wrap">
|
||||||
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="border-b border-gray-700">
|
<tr>
|
||||||
<th
|
<th v-for="col in result.data.columns" :key="col">{{ col }}</th>
|
||||||
v-for="col in result.data.columns"
|
|
||||||
:key="col"
|
|
||||||
class="text-left px-3 py-2 text-gray-400 font-medium"
|
|
||||||
>
|
|
||||||
{{ col }}
|
|
||||||
</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr
|
<tr v-for="(row, idx) in result.data.rows" :key="idx">
|
||||||
v-for="(row, idx) in result.data.rows"
|
<td v-for="(cell, cidx) in row" :key="cidx">
|
||||||
:key="idx"
|
|
||||||
class="border-b border-gray-700/50 hover:bg-gray-700/30"
|
|
||||||
>
|
|
||||||
<td
|
|
||||||
v-for="(cell, cidx) in row"
|
|
||||||
:key="cidx"
|
|
||||||
class="px-3 py-2 text-gray-300"
|
|
||||||
>
|
|
||||||
{{ formatCell(cell) }}
|
{{ formatCell(cell) }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex-1 overflow-auto p-6 bg-gray-800">
|
<div class="page">
|
||||||
<div class="max-w-7xl mx-auto space-y-6">
|
<div class="page-content">
|
||||||
|
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="flex items-center justify-between">
|
<div class="page-header">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="text-xl font-semibold text-white">Dashboard Revenus Locatifs</h1>
|
<h1 class="page-title">Dashboard Revenus Locatifs</h1>
|
||||||
<p class="text-sm text-gray-400 mt-1">
|
<p class="page-subtitle">
|
||||||
Suivi et analyse des loyers, reglements et impayes
|
Suivi et analyse des loyers, reglements et impayes
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<button
|
<button
|
||||||
@click="loadData"
|
@click="loadData"
|
||||||
:disabled="isLoading"
|
:disabled="isLoading"
|
||||||
class="px-3 py-1.5 text-sm bg-blue-600 hover:bg-blue-700 text-white rounded transition-colors disabled:opacity-50"
|
class="btn btn-primary"
|
||||||
>
|
>
|
||||||
<span v-if="isLoading">Chargement...</span>
|
<span v-if="isLoading">Chargement...</span>
|
||||||
<span v-else>Actualiser</span>
|
<span v-else>Actualiser</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user