Function bodies 106 total
processRow function · javascript · L209-L245 (37 LOC)js/ingest.js
function processRow(row, options) {
const deal = {};
const referenceDate = (options && options.referenceDate) || undefined;
// Map columns
for (const [csvCol, internalField] of Object.entries(COLUMN_MAPPINGS)) {
deal[internalField] = row[csvCol] || '';
}
// Parse ACV - only keep CAD values
const acvResult = parseACV(deal.acv);
if (!acvResult.isCAD) {
return null; // Skip non-CAD values
}
deal.acv = acvResult.value;
deal.acvFormatted = formatCurrency(acvResult.value);
// Parse and format dates
deal.closingDate = parseDate(deal.closingDate);
deal.modifiedDate = parseDate(deal.modifiedDate);
// Calculate days since
deal.daysSince = calculateDaysSince(deal.modifiedDate, referenceDate);
deal.urgency = getUrgencyLevel(deal.daysSince);
// Closing date awareness
deal.daysUntilClosing = calculateDaysUntilClosing(validateRow function · javascript · L247-L262 (16 LOC)js/ingest.js
function validateRow(deal) {
if (!deal) return false;
// Check Deal Owner format - should be a name, not a sentence
const owner = deal.dealOwner || '';
if (!owner || owner.length > 100 || owner.split(' ').length > 5) {
return false;
}
// Check Deal Name exists
if (!deal.dealName || deal.dealName.trim().length === 0) {
return false;
}
return true;
}generateFallbackSummary function · javascript · L264-L284 (21 LOC)js/ingest.js
function generateFallbackSummary(notes) {
// Deduplicate notes
const unique = [...new Set(notes)];
if (unique.length === 0) return '';
// Extract first sentence from each unique note
const sentences = unique.map(note => {
const match = note.match(/^(.+?[.!?])\s/);
if (match && match[1].length <= 150) {
return match[1];
}
return note.length > 150 ? note.slice(0, 147) + '...' : note;
});
// Join and cap at 500 characters
let summary = sentences.join(' | ');
if (summary.length > 500) {
summary = summary.slice(0, 497) + '...';
}
return summary;
}deduplicateDeals function · javascript · L286-L386 (101 LOC)js/ingest.js
async function deduplicateDeals(deals, existingDeals = [], generateAISummaries = async () => null) {
const dealMap = new Map();
const notesMap = new Map();
for (const deal of deals) {
const key = deal.dealKey;
const existing = dealMap.get(key);
// Collect all notes for this deal
if (!notesMap.has(key)) {
notesMap.set(key, []);
}
if (deal.noteContent && deal.noteContent.trim()) {
notesMap.get(key).push(deal.noteContent.trim());
}
if (!existing) {
dealMap.set(key, deal);
} else {
// Keep the one with newer modified date
if (deal.modifiedDate && existing.modifiedDate) {
if (deal.modifiedDate > existing.modifiedDate) {
dealMap.set(key, deal);
}
} else if (deal.modifiedDate) {
applyAISummaries function · javascript · L390-L457 (68 LOC)js/ingest.js
async function applyAISummaries(deals, existingDeals = [], generateAISummaries = async () => null) {
// Build lookup of existing deal data by key
const oldDealMap = new Map();
for (const old of existingDeals) {
oldDealMap.set(old.dealKey || makeDealKey(old.dealName, old.dealOwner), old);
}
// Determine which deals need new AI summaries (hash-based reuse)
const dealsNeedingSummary = [];
let cacheHits = 0;
for (const deal of deals) {
const oldDeal = oldDealMap.get(deal.dealKey);
const oldSummary = oldDeal?.notesSummary || '';
const oldHash = oldDeal?.summaryHash || '';
if (oldSummary && oldHash && oldHash === deal.notesHash) {
deal.notesSummary = oldSummary;
deal.summaryHash = oldHash;
cacheHits++;
} else {
dealsNeedingSummary.push(deal);
}
}
// Call AI in callClaude function · typescript · L30-L88 (59 LOC)supabase/functions/summarize-notes/index.ts
async function callClaude(
apiKey: string,
dealsList: string,
dealCount: number
): Promise<Record<string, string>> {
const response = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": apiKey,
"anthropic-version": "2023-06-01",
},
body: JSON.stringify({
model: MODEL_ID,
max_tokens: 4096,
messages: [
{
role: "user",
content: `You are summarizing CRM deal notes for a sales dashboard. For each deal below, write a 3-5 sentence summary that covers: (1) current status and stage of the deal, (2) key activities and interactions so far, (3) blockers or risks, and (4) next steps and expected timeline. Be factual and specific—include names, dates, and action items where available.
Return a JSON object where keys are the deal numbers ("1", "2", etc.) and values are the summary strings. You must include all ${dealCount} deals.
${de‹ prevpage 3 / 3