feat: completer Settings.tsx - ContactsSection + export default Settings

This commit is contained in:
Claude
2026-05-01 17:26:23 +02:00
parent 1c62d6b325
commit 964c919430
+57 -58
View File
@@ -579,18 +579,11 @@ function ContactsSection() {
} }
} }
return (
<div className="card">
<button onClick={() => setOpen(!open)}
className="flex items-center justify-between w-full p-4">
<div className="flex items-center gap-3">
<div className="w-9 h-9 rounded-xl bg-violet-50 flex items-center justify-center">
<svg className="w-5 h-5 text-violet-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/> <path strokeLinecap="round" strokeLinejoin="round" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg> </svg>
</div> </div>
<div className="text-left"> <div className="text-left">
<p className="font-semibold text-sm text-gray-900">Contacts / Invités</p> <p className="font-semibold text-sm text-gray-900">Contacts</p>
<p className="text-xs text-gray-400">{contacts.length} contact{contacts.length !== 1 ? 's' : ''}</p> <p className="text-xs text-gray-400">{contacts.length} contact{contacts.length !== 1 ? 's' : ''}</p>
</div> </div>
</div> </div>
@@ -602,57 +595,63 @@ function ContactsSection() {
{open && ( {open && (
<div className="border-t border-gray-50"> <div className="border-t border-gray-50">
{/* Bouton import */}
<div className="px-4 pt-3 pb-2 flex gap-2">
<input ref={fileRef} type="file" accept=".csv,.xls,.xlsx"
onChange={handleFileImport} className="hidden" />
<button
onClick={() => fileRef.current?.click()}
disabled={importing}
className="btn-secondary py-2 text-sm flex items-center gap-2">
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" 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>
{importing ? 'Import en cours…' : 'Importer CSV / Excel'}
</button>
<p className="text-xs text-gray-400 self-center">Colonnes : Nom, Société (optionnel)</p>
</div>
{/* Liste des contacts */} {/* Liste des contacts */}
{contacts.length > 0 && ( {contacts.length === 0 && (
<div className="px-4 pb-2 max-h-64 overflow-y-auto space-y-1"> <p className="px-4 py-3 text-sm text-gray-400">Aucun contact enregistré.</p>
{contacts.map(c => (
<div key={c.id} className="flex items-center gap-3 py-1.5 border-b border-gray-50 last:border-0">
<div className="flex-1 min-w-0">
<span className="text-sm font-medium text-gray-900">{c.name}</span>
{c.company && <span className="ml-2 text-xs text-gray-400">{c.company}</span>}
</div>
<button onClick={() => del.mutate(c.id)}
className="p-1 text-gray-300 hover:text-red-400 shrink-0">
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
))}
</div>
)} )}
{contacts.map(c => (
<div key={c.id} className="flex items-center gap-3 px-4 py-2.5 border-b border-gray-50 last:border-0">
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-gray-900 truncate">{c.name}</p>
{c.company && <p className="text-xs text-gray-400 truncate">{c.company}</p>}
</div>
<button onClick={() => del.mutate(c.id)} className="p-1.5 text-gray-300 hover:text-red-500 transition-colors">
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
))}
{/* Ajout manuel */} {/* Formulaire ajout manuel */}
<div className="px-4 pb-4 pt-2 space-y-2 border-t border-gray-50"> <div className="p-4 space-y-2 border-t border-gray-50">
<p className="text-xs font-semibold text-gray-400 uppercase tracking-wide">Ajouter manuellement</p> <p className="text-xs font-semibold text-gray-400 uppercase tracking-wide">Ajouter un contact</p>
<div className="flex gap-2"> <input
<input className="form-input text-sm py-2 flex-1" placeholder="Nom *" className="form-input text-sm"
value={newName} onChange={e => setNewName(e.target.value)} placeholder="Nom *"
onKeyDown={e => { if (e.key === 'Enter' && newName.trim()) { e.preventDefault(); add.mutate({ name: newName.trim(), company: newCompany.trim() }); }}} /> value={newName}
<input className="form-input text-sm py-2 flex-1" placeholder="Société (optionnel)" onChange={e => setNewName(e.target.value)}
value={newCompany} onChange={e => setNewCompany(e.target.value)} />
onKeyDown={e => { if (e.key === 'Enter' && newName.trim()) { e.preventDefault(); add.mutate({ name: newName.trim(), company: newCompany.trim() }); }}} /> <input
className="form-input text-sm"
placeholder="Société"
value={newCompany}
onChange={e => setNewCompany(e.target.value)}
/>
<div className="flex gap-2 pt-1">
<button <button
onClick={() => { if (newName.trim()) add.mutate({ name: newName.trim(), company: newCompany.trim() }); }} onClick={() => { if (newName.trim()) { add.mutate({ name: newName.trim(), company: newCompany.trim() }); } }}
disabled={!newName.trim() || add.isPending} disabled={!newName.trim() || add.isPending}
className="px-4 py-2 bg-indigo-600 text-white text-sm font-semibold rounded-xl disabled:opacity-40"> className="btn-primary py-2 text-sm"
+ >
{add.isPending ? 'Ajout…' : 'Ajouter'}
</button>
{/* Import CSV */}
<input
ref={fileRef}
type="file"
accept=".csv,.xlsx,.xls"
className="hidden"
onChange={handleFileImport}
/>
<button
type="button"
onClick={() => fileRef.current?.click()}
disabled={importing}
className="btn-secondary py-2 text-sm"
>
{importing ? 'Import…' : 'Importer CSV/Excel'}
</button> </button>
</div> </div>
</div> </div>
@@ -662,18 +661,18 @@ function ContactsSection() {
); );
} }
// ─── Page principale ───────────────────────────────────────── // ─── Page Paramètres (assemblage) ────────────────────────────
export default function Settings() { export default function Settings() {
return ( return (
<div className="space-y-4"> <div className="space-y-3">
<h2 className="text-xl font-bold text-gray-900">Paramètres</h2> <h1 className="text-xl font-bold text-gray-900 mb-4">Paramètres</h1>
<PasswordSection /> <PasswordSection />
<SmtpSection /> <SmtpSection />
<CompaniesSection /> <CompaniesSection />
<CategoriesSection /> <CategoriesSection />
<ContactsSection />
<GraphSection /> <GraphSection />
<ContactsSection />
</div> </div>
); );
} }