feat: completer Settings.tsx - ContactsSection + export default Settings
This commit is contained in:
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user