feat: add homepage

This commit is contained in:
2026-01-18 21:11:35 +01:00
parent ee1db93298
commit c5e51a7513
17 changed files with 1550 additions and 115 deletions

View File

@@ -0,0 +1,224 @@
<template>
<div class="flex-1 flex overflow-hidden">
<!-- Left panel: Upload or PDF Preview -->
<div class="w-1/2 flex flex-col border-r border-gray-700">
<!-- Upload zone when no file -->
<div v-if="!pdfFile" class="flex-1 flex items-center justify-center p-8 bg-gray-800">
<div class="max-w-md w-full">
<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"
@click="triggerFileInput"
@dragover.prevent="isDragging = true"
@dragleave.prevent="isDragging = false"
@drop.prevent="onDrop"
:class="{ 'border-blue-500 bg-blue-500/10': isDragging }"
>
<input
ref="fileInput"
type="file"
accept="application/pdf,.pdf"
class="hidden"
@change="onFileChange"
/>
<svg class="mx-auto h-12 w-12 text-gray-400 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
</svg>
<p class="text-gray-400 mb-2">Glisser un PDF ici</p>
<p class="text-sm text-gray-500">ou <span class="text-blue-400">cliquer pour parcourir</span></p>
</div>
</div>
</div>
<!-- PDF Preview when file loaded -->
<PdfPreview
v-else
:file="pdfFile"
:file-name="pdfFile.name"
class="flex-1"
/>
</div>
<!-- Right panel: JSON Viewer -->
<div class="w-1/2 flex flex-col bg-gray-50">
<!-- Extract button bar -->
<div v-if="pdfFile && !extractedData && !isExtracting" class="flex-shrink-0 p-4 bg-white border-b border-gray-200">
<button
@click="extractCurrentFile"
class="w-full px-4 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-medium"
>
Extraire les donnees du PDF
</button>
</div>
<!-- Save button bar (when data is extracted) -->
<div v-if="extractedData && !isExtracting" class="flex-shrink-0 p-4 bg-white border-b border-gray-200">
<div class="flex items-center gap-3">
<button
@click="saveToDatabase"
:disabled="isSaving"
class="flex-1 px-4 py-3 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors font-medium disabled:opacity-50 disabled:cursor-not-allowed"
>
<span v-if="isSaving">Sauvegarde en cours...</span>
<span v-else>Sauvegarder en base</span>
</button>
</div>
<!-- Save feedback message -->
<div v-if="saveMessage" class="mt-3">
<div
:class="[
'px-4 py-3 rounded-lg text-sm',
saveMessage.type === 'success' ? 'bg-green-100 text-green-800 border border-green-200' : '',
saveMessage.type === 'duplicate' ? 'bg-yellow-100 text-yellow-800 border border-yellow-200' : '',
saveMessage.type === 'error' ? 'bg-red-100 text-red-800 border border-red-200' : ''
]"
>
<div class="font-medium">{{ saveMessage.title }}</div>
<div v-if="saveMessage.details" class="mt-1 text-xs opacity-80">{{ saveMessage.details }}</div>
</div>
</div>
</div>
<!-- Placeholder when no file -->
<div v-if="!pdfFile" class="flex-1 flex items-center justify-center text-gray-400">
<p>Selectionnez un PDF pour commencer</p>
</div>
<JsonViewer
v-else
:data="extractedData"
:is-loading="isExtracting"
class="flex-1 overflow-hidden"
/>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { pendingFile } from '../store'
import PdfPreview from '../components/PdfPreview.vue'
import JsonViewer from '../components/JsonViewer.vue'
const router = useRouter()
const fileInput = ref(null)
const isDragging = ref(false)
const pdfFile = ref(null)
const extractedData = ref(null)
const isExtracting = ref(false)
const isSaving = ref(false)
const saveMessage = ref(null)
// Check for pending file from store on mount
onMounted(() => {
if (pendingFile.value) {
pdfFile.value = pendingFile.value
pendingFile.value = null // Clear the store
extractCurrentFile()
}
})
function triggerFileInput() {
fileInput.value?.click()
}
function onFileChange(e) {
const file = e.target.files[0]
if (file && file.name.toLowerCase().endsWith('.pdf')) {
pdfFile.value = file
extractedData.value = null
saveMessage.value = null
extractCurrentFile()
}
e.target.value = ''
}
function onDrop(e) {
isDragging.value = false
const file = e.dataTransfer.files[0]
if (file && file.name.toLowerCase().endsWith('.pdf')) {
pdfFile.value = file
extractedData.value = null
saveMessage.value = null
extractCurrentFile()
}
}
async function extractCurrentFile() {
if (!pdfFile.value) return
isExtracting.value = true
try {
const formData = new FormData()
formData.append('file', pdfFile.value)
const response = await fetch('/api/extract', {
method: 'POST',
body: formData
})
if (!response.ok) {
const errorData = await response.json()
throw new Error(errorData.detail || 'Erreur lors de l\'extraction')
}
const data = await response.json()
extractedData.value = data
} catch (err) {
console.error('Extraction error:', err)
alert('Erreur: ' + err.message)
} finally {
isExtracting.value = false
}
}
async function saveToDatabase() {
if (!extractedData.value) return
isSaving.value = true
saveMessage.value = null
try {
const response = await fetch('/api/save', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
source_file: extractedData.value.source_file,
data: extractedData.value.data
})
})
const result = await response.json()
if (!response.ok) {
throw new Error(result.detail || 'Erreur lors de la sauvegarde')
}
if (result.success) {
// Success - redirect to home
router.push('/')
} else {
// Duplicate detected - stay on current view
saveMessage.value = {
type: 'duplicate',
title: 'Document deja existant',
details: `Reference: ${result.reference} | Date: ${result.date}`
}
}
} catch (err) {
console.error('Save error:', err)
saveMessage.value = {
type: 'error',
title: 'Erreur lors de la sauvegarde',
details: err.message
}
} finally {
isSaving.value = false
}
}
</script>