qualifier / frontend /src /lib /mergeSequenceIntoContacts.js
Sethu0330's picture
Add FTL RFQ Qualifier agent + full SALES CRM app for HF Spaces testing
005df22 unverified
Raw History Blame Contribute Delete
2.93 kB
/**
* Merge one generated sequence row (email or LinkedIn) into grouped contacts for previews.
* When stepOrder is present (campaign wizard), dedupe by stepOrder + channel so merged timelines stay stable.
* Always returns new references (immutable) so React re-renders when LinkedIn rows stream in after emails.
*/
export function mergeSequenceIntoContacts(prev, sequence) {
const ch = sequence.channel || 'email';
// `id` (the CSV row's sequence_id) is a stable per-contact identifier that's always present
// and unique within a run — prefer it so contacts without an email (common for Apollo rows
// with only a LinkedIn URL) never collide with another no-email contact sharing the same name.
const matchIdx =
sequence.id != null
? prev.findIndex((c) => c.id === sequence.id)
: prev.findIndex(
(c) =>
c.firstName === sequence.firstName &&
c.lastName === sequence.lastName &&
c.email === sequence.email
);
const stepOrder =
sequence.stepOrder != null && sequence.stepOrder !== undefined ? sequence.stepOrder : null;
const step = {
emailNumber: sequence.emailNumber || 1,
subject: sequence.subject,
emailContent: sequence.emailContent,
channel: ch,
stepOrder,
};
const dedupeKey =
stepOrder != null ? `ord-${stepOrder}-${ch}` : `${ch}-${step.emailNumber}`;
if (matchIdx === -1) {
const base = {
id: sequence.id,
firstName: sequence.firstName,
lastName: sequence.lastName,
email: sequence.email,
company: sequence.company,
title: sequence.title,
product: sequence.product,
emails: [],
linkedin: [],
};
if (ch === 'linkedin') {
base.linkedin = [step];
} else {
base.emails = [step];
}
return [...prev, base];
}
const existing = prev[matchIdx];
const emails = [...(existing.emails || [])];
const linkedin = [...(existing.linkedin || [])];
if (ch === 'linkedin') {
if (
!linkedin.some((e) => {
const k =
e.stepOrder != null ? `ord-${e.stepOrder}-${ch}` : `${ch}-${e.emailNumber}`;
return k === dedupeKey;
})
) {
linkedin.push(step);
}
} else {
if (
!emails.some((e) => {
const k =
e.stepOrder != null ? `ord-${e.stepOrder}-${ch}` : `${ch}-${e.emailNumber}`;
return k === dedupeKey;
})
) {
emails.push(step);
}
}
const updated = {
...existing,
emails,
linkedin,
};
return prev.map((c, i) => (i === matchIdx ? updated : c));
}