Unification polish: LinkedIn in the grid inline contact editor (v0.1.0:54)
The fundraising grid's per-contact editor now has a LinkedIn URL field next to name, email, title, and location. It threads through the grid contact object and sanitize (which preserves contact-object fields), and _upsert_contact_from_fundraising now reads and persists linkedin_url on both the update and insert paths — so a LinkedIn entered in the grid lands on the linked contact record. Test: test_grid_contact_link.py extended to assert LinkedIn entered in the grid persists to the contact (idempotent). Frontend html.parser clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+15
-4
@@ -6946,9 +6946,9 @@
|
||||
const contacts = Array.isArray(value) ? [...value] : [];
|
||||
const updateContacts = (next) => updateCell(row.id, col.id, next);
|
||||
return (
|
||||
<div style={{ minWidth: '380px', background: '#0b1118', border: '1px solid #263548', borderRadius: '8px', padding: '8px' }}>
|
||||
<div style={{ minWidth: '480px', background: '#0b1118', border: '1px solid #263548', borderRadius: '8px', padding: '8px' }}>
|
||||
{contacts.map((c, idx) => (
|
||||
<div key={idx} style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr auto', gap: '6px', marginBottom: '6px' }}>
|
||||
<div key={idx} style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr 1fr auto', gap: '6px', marginBottom: '6px' }}>
|
||||
<input className="text-input" placeholder="Name" value={c.name || ''} onChange={(e) => {
|
||||
const next = [...contacts];
|
||||
next[idx] = { ...next[idx], name: e.target.value };
|
||||
@@ -6982,6 +6982,17 @@
|
||||
moveByKey(rowIndex, colIndex, 'Enter');
|
||||
}
|
||||
}} />
|
||||
<input className="text-input" placeholder="LinkedIn URL" value={c.linkedin_url || ''} onChange={(e) => {
|
||||
const next = [...contacts];
|
||||
next[idx] = { ...next[idx], linkedin_url: e.target.value };
|
||||
updateContacts(next);
|
||||
}} onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
setEditing(null);
|
||||
moveByKey(rowIndex, colIndex, 'Enter');
|
||||
}
|
||||
}} />
|
||||
<input
|
||||
className="text-input"
|
||||
list="location-suggestions"
|
||||
@@ -7012,13 +7023,13 @@
|
||||
const next = contacts.filter((_, i) => i !== idx);
|
||||
updateContacts(next);
|
||||
}}>×</button>
|
||||
<div style={{ gridColumn: '1 / span 5', fontSize: '11px', color: '#8ea2b7' }}>
|
||||
<div style={{ gridColumn: '1 / span 6', fontSize: '11px', color: '#8ea2b7' }}>
|
||||
{c.city || c.state || c.country ? `${c.city || '-'}, ${c.state || '-'}, ${c.country || '-'}` : 'No location set'}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<button type="button" className="button-secondary" onClick={() => updateContacts([...contacts, { name: '', email: '', title: '', city: '', state: '', country: '', location_query: '' }])}>+ Contact</button>
|
||||
<button type="button" className="button-secondary" onClick={() => updateContacts([...contacts, { name: '', email: '', title: '', linkedin_url: '', city: '', state: '', country: '', location_query: '' }])}>+ Contact</button>
|
||||
<button type="button" onClick={() => { setEditing(null); moveFocus(rowIndex + 1, colIndex); }}>Done</button>
|
||||
</div>
|
||||
<datalist id="location-suggestions">
|
||||
|
||||
Reference in New Issue
Block a user