Suspects: Spalte Mail und Löschen direkt in der Tabelle

- email als Symbol mit Adresse im Tooltip, Klick öffnet mailto:
  (Spalte per migrateColumns nachgezogen)
- Löschen-Knopf am Zeilenende; Server erlaubt Löschen jetzt auch
  ohne vorheriges Archivieren

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
schakal00783
2026-09-29 12:33:32 +02:00
co-authored by Claude Opus 5.5
parent 6a452f1510
commit 78d6bef52c
7 changed files with 62 additions and 42 deletions
+4 -3
View File
@@ -36,14 +36,15 @@ siehe [13 Suspects](13-suspects.md).
1. **Erfassen:** in die leere Zeile unten schreiben (Name ist Pflicht). Die Zeile 1. **Erfassen:** in die leere Zeile unten schreiben (Name ist Pflicht). Die Zeile
speichert, sobald du sie verlässt oder Enter drückst. Vorhandene Zellen speichert, sobald du sie verlässt oder Enter drückst. Vorhandene Zellen
einfach anklicken und ändern. einfach anklicken und ändern.
2. **Links:** LinkedIn und Website werden nach dem Speichern zum Symbol. Die 2. **Links:** Mail, LinkedIn und Website werden nach dem Speichern zum Symbol.
Adresse steht im Tooltip, ein Klick öffnet sie. Zum Ändern auf den kleinen Die Adresse steht im Tooltip, ein Klick öffnet sie (Mail: das Mailprogramm).
Stift daneben. Zum Ändern auf den kleinen Stift daneben.
3. **Nächster Impuls:** Wecker anklicken, Anzahl Tage eingeben, *Speichern*. Ist 3. **Nächster Impuls:** Wecker anklicken, Anzahl Tage eingeben, *Speichern*. Ist
der Tag erreicht, erinnert das CRM daran, die Person erneut anzusprechen. der Tag erreicht, erinnert das CRM daran, die Person erneut anzusprechen.
*Kontakt aufgenommen* trägt heute als letzten Kontakt ein. *Kontakt aufgenommen* trägt heute als letzten Kontakt ein.
4. **Archivieren** (Kasten-Symbol am Zeilenende): die Zeile wandert nach 4. **Archivieren** (Kasten-Symbol am Zeilenende): die Zeile wandert nach
*Suspects → Archiv*. Dort *Zurückholen* oder *Endgültig löschen*. *Suspects → Archiv*. Dort *Zurückholen* oder *Endgültig löschen*.
**Löschen** (rotes Symbol) entfernt die Zeile sofort endgültig – nach Rückfrage.
5. **Lead erstellen** (Symbol daneben): die Lead-Karte öffnet sich mit allen 5. **Lead erstellen** (Symbol daneben): die Lead-Karte öffnet sich mit allen
bekannten Daten. Titel eintragen, *Speichern* – der Lead steht dann unter bekannten Daten. Titel eintragen, *Speichern* – der Lead steht dann unter
*Neuer Lead*, der Suspect im Archiv. *Neuer Lead*, der Suspect im Archiv.
+10 -6
View File
@@ -21,6 +21,7 @@ flowchart LR
| Name | `name` | Pflicht – ohne Namen wird eine neue Zeile nicht gespeichert | | Name | `name` | Pflicht – ohne Namen wird eine neue Zeile nicht gespeichert |
| Funktion | `role` | Freitext | | Funktion | `role` | Freitext |
| Unternehmen | `company` | Freitext, **kein** Verweis auf `icon_companies` | | Unternehmen | `company` | Freitext, **kein** Verweis auf `icon_companies` |
| Mail | `email` | nach dem Speichern nur ein Briefsymbol, Adresse im Tooltip, Klick öffnet das Mailprogramm |
| LinkedIn | `linkedin` | nach dem Speichern nur ein Symbol, URL im Tooltip | | LinkedIn | `linkedin` | nach dem Speichern nur ein Symbol, URL im Tooltip |
| Website | `website` | ebenso; fehlt `https://`, ergänzt es der Server | | Website | `website` | ebenso; fehlt `https://`, ergänzt es der Server |
| Notiz | `notes` | einzeilig, volle Länge im Tooltip | | Notiz | `notes` | einzeilig, volle Länge im Tooltip |
@@ -56,19 +57,21 @@ Das Lead-Symbol am Zeilenende öffnet die **Lead-Karte vorbelegt**
- Phase **Neuer Lead**, zuständig der angemeldete Benutzer - Phase **Neuer Lead**, zuständig der angemeldete Benutzer
- **Unternehmen** und **Ansprechpartner** nur, wenn es im CRM einen mit gleichem - **Unternehmen** und **Ansprechpartner** nur, wenn es im CRM einen mit gleichem
Namen gibt (Groß/klein, Rechtsform und Satzzeichen egal) Namen gibt (Groß/klein, Rechtsform und Satzzeichen egal)
- **Notiz** mit allem aus der Zeile: Name, Funktion, Unternehmen, LinkedIn, - **Notiz** mit allem aus der Zeile: Name, Funktion, Unternehmen, Mail, LinkedIn,
Website, letzter Kontakt, Notiz Website, letzter Kontakt, Notiz
- **Titel bleibt leer** – der ist Pflicht und muss eingetragen werden - **Titel bleibt leer** – der ist Pflicht und muss eingetragen werden
Erst beim Speichern entsteht der Lead; danach wandert der Suspect ins Archiv Erst beim Speichern entsteht der Lead; danach wandert der Suspect ins Archiv
und trägt `lead_id` (im Archiv als **Lead** markiert). Abbrechen ändert nichts. und trägt `lead_id` (im Archiv als **Lead** markiert). Abbrechen ändert nichts.
## Archiv ## Archiv und Löschen
Archivierte und in Leads übernommene Suspects, neueste zuerst. **Zurückholen** Archivierte und in Leads übernommene Suspects, neueste zuerst. **Zurückholen**
stellt die Zeile wieder in die Tabelle, **Endgültig löschen** entfernt sie stellt die Zeile wieder in die Tabelle, **Endgültig löschen** entfernt sie.
(mit Rückfrage). Löschen geht nur aus dem Archiv – der Server antwortet sonst
mit `409`. Löschen geht auch direkt in der Tabelle (rotes Symbol am Zeilenende) – beide
Wege fragen vorher nach. Anfangs war Löschen nur aus dem Archiv möglich; auf
Wunsch vom 29.09.2026 gibt es den Knopf jetzt auch in der Tabelle.
## Tabelle `icon_suspects` ## Tabelle `icon_suspects`
@@ -76,6 +79,7 @@ mit `409`.
| --- | --- | --- | | --- | --- | --- |
| `id` | INT | | | `id` | INT | |
| `name`, `role`, `company` | VARCHAR(190) | | | `name`, `role`, `company` | VARCHAR(190) | |
| `email` | VARCHAR(190) | ohne `mailto:`; nachträglich per `migrateColumns` ergänzt |
| `linkedin`, `website` | VARCHAR(500) | immer mit Protokoll gespeichert | | `linkedin`, `website` | VARCHAR(500) | immer mit Protokoll gespeichert |
| `notes` | TEXT | | | `notes` | TEXT | |
| `last_contact_on` | DATE | | | `last_contact_on` | DATE | |
@@ -99,7 +103,7 @@ Alle unter `/api/suspects`, alle hinter `requireAuth`.
| POST | `/` | anlegen, `name` Pflicht | | POST | `/` | anlegen, `name` Pflicht |
| PATCH | `/:id` | beliebige Felder; `remind_in_days` (Zahl oder `null`), `archived`, `lead_id` | | PATCH | `/:id` | beliebige Felder; `remind_in_days` (Zahl oder `null`), `archived`, `lead_id` |
| POST | `/:id/acknowledge` | `{ snooze_days }` verschiebt, sonst Kontakt heute + Impuls weg | | POST | `/:id/acknowledge` | `{ snooze_days }` verschiebt, sonst Kontakt heute + Impuls weg |
| DELETE | `/:id` | nur archivierte (`409` sonst) | | DELETE | `/:id` | endgültig löschen (aus Tabelle oder Archiv) |
Die Zähler im Menü kommen aus `GET /api/dashboard` Die Zähler im Menü kommen aus `GET /api/dashboard`
(`counts.suspects_open`, `counts.suspects_archived`). (`counts.suspects_open`, `counts.suspects_archived`).
+6
View File
@@ -179,6 +179,12 @@ async function migrateColumns() {
'title', 'title',
'ALTER TABLE icon_contacts ADD COLUMN title VARCHAR(60) NULL AFTER id', 'ALTER TABLE icon_contacts ADD COLUMN title VARCHAR(60) NULL AFTER id',
], ],
// Mail-Adresse der Suspects - kam am Tag der Einfuehrung dazu (29.09.2026).
[
'icon_suspects',
'email',
'ALTER TABLE icon_suspects ADD COLUMN email VARCHAR(190) NULL AFTER company',
],
[ [
'icon_cv_imports', 'icon_cv_imports',
'photo', 'photo',
+5 -4
View File
@@ -13,7 +13,7 @@ import { logHistory } from '../history.js';
const router = express.Router(); const router = express.Router();
router.use(requireAuth); router.use(requireAuth);
const TEXT_FIELDS = { name: 190, role: 190, company: 190, linkedin: 500, website: 500, notes: 5000 }; const TEXT_FIELDS = { name: 190, role: 190, company: 190, email: 190, linkedin: 500, website: 500, notes: 5000 };
function readBody(body) { function readBody(body) {
const out = {}; const out = {};
@@ -26,6 +26,8 @@ function readBody(body) {
for (const field of ['linkedin', 'website']) { for (const field of ['linkedin', 'website']) {
if (out[field] && !/^https?:\/\//i.test(out[field])) out[field] = `https://${out[field]}`; if (out[field] && !/^https?:\/\//i.test(out[field])) out[field] = `https://${out[field]}`;
} }
// "mailto:" aus kopierten Links entfernen - gespeichert wird die blanke Adresse.
if (out.email) out.email = out.email.replace(/^mailto:/i, '');
if (body.last_contact_on !== undefined) { if (body.last_contact_on !== undefined) {
out.last_contact_on = /^\d{4}-\d{2}-\d{2}$/.test(String(body.last_contact_on ?? '')) out.last_contact_on = /^\d{4}-\d{2}-\d{2}$/.test(String(body.last_contact_on ?? ''))
? body.last_contact_on ? body.last_contact_on
@@ -152,14 +154,13 @@ router.post('/:id/acknowledge', async (req, res, next) => {
} }
}); });
/** Endgueltig loeschen - nur aus dem Archiv, damit nichts aus Versehen verschwindet. */ /** Endgueltig loeschen - aus der Tabelle wie aus dem Archiv (die Oberflaeche fragt nach). */
router.delete('/:id', async (req, res, next) => { router.delete('/:id', async (req, res, next) => {
const id = Number(req.params.id); const id = Number(req.params.id);
if (!Number.isInteger(id)) return res.status(400).json({ error: 'Ungültige Kennung.' }); if (!Number.isInteger(id)) return res.status(400).json({ error: 'Ungültige Kennung.' });
try { try {
const [[row]] = await pool.query('SELECT name, archived FROM icon_suspects WHERE id = ?', [id]); const [[row]] = await pool.query('SELECT name FROM icon_suspects WHERE id = ?', [id]);
if (!row) return res.status(404).json({ error: 'Suspect nicht gefunden.' }); if (!row) return res.status(404).json({ error: 'Suspect nicht gefunden.' });
if (!row.archived) return res.status(409).json({ error: 'Bitte zuerst archivieren.' });
await pool.query('DELETE FROM icon_suspects WHERE id = ?', [id]); await pool.query('DELETE FROM icon_suspects WHERE id = ?', [id]);
await logHistory({ await logHistory({
entityType: 'system', entityType: 'system',
+1
View File
@@ -356,6 +356,7 @@ CREATE TABLE IF NOT EXISTS icon_suspects (
name VARCHAR(190) NOT NULL, name VARCHAR(190) NOT NULL,
role VARCHAR(190) NULL, role VARCHAR(190) NULL,
company VARCHAR(190) NULL, company VARCHAR(190) NULL,
email VARCHAR(190) NULL,
linkedin VARCHAR(500) NULL, linkedin VARCHAR(500) NULL,
website VARCHAR(500) NULL, website VARCHAR(500) NULL,
notes TEXT NULL, notes TEXT NULL,
+34 -28
View File
@@ -31,12 +31,13 @@ interface SuspectsProps {
onChanged: () => void; onChanged: () => void;
} }
type TextField = 'name' | 'role' | 'company' | 'linkedin' | 'website' | 'notes'; type TextField = 'name' | 'role' | 'company' | 'email' | 'linkedin' | 'website' | 'notes';
const EMPTY_DRAFT: Record<TextField, string> & { last_contact_on: string } = { const EMPTY_DRAFT: Record<TextField, string> & { last_contact_on: string } = {
name: '', name: '',
role: '', role: '',
company: '', company: '',
email: '',
linkedin: '', linkedin: '',
website: '', website: '',
notes: '', notes: '',
@@ -161,23 +162,25 @@ export const Suspects: React.FC<SuspectsProps> = ({ archived, companies, contact
</p> </p>
<div className="overflow-x-auto rounded-xl border border-ink-200 bg-surface"> <div className="overflow-x-auto rounded-xl border border-ink-200 bg-surface">
<table className="w-full min-w-[1100px] table-fixed text-sm"> <table className="w-full min-w-[1200px] table-fixed text-sm">
<colgroup> <colgroup>
<col className="w-[15%]" /> <col className="w-[15%]" />
<col className="w-[14%]" /> <col className="w-[14%]" />
<col className="w-[14%]" /> <col className="w-[14%]" />
<col className="w-[64px]" />
<col className="w-[72px]" /> <col className="w-[72px]" />
<col className="w-[72px]" /> <col className="w-[72px]" />
<col /> <col />
<col className="w-[130px]" /> <col className="w-[130px]" />
<col className="w-[120px]" /> <col className="w-[120px]" />
<col className="w-[96px]" /> <col className="w-[128px]" />
</colgroup> </colgroup>
<thead> <thead>
<tr className="border-b border-ink-200 text-left text-[11px] font-semibold uppercase tracking-wider text-ink-500"> <tr className="border-b border-ink-200 text-left text-[11px] font-semibold uppercase tracking-wider text-ink-500">
<th className="px-3 py-3">Name</th> <th className="px-3 py-3">Name</th>
<th className="px-3 py-3">Funktion</th> <th className="px-3 py-3">Funktion</th>
<th className="px-3 py-3">Unternehmen</th> <th className="px-3 py-3">Unternehmen</th>
<th className="px-3 py-3 text-center">Mail</th>
<th className="px-3 py-3 text-center">LinkedIn</th> <th className="px-3 py-3 text-center">LinkedIn</th>
<th className="px-3 py-3 text-center">Website</th> <th className="px-3 py-3 text-center">Website</th>
<th className="px-3 py-3">Notiz</th> <th className="px-3 py-3">Notiz</th>
@@ -206,6 +209,9 @@ export const Suspects: React.FC<SuspectsProps> = ({ archived, companies, contact
/> />
</td> </td>
))} ))}
<td className="px-1 py-1 text-center">
<LinkCell value={row.email} kind="mail" onCommit={(value) => void update(row, { email: value })} />
</td>
<td className="px-1 py-1 text-center"> <td className="px-1 py-1 text-center">
<LinkCell value={row.linkedin} kind="linkedin" onCommit={(value) => void update(row, { linkedin: value })} /> <LinkCell value={row.linkedin} kind="linkedin" onCommit={(value) => void update(row, { linkedin: value })} />
</td> </td>
@@ -231,6 +237,7 @@ export const Suspects: React.FC<SuspectsProps> = ({ archived, companies, contact
<div className="flex justify-end gap-1"> <div className="flex justify-end gap-1">
<IconButton title="Archivieren" onClick={() => void archive(row)} path={ARCHIVE_ICON} /> <IconButton title="Archivieren" onClick={() => void archive(row)} path={ARCHIVE_ICON} />
<IconButton title="Lead erstellen" onClick={() => setToLead(row)} path={LEAD_ICON} tone="brand" /> <IconButton title="Lead erstellen" onClick={() => setToLead(row)} path={LEAD_ICON} tone="brand" />
<IconButton title="Löschen" onClick={() => setRemoving(row)} path={DELETE_ICON} tone="red" />
</div> </div>
</td> </td>
</tr> </tr>
@@ -251,7 +258,7 @@ export const Suspects: React.FC<SuspectsProps> = ({ archived, companies, contact
} }
}} }}
> >
{(['name', 'role', 'company', 'linkedin', 'website', 'notes'] as const).map((field) => ( {(['name', 'role', 'company', 'email', 'linkedin', 'website', 'notes'] as const).map((field) => (
<td key={field} className="px-1 py-1"> <td key={field} className="px-1 py-1">
<input <input
ref={field === 'name' ? draftName : undefined} ref={field === 'name' ? draftName : undefined}
@@ -278,7 +285,7 @@ export const Suspects: React.FC<SuspectsProps> = ({ archived, companies, contact
{archived && rows.length === 0 && ( {archived && rows.length === 0 && (
<tr> <tr>
<td colSpan={9} className="py-12 text-center text-sm text-ink-400"> <td colSpan={10} className="py-12 text-center text-sm text-ink-400">
Das Archiv ist leer. Das Archiv ist leer.
</td> </td>
</tr> </tr>
@@ -328,6 +335,7 @@ const DRAFT_PLACEHOLDERS: Record<TextField, string> = {
name: '+ Name', name: '+ Name',
role: 'Funktion', role: 'Funktion',
company: 'Unternehmen', company: 'Unternehmen',
email: 'Mail',
linkedin: 'LinkedIn-URL', linkedin: 'LinkedIn-URL',
website: 'Website', website: 'Website',
notes: 'Notiz', notes: 'Notiz',
@@ -372,8 +380,16 @@ const TextCell: React.FC<{ value: string | null; required?: boolean; onCommit: (
); );
}; };
type LinkKind = 'linkedin' | 'website' | 'mail';
const LINK_KINDS: Record<LinkKind, { color: string; href: (value: string) => string; placeholder: string }> = {
linkedin: { color: 'text-[#0a66c2]', href: (v) => v, placeholder: 'URL' },
website: { color: 'text-brand-600', href: (v) => v, placeholder: 'URL' },
mail: { color: 'text-brand-600', href: (v) => `mailto:${v}`, placeholder: 'Mail' },
};
/** Link als Symbol; die Adresse zeigt der Tooltip. Bearbeiten über den Stift. */ /** Link als Symbol; die Adresse zeigt der Tooltip. Bearbeiten über den Stift. */
const LinkCell: React.FC<{ value: string | null; kind: 'linkedin' | 'website'; onCommit: (value: string | null) => void }> = ({ const LinkCell: React.FC<{ value: string | null; kind: LinkKind; onCommit: (value: string | null) => void }> = ({
value, value,
kind, kind,
onCommit, onCommit,
@@ -399,7 +415,8 @@ const LinkCell: React.FC<{ value: string | null; kind: 'linkedin' | 'website'; o
setEditing(false); setEditing(false);
} }
}} }}
placeholder="URL" type={kind === 'mail' ? 'email' : 'text'}
placeholder={LINK_KINDS[kind].placeholder}
className={`${cellInput} text-xs`} className={`${cellInput} text-xs`}
/> />
); );
@@ -407,23 +424,11 @@ const LinkCell: React.FC<{ value: string | null; kind: 'linkedin' | 'website'; o
return ( return (
<span className="group inline-flex items-center gap-0.5"> <span className="group inline-flex items-center gap-0.5">
<a <StaticLink href={value} kind={kind} />
href={value}
target="_blank"
rel="noopener noreferrer"
title={value}
className={`inline-flex h-8 w-8 items-center justify-center rounded-md hover:bg-ink-100 ${
kind === 'linkedin' ? 'text-[#0a66c2]' : 'text-brand-600'
}`}
>
<svg viewBox="0 0 24 24" className="h-4 w-4" fill="currentColor" aria-hidden="true">
<path d={kind === 'linkedin' ? LINKEDIN_ICON : LINK_ICON} />
</svg>
</a>
<button <button
type="button" type="button"
onClick={() => setEditing(true)} onClick={() => setEditing(true)}
title="Link bearbeiten" title={kind === 'mail' ? 'Mail bearbeiten' : 'Link bearbeiten'}
className="invisible rounded p-0.5 text-ink-400 hover:text-ink-700 group-hover:visible" className="invisible rounded p-0.5 text-ink-400 hover:text-ink-700 group-hover:visible"
> >
<svg viewBox="0 0 24 24" className="h-3 w-3" fill="currentColor" aria-hidden="true"> <svg viewBox="0 0 24 24" className="h-3 w-3" fill="currentColor" aria-hidden="true">
@@ -534,6 +539,7 @@ const ArchivedRow: React.FC<{ row: Suspect; onRestore: () => void; onDelete: ()
</td> </td>
<td className="truncate px-3 py-2.5 text-ink-600">{row.role ?? '–'}</td> <td className="truncate px-3 py-2.5 text-ink-600">{row.role ?? '–'}</td>
<td className="truncate px-3 py-2.5 text-ink-600">{row.company ?? '–'}</td> <td className="truncate px-3 py-2.5 text-ink-600">{row.company ?? '–'}</td>
<td className="px-3 py-2.5 text-center">{row.email && <StaticLink href={row.email} kind="mail" />}</td>
<td className="px-3 py-2.5 text-center">{row.linkedin && <StaticLink href={row.linkedin} kind="linkedin" />}</td> <td className="px-3 py-2.5 text-center">{row.linkedin && <StaticLink href={row.linkedin} kind="linkedin" />}</td>
<td className="px-3 py-2.5 text-center">{row.website && <StaticLink href={row.website} kind="website" />}</td> <td className="px-3 py-2.5 text-center">{row.website && <StaticLink href={row.website} kind="website" />}</td>
<td className="truncate px-3 py-2.5 text-ink-600" title={row.notes ?? undefined}> <td className="truncate px-3 py-2.5 text-ink-600" title={row.notes ?? undefined}>
@@ -550,18 +556,16 @@ const ArchivedRow: React.FC<{ row: Suspect; onRestore: () => void; onDelete: ()
</tr> </tr>
); );
const StaticLink: React.FC<{ href: string; kind: 'linkedin' | 'website' }> = ({ href, kind }) => ( const StaticLink: React.FC<{ href: string; kind: LinkKind }> = ({ href, kind }) => (
<a <a
href={href} href={LINK_KINDS[kind].href(href)}
target="_blank" target={kind === 'mail' ? undefined : '_blank'}
rel="noopener noreferrer" rel="noopener noreferrer"
title={href} title={href}
className={`inline-flex h-7 w-7 items-center justify-center rounded-md hover:bg-ink-100 ${ className={`inline-flex h-8 w-8 items-center justify-center rounded-md hover:bg-ink-100 ${LINK_KINDS[kind].color}`}
kind === 'linkedin' ? 'text-[#0a66c2]' : 'text-brand-600'
}`}
> >
<svg viewBox="0 0 24 24" className="h-4 w-4" fill="currentColor" aria-hidden="true"> <svg viewBox="0 0 24 24" className="h-4 w-4" fill="currentColor" aria-hidden="true">
<path d={kind === 'linkedin' ? LINKEDIN_ICON : LINK_ICON} /> <path d={kind === 'linkedin' ? LINKEDIN_ICON : kind === 'mail' ? MAIL_ICON : LINK_ICON} />
</svg> </svg>
</a> </a>
); );
@@ -607,6 +611,7 @@ function leadFromSuspect(row: Suspect, companies: Company[], contacts: Contact[]
const notes = [ const notes = [
`Aus Suspects: ${row.name}${row.role ? `, ${row.role}` : ''}${row.company ? ` (${row.company})` : ''}`, `Aus Suspects: ${row.name}${row.role ? `, ${row.role}` : ''}${row.company ? ` (${row.company})` : ''}`,
row.email ? `Mail: ${row.email}` : null,
row.linkedin ? `LinkedIn: ${row.linkedin}` : null, row.linkedin ? `LinkedIn: ${row.linkedin}` : null,
row.website ? `Website: ${row.website}` : null, row.website ? `Website: ${row.website}` : null,
row.last_contact_on ? `Letzter Kontakt: ${formatDate(row.last_contact_on)}` : null, row.last_contact_on ? `Letzter Kontakt: ${formatDate(row.last_contact_on)}` : null,
@@ -634,5 +639,6 @@ const ALARM_ICON =
'M12 5a8 8 0 1 1 0 16 8 8 0 0 1 0-16Zm0 2a6 6 0 1 0 0 12 6 6 0 0 0 0-12Zm-1 2h2v4l3 2-1 1.6-4-2.6V9ZM6.3 2.3l1.4 1.4-3.9 3.9-1.4-1.4 3.9-3.9Zm11.4 0 3.9 3.9-1.4 1.4-3.9-3.9 1.4-1.4Z'; 'M12 5a8 8 0 1 1 0 16 8 8 0 0 1 0-16Zm0 2a6 6 0 1 0 0 12 6 6 0 0 0 0-12Zm-1 2h2v4l3 2-1 1.6-4-2.6V9ZM6.3 2.3l1.4 1.4-3.9 3.9-1.4-1.4 3.9-3.9Zm11.4 0 3.9 3.9-1.4 1.4-3.9-3.9 1.4-1.4Z';
const LINK_ICON = const LINK_ICON =
'M10.6 13.4a1 1 0 0 1 0-1.4l3.5-3.5a1 1 0 1 1 1.4 1.4L12 13.4a1 1 0 0 1-1.4 0ZM8.5 11.3 6.4 13.4a3 3 0 0 0 4.2 4.2l2.1-2.1 1.4 1.4-2.1 2.1a5 5 0 0 1-7-7l2.1-2.1 1.4 1.4Zm7-5.7a3 3 0 0 0-4.2 0L9.2 7.7 7.8 6.3l2.1-2.1a5 5 0 0 1 7 7l-2.1 2.1-1.4-1.4 2.1-2.1a3 3 0 0 0 0-4.2Z'; 'M10.6 13.4a1 1 0 0 1 0-1.4l3.5-3.5a1 1 0 1 1 1.4 1.4L12 13.4a1 1 0 0 1-1.4 0ZM8.5 11.3 6.4 13.4a3 3 0 0 0 4.2 4.2l2.1-2.1 1.4 1.4-2.1 2.1a5 5 0 0 1-7-7l2.1-2.1 1.4 1.4Zm7-5.7a3 3 0 0 0-4.2 0L9.2 7.7 7.8 6.3l2.1-2.1a5 5 0 0 1 7 7l-2.1 2.1-1.4-1.4 2.1-2.1a3 3 0 0 0 0-4.2Z';
const MAIL_ICON = 'M3 5h18v14H3V5Zm2 2v.4l7 4.6 7-4.6V7H5Zm14 2.8-7 4.6-7-4.6V17h14V9.8Z';
const LINKEDIN_ICON = const LINKEDIN_ICON =
'M4 3h16a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1Zm3.3 7H5v8h2.3v-8ZM6.2 5.8a1.3 1.3 0 1 0 0 2.7 1.3 1.3 0 0 0 0-2.7ZM19 13.4c0-2.2-1.2-3.5-3.1-3.5-1.1 0-1.9.6-2.2 1.1V10h-2.2v8h2.3v-4.2c0-1.1.5-1.8 1.4-1.8.8 0 1.3.6 1.3 1.8V18H19v-4.6Z'; 'M4 3h16a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1Zm3.3 7H5v8h2.3v-8ZM6.2 5.8a1.3 1.3 0 1 0 0 2.7 1.3 1.3 0 0 0 0-2.7ZM19 13.4c0-2.2-1.2-3.5-3.1-3.5-1.1 0-1.9.6-2.2 1.1V10h-2.2v8h2.3v-4.2c0-1.1.5-1.8 1.4-1.8.8 0 1.3.6 1.3 1.8V18H19v-4.6Z';
+2 -1
View File
@@ -440,6 +440,7 @@ export interface Suspect {
name: string; name: string;
role: string | null; role: string | null;
company: string | null; company: string | null;
email: string | null;
linkedin: string | null; linkedin: string | null;
website: string | null; website: string | null;
notes: string | null; notes: string | null;
@@ -454,7 +455,7 @@ export interface Suspect {
} }
export type SuspectInput = Partial< export type SuspectInput = Partial<
Pick<Suspect, 'name' | 'role' | 'company' | 'linkedin' | 'website' | 'notes' | 'last_contact_on' | 'archived' | 'lead_id'> Pick<Suspect, 'name' | 'role' | 'company' | 'email' | 'linkedin' | 'website' | 'notes' | 'last_contact_on' | 'archived' | 'lead_id'>
> & { remind_in_days?: number | null }; > & { remind_in_days?: number | null };
export const suspectsApi = { export const suspectsApi = {