import { senderDisplayName } from "./senderAvatar"; const CATEGORY_ORDER = ["Primary", "Updates", "Promotions", "Social", "Finance"]; export function emailSearchText(email) { const sender = typeof email.sender === "string" ? email.sender : email.sender?.name || email.sender?.email || email.sender?.address || ""; return [ sender, email.subject, email.snippet, email.preview, email.body_text, ].filter(Boolean).join(" ").toLowerCase(); } export function emailCategory(email) { const text = emailSearchText(email); if (/invoice|bill|payment|receipt|finance|bank|transfer|otp|wallet/.test(text)) return "Finance"; if (/linkedin|twitter|facebook|instagram|connection request|social/.test(text)) return "Social"; if (/unsubscribe|promo|offer|sale|newsletter|deal|discount/.test(text)) return "Promotions"; if (/notification|update|alert|noreply|no-reply|provisioned|activated|console|security/.test(text)) return "Updates"; return "Primary"; } const PRIORITY_LABELS = new Set(["Important", "Clients", "Projects", "Finance", "HR"]); const ACTION_LABELS = new Set(["Clients", "Important", "Projects", "Finance", "HR", "AI"]); export function emailLabels(email) { const text = emailSearchText(email); const labels = []; if (email.is_starred || /urgent|important|asap|priority/.test(text)) { labels.push({ name: "Important", tone: "rose" }); } if (/project|proposal|website|development|sow|contract/.test(text)) { labels.push({ name: "Projects", tone: "violet" }); } if (/client|customer|internship|candidate|revdau|desi clans/.test(text)) { labels.push({ name: "Clients", tone: "blue" }); } if (/hr|hiring|recruit|offer letter|onboarding/.test(text)) { labels.push({ name: "HR", tone: "violet" }); } if (/invoice|bill|payment|finance|receipt/.test(text)) { labels.push({ name: "Finance", tone: "amber" }); } if (/integration|api|webhook|connect/.test(text)) { labels.push({ name: "Integrations", tone: "green" }); } if (/ai|agent|llm|gpt|model/.test(text)) { labels.push({ name: "AI", tone: "pink" }); } if (/domain|dns|mx|spf|dkim|\.env|environment variable/.test(text)) { labels.push({ name: "Domains", tone: "amber" }); } if (/linkedin|twitter|facebook|instagram|social/.test(text)) { labels.push({ name: "Social", tone: "green" }); } if (/notification|update|alert|noreply|security/.test(text)) { labels.push({ name: "Updates", tone: "blue" }); } if (/personal|family/.test(text)) { labels.push({ name: "Personal", tone: "green" }); } const unique = []; labels.forEach((label) => { if (!unique.some((item) => item.name === label.name)) unique.push(label); }); return unique.slice(0, 2); } export function matchesLens(email, lens) { if (!lens || lens === "all") return true; if (lens === "starred") return email.is_starred === true; return emailLabels(email).some((label) => label.name === lens); } export function categoryCounts(emails) { const counts = Object.fromEntries(CATEGORY_ORDER.map((name) => [name, 0])); emails.forEach((email) => { if (email.is_read === false) counts[emailCategory(email)] += 1; }); return counts; } function hasPriorityLabel(email) { return emailLabels(email).some((label) => PRIORITY_LABELS.has(label.name)); } function hasActionLabel(email) { return emailLabels(email).some((label) => ACTION_LABELS.has(label.name)); } function looksActionable(email) { const text = emailSearchText(email); return /please|action|reply|review|attached|confirm|need|waiting|\?|follow up|response/.test(text) || attachmentCount(email) > 0 || /https?:\/\//.test(text); } export function inboxIntelligence(emails) { const unread = emails.filter((email) => email.is_read === false); const important = emails.filter((email) => email.is_starred || email.is_read === false || hasPriorityLabel(email) || emailLabels(email).length > 0, ); const actionItems = emails.filter((email) => looksActionable(email) || hasActionLabel(email) || email.is_read === false, ); const meetings = emails.filter((email) => /meet|meeting|calendar|schedule|call|zoom|interview|sync|demo|standup/.test(emailSearchText(email)), ); const invoices = emails.filter((email) => /invoice|bill|payment|receipt|purchase order|\bpo\b/.test(emailSearchText(email)), ); return { unread: unread.length, important: important.length, actionItems: actionItems.length, meetings: meetings.length, invoices: invoices.length, importantFromClients: important.filter((email) => emailLabels(email).some((label) => label.name === "Clients"), ).length, }; } export function attachmentCount(email) { if (Array.isArray(email.attachments)) return email.attachments.length; return Number(email.attachment_count || 0); } function emailTimestamp(email) { const value = email.received_at || email.sent_at || email.created_at || email.updated_at; if (!value) return null; const date = new Date(value); return Number.isNaN(date.getTime()) ? null : date; } function formatRelativeAge(date) { if (!date) return ""; const hours = Math.floor((Date.now() - date.getTime()) / 3600000); if (hours < 1) return "<1h"; if (hours < 24) return `${hours}h`; return `${Math.floor(hours / 24)}d`; } function extractMeetingTime(text) { const match = text.match(/\b(\d{1,2}:\d{2})\s*(am|pm)?/i) || text.match(/\b(\d{1,2})\s*(am|pm)\b/i); if (!match) return null; const raw = match[0].replace(/\s+/g, " ").trim(); if (/am|pm/i.test(raw)) return raw.toUpperCase(); const [hours, minutes] = raw.split(":").map(Number); if (Number.isNaN(hours)) return raw; const period = hours >= 12 ? "PM" : "AM"; const hour12 = hours % 12 || 12; return `${hour12}:${String(minutes || 0).padStart(2, "0")} ${period}`; } function extractVenue(text) { if (/google meet|meet\.google/i.test(text)) return "Google Meet"; if (/zoom/i.test(text)) return "Zoom"; if (/teams|microsoft teams/i.test(text)) return "Teams"; if (/video call|video meeting/i.test(text)) return "Video call"; return ""; } function extractReplyStatus(text) { if (/confirm|confirmation|awaiting/.test(text)) return "Awaiting confirmation"; if (/proposal|quote/.test(text)) return "Proposal reply"; if (/invoice|payment|bill/.test(text)) return "Payment follow-up"; if (/review|feedback/.test(text)) return "Needs review"; if (/reply|respond/.test(text)) return "Needs reply"; return "Needs attention"; } function extractDeadline(text) { if (/tomorrow/i.test(text)) return "Tomorrow"; const weekday = text.match(/\b(monday|tuesday|wednesday|thursday|friday|saturday|sunday)\b/i); if (weekday) { return weekday[1].charAt(0).toUpperCase() + weekday[1].slice(1); } if (/today/i.test(text)) return "Today"; return null; } export function inboxContext(emails) { const stats = inboxIntelligence(emails); const needsReply = emails .filter((email) => email.is_read === false || looksActionable(email) || hasActionLabel(email) || /please|reply|respond|confirm|review|attached|need your|waiting|action required/.test( emailSearchText(email), ), ) .sort((left, right) => { const leftDate = emailTimestamp(left)?.getTime() || 0; const rightDate = emailTimestamp(right)?.getTime() || 0; return rightDate - leftDate; }) .slice(0, 4) .map((email) => ({ id: email.id, name: senderDisplayName(email), age: formatRelativeAge(emailTimestamp(email)), status: extractReplyStatus(emailSearchText(email)), })); const today = []; emails.forEach((email) => { const text = emailSearchText(email); if (!/meet|meeting|call|interview|sync|calendar/.test(text)) return; const time = extractMeetingTime(text); if (!time) return; const label = (email.subject || "Meeting").trim().slice(0, 42); const venue = extractVenue(text); const tone = /invoice|payment|bill|purchase order|\bpo\b/.test(text) ? "amber" : /sync|project/.test(text) ? "rose" : /client|demo/.test(text) ? "blue" : "green"; if (!today.some((item) => item.time === time && item.label === label)) { today.push({ time, label, venue, tone }); } }); const deadlines = []; emails.forEach((email) => { const text = emailSearchText(email); if (!/invoice|proposal|payment|due|deadline|bill/.test(text)) return; const due = extractDeadline(text); if (!due) return; const label = (email.subject || "Deadline") .split(/[-–:|]/) .map((part) => part.trim()) .find(Boolean) ?.slice(0, 28) || "Deadline"; if (!deadlines.some((item) => item.label === label && item.due === due)) { deadlines.push({ label, due }); } }); return { stats, needsReply, today: today.slice(0, 4), deadlines: deadlines.slice(0, 4), }; } export function labelCounts(emails) { const counts = { Important: 0, Projects: 0, Clients: 0, Finance: 0, Personal: 0, }; emails.forEach((email) => { emailLabels(email).forEach((label) => { counts[label.name] = (counts[label.name] || 0) + 1; }); }); return counts; }