export function extractDomain(fromAddr) { const m = String(fromAddr || '').match(/@([A-Za-z0-9.-]+)/); return m ? m[1].toLowerCase() : ''; } export function extractFirstJobNumber(text) { const t = String(text || ''); // heuristic: prefer 8+ digit job numbers (e.g., 0222600001) const m = t.match(/\b\d{8,}\b/); return m ? m[0] : ''; } export function validatePassword(pw) { const p = String(pw || ''); if (p.length < 12) return 'Password must be at least 12 characters.'; if (!/[A-Z]/.test(p)) return 'Password must include an uppercase letter.'; if (!/[a-z]/.test(p)) return 'Password must include a lowercase letter.'; if (!/\d/.test(p)) return 'Password must include a number.'; return null; } export async function applyRulesToEmail(emailRow, rules) { const from = (emailRow.from_addr || '').toLowerCase(); const subj = (emailRow.subject || '').toLowerCase(); const body = (emailRow.body_text || '').toLowerCase(); const thread = (emailRow.thread_key || '').toLowerCase(); for (const r of rules) { if (!r.enabled) continue; const v = (r.match_value || '').toLowerCase(); let hit = false; if (r.match_type === 'from_domain') { hit = v && from.includes('@') && from.split('@').pop()?.includes(v.replace(/^@/, '')); } else if (r.match_type === 'from_contains') { hit = v && from.includes(v); } else if (r.match_type === 'subject_contains') { hit = v && subj.includes(v); } else if (r.match_type === 'body_contains') { hit = v && body.includes(v); } else if (r.match_type === 'thread_key') { hit = v && thread && thread === v; } if (hit) { return { projectId: r.project_id, confidence: 0.9, ruleId: r.id }; } } return null; } export function buildPCOBody(email, project) { return [ `# Potential Change Order (PCO) — Draft`, ``, `**Project:** ${project?.name || ''}`, `**Job #:** ${project?.job_number || ''}`, `**Source email:** ${email?.subject || ''} (${email?.id || ''})`, `**From:** ${email?.from_addr || ''}`, `**Date:** ${email?.date || ''}`, ``, `## Description / Change event`, `- (Describe what changed and why)`, ``, `## Contract / drawing references`, `- (Sheet/spec refs)`, ``, `## Cost / schedule impact`, `- Labor:`, `- Material:`, `- Equipment:`, `- Schedule impact:`, ``, `## Supporting info`, `- Email excerpt:`, ``, `> ${String(email?.body_text || '').slice(0, 1200).replace(/\n/g, '\n> ')}` ].join('\n'); } export function buildRFIBody(email, project) { return [ `# RFI — Draft`, ``, `**Project:** ${project?.name || ''}`, `**Job #:** ${project?.job_number || ''}`, `**Source email:** ${email?.subject || ''} (${email?.id || ''})`, `**From:** ${email?.from_addr || ''}`, `**Date:** ${email?.date || ''}`, ``, `## Question`, `- (Write the question clearly)`, ``, `## Background`, `- (Why this is needed / conflict)`, ``, `## Drawing/spec references`, `- (Sheet/detail/spec section)`, ``, `## Proposed resolution (optional)`, `-`, ``, `## Supporting excerpt`, `> ${String(email?.body_text || '').slice(0, 1200).replace(/\n/g, '\n> ')}` ].join('\n'); }