feat(ui): pose un design system unique pour toute l'interface
Les pages melangeaient deux themes (sombre pour les tableaux de bord, clair pour le flux d'extraction) et repetaient des utilitaires Tailwind divergents : trois styles de boutons, quatre variantes de cartes, des champs de saisie a paddings differents. Definit dans style.css une palette unique (gray-950 fond, gray-900 surfaces, gray-700 bordures, blue-600 accent) et les classes composants associees : page, card, btn, input, table, badge, spinner, empty-state. Le shell s'y aligne et sa navigation est generee depuis une liste. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,68 +1,27 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="h-screen flex flex-col bg-gray-900">
|
<div class="h-screen flex flex-col bg-gray-950">
|
||||||
<!-- Header compact -->
|
<!-- Header compact -->
|
||||||
<header class="flex-shrink-0 bg-gray-800 border-b border-gray-700 px-4 py-2">
|
<header class="flex-shrink-0 bg-gray-900 border-b border-gray-700 px-6 py-2">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between gap-6">
|
||||||
<router-link to="/" class="text-lg font-semibold text-white hover:text-blue-400 transition-colors">
|
<router-link to="/" class="text-lg font-semibold text-white hover:text-blue-400 transition-colors whitespace-nowrap">
|
||||||
Plesna Gerance
|
Plesna Gerance
|
||||||
<span class="text-gray-400 font-normal text-sm ml-2">Extracteur de comptes rendus</span>
|
<span class="text-gray-400 font-normal text-sm ml-2">Extracteur de comptes rendus</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<!-- Navigation -->
|
<!-- Navigation -->
|
||||||
<nav class="flex items-center gap-3">
|
<nav class="flex items-center gap-1">
|
||||||
<router-link
|
<router-link
|
||||||
to="/"
|
v-for="link in navLinks"
|
||||||
class="px-3 py-1.5 text-sm rounded transition-colors"
|
:key="link.to"
|
||||||
:class="$route.path === '/' ? 'bg-gray-700 text-white' : 'text-gray-400 hover:text-white'"
|
:to="link.to"
|
||||||
|
class="px-3 py-1.5 text-sm rounded-lg transition-colors"
|
||||||
|
:class="$route.path === link.to ? 'bg-gray-800 text-white' : 'text-gray-400 hover:text-white hover:bg-gray-800'"
|
||||||
>
|
>
|
||||||
Accueil
|
{{ link.label }}
|
||||||
</router-link>
|
|
||||||
|
|
||||||
<router-link
|
|
||||||
to="/documents"
|
|
||||||
class="px-3 py-1.5 text-sm rounded transition-colors"
|
|
||||||
:class="$route.path === '/documents' ? 'bg-gray-700 text-white' : 'text-gray-400 hover:text-white'"
|
|
||||||
>
|
|
||||||
Documents
|
|
||||||
</router-link>
|
|
||||||
|
|
||||||
<router-link
|
|
||||||
to="/revenus"
|
|
||||||
class="px-3 py-1.5 text-sm rounded transition-colors"
|
|
||||||
:class="$route.path === '/revenus' ? 'bg-gray-700 text-white' : 'text-gray-400 hover:text-white'"
|
|
||||||
>
|
|
||||||
Revenus
|
|
||||||
</router-link>
|
|
||||||
|
|
||||||
<router-link
|
|
||||||
to="/analytics"
|
|
||||||
class="px-3 py-1.5 text-sm rounded transition-colors"
|
|
||||||
:class="$route.path === '/analytics' ? 'bg-gray-700 text-white' : 'text-gray-400 hover:text-white'"
|
|
||||||
>
|
|
||||||
Depenses
|
|
||||||
</router-link>
|
|
||||||
|
|
||||||
<router-link
|
|
||||||
to="/ia"
|
|
||||||
class="px-3 py-1.5 text-sm rounded transition-colors"
|
|
||||||
:class="$route.path === '/ia' ? 'bg-gray-700 text-white' : 'text-gray-400 hover:text-white'"
|
|
||||||
>
|
|
||||||
IA
|
|
||||||
</router-link>
|
|
||||||
|
|
||||||
<router-link
|
|
||||||
to="/config"
|
|
||||||
class="px-3 py-1.5 text-sm rounded transition-colors"
|
|
||||||
:class="$route.path === '/config' ? 'bg-gray-700 text-white' : 'text-gray-400 hover:text-white'"
|
|
||||||
>
|
|
||||||
Config
|
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
||||||
<!-- Import button with file input -->
|
<!-- Import button with file input -->
|
||||||
<label
|
<label class="btn btn-primary ml-2 cursor-pointer">
|
||||||
class="px-3 py-1.5 text-sm rounded transition-colors cursor-pointer bg-blue-600/20 text-blue-400 hover:bg-blue-600 hover:text-white"
|
|
||||||
:class="$route.path === '/extract' ? 'bg-blue-600 text-white' : ''"
|
|
||||||
>
|
|
||||||
Importer un PDF
|
Importer un PDF
|
||||||
<input
|
<input
|
||||||
ref="fileInput"
|
ref="fileInput"
|
||||||
@@ -91,6 +50,15 @@ import { pendingFile } from './store'
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const fileInput = ref(null)
|
const fileInput = ref(null)
|
||||||
|
|
||||||
|
const navLinks = [
|
||||||
|
{ to: '/', label: 'Accueil' },
|
||||||
|
{ to: '/documents', label: 'Documents' },
|
||||||
|
{ to: '/revenus', label: 'Revenus' },
|
||||||
|
{ to: '/analytics', label: 'Depenses' },
|
||||||
|
{ to: '/ia', label: 'IA' },
|
||||||
|
{ to: '/config', label: 'Config' }
|
||||||
|
]
|
||||||
|
|
||||||
function onFileSelect(e) {
|
function onFileSelect(e) {
|
||||||
const file = e.target.files[0]
|
const file = e.target.files[0]
|
||||||
if (file && file.name.toLowerCase().endsWith('.pdf')) {
|
if (file && file.name.toLowerCase().endsWith('.pdf')) {
|
||||||
|
|||||||
@@ -2,22 +2,204 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
/* Custom scrollbar for JSON viewer */
|
/*
|
||||||
|
* Design system : une seule palette sombre pour toute l'application.
|
||||||
|
*
|
||||||
|
* gray-950 fond de page gray-900 surfaces (cartes, header)
|
||||||
|
* gray-800 sous-surfaces gray-700 bordures
|
||||||
|
* white / gray-400 / gray-500 textes principal / secondaire / tertiaire
|
||||||
|
* blue-600 accent
|
||||||
|
*/
|
||||||
|
@layer components {
|
||||||
|
|
||||||
|
/* --- Structure de page ------------------------------------------------ */
|
||||||
|
|
||||||
|
/* Conteneur racine d'une page : pleine largeur, marge laterale fixe. */
|
||||||
|
.page {
|
||||||
|
@apply flex-1 overflow-auto bg-gray-950 px-6 py-6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Page sans defilement propre (split view type edition). */
|
||||||
|
.page-fixed {
|
||||||
|
@apply flex-1 flex flex-col overflow-hidden bg-gray-950;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-content {
|
||||||
|
@apply space-y-6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
@apply flex items-start justify-between gap-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
@apply text-xl font-semibold text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-subtitle {
|
||||||
|
@apply text-sm text-gray-400 mt-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Cartes ----------------------------------------------------------- */
|
||||||
|
|
||||||
|
.card {
|
||||||
|
@apply bg-gray-900 border border-gray-700 rounded-lg overflow-hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
@apply px-4 py-3 border-b border-gray-700 flex items-center justify-between gap-3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
@apply text-sm font-medium text-gray-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
@apply p-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bloc secondaire a l'interieur d'une carte. */
|
||||||
|
.subcard {
|
||||||
|
@apply bg-gray-800 border border-gray-700 rounded-lg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Boutons ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
@apply inline-flex items-center justify-center gap-2 px-3 py-1.5 text-sm rounded-lg
|
||||||
|
transition-colors disabled:opacity-50 disabled:cursor-not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
@apply bg-blue-600 text-white hover:bg-blue-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
@apply bg-gray-800 text-gray-300 border border-gray-700 hover:bg-gray-700 hover:text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-ghost {
|
||||||
|
@apply text-gray-400 hover:text-white hover:bg-gray-800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-warning {
|
||||||
|
@apply bg-amber-600 text-white hover:bg-amber-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger {
|
||||||
|
@apply bg-red-600 text-white hover:bg-red-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Variante compacte pour les actions dans les listes et tableaux. */
|
||||||
|
.btn-sm {
|
||||||
|
@apply px-2 py-1 text-xs gap-1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Formulaires ------------------------------------------------------ */
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
@apply block text-sm font-medium text-gray-300 mb-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* [color-scheme:dark] : les widgets natifs (calendrier, spinners) suivent le theme. */
|
||||||
|
.input {
|
||||||
|
@apply w-full bg-gray-950 border border-gray-700 rounded-lg px-3 py-2 text-sm text-white
|
||||||
|
placeholder-gray-500 focus:outline-none focus:border-blue-500 transition-colors
|
||||||
|
[color-scheme:dark];
|
||||||
|
}
|
||||||
|
|
||||||
|
select.input {
|
||||||
|
@apply cursor-pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-hint {
|
||||||
|
@apply text-xs text-gray-500 mt-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Tableaux --------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* Enveloppe a poser autour d'un <table> pour le defilement horizontal. */
|
||||||
|
.table-wrap {
|
||||||
|
@apply overflow-x-auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
@apply w-full text-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table thead {
|
||||||
|
@apply bg-gray-800 border-b border-gray-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table th {
|
||||||
|
@apply px-4 py-3 text-left text-xs font-medium text-gray-400 uppercase tracking-wider whitespace-nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tbody {
|
||||||
|
@apply divide-y divide-gray-800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table td {
|
||||||
|
@apply px-4 py-3 text-gray-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tbody tr {
|
||||||
|
@apply hover:bg-gray-800/50 transition-colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Badges ----------------------------------------------------------- */
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
@apply inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs font-medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-neutral {
|
||||||
|
@apply bg-gray-700 text-gray-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-info {
|
||||||
|
@apply bg-blue-500/15 text-blue-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-success {
|
||||||
|
@apply bg-green-500/15 text-green-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-warning {
|
||||||
|
@apply bg-amber-500/15 text-amber-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-danger {
|
||||||
|
@apply bg-red-500/15 text-red-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Etats ------------------------------------------------------------ */
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
@apply p-12 text-center text-gray-500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
@apply inline-block animate-spin rounded-full border-2 border-gray-700 border-t-blue-400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Barres de defilement accordees au theme sombre. */
|
||||||
.custom-scrollbar::-webkit-scrollbar {
|
.custom-scrollbar::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
height: 8px;
|
height: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-scrollbar::-webkit-scrollbar-track {
|
.custom-scrollbar::-webkit-scrollbar-track {
|
||||||
background: #f1f5f9;
|
background: #111827; /* gray-900 */
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-scrollbar::-webkit-scrollbar-thumb {
|
.custom-scrollbar::-webkit-scrollbar-thumb {
|
||||||
background: #cbd5e1;
|
background: #374151; /* gray-700 */
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||||
background: #94a3b8;
|
background: #4b5563; /* gray-600 */
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user