From a807f22b43e2ff0f37fbe9282c62e711828c4fc1 Mon Sep 17 00:00:00 2001 From: Joseph McQueen Date: Tue, 21 Jul 2026 15:53:14 -0400 Subject: [PATCH] Use Jira component icons in poller enrichment summary bullets. Show Mobility, Communication Services, and AV with distinct emoji in the hourly Webex summary instead of a generic Phone/AV label. --- services/jiraPollerService.js | 40 +++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/services/jiraPollerService.js b/services/jiraPollerService.js index bec2ea6..9a3a09e 100644 --- a/services/jiraPollerService.js +++ b/services/jiraPollerService.js @@ -88,6 +88,38 @@ export const COMPONENT_ROUTES = { 'Audio Visual': { kind: 'av', collect: collectDeviceStatus }, }; +// Component name → emoji for Webex summary bullets. Icons follow the +// Jira component (not AI `kind`) so Mobility and Communication Services +// stay visually distinct even though both enrich as phone snapshots. +export const COMPONENT_ICONS = { + 'Mobility': '📱', + 'Communication Services': '☎️', + 'Audio Visual': '📺', +}; + +// Fallbacks when a ticket has no recognized component (shouldn't +// happen given POLLER_JQL, but the AI can re-route kind independently). +const KIND_ICONS = { + phone: '☎️', + av: '📺', +}; + +/** + * Pick the summary-bullet icon for a ticket. + * Prefer the first recognized Jira component; fall back to AI kind. + * + * @param {Array<{name?: string}>|null|undefined} components + * @param {'phone'|'av'|string} [kind] + * @returns {string} + */ +export function iconForTicket(components, kind) { + for (const c of components || []) { + const icon = COMPONENT_ICONS[c?.name]; + if (icon) return icon; + } + return KIND_ICONS[kind] || '🎫'; +} + // The JQL kept as a single owned constant so it's obvious in one place // and easy to audit against the spec. Any status/component change lives // here. @@ -263,7 +295,7 @@ export async function pollNewTickets({ prime = false } = {}) { return { enriched: 0, skipped: issues.length - primed, primed }; } - const enriched = []; // { key, summary, storeNum, kind, reason } + const enriched = []; // { key, summary, storeNum, kind, reason, components } const skipped = []; // { key, reason } let totalTokens = 0; for (const issue of issues) { @@ -369,6 +401,10 @@ export async function pollNewTickets({ prime = false } = {}) { storeNum: classification.storeNum, kind: classification.kind, reason: classification.reason, + // Jira components drive the Webex bullet icon (Mobility vs Comm + // vs AV). Capture at enrich time so the summary post doesn't + // re-walk the issue object. + components: f.components || [], }); } catch (err) { logger('jira:poller', `${key}: enrichment failed — ${err.message}`, 'error'); @@ -390,7 +426,7 @@ export async function pollNewTickets({ prime = false } = {}) { `**${enriched.length} new ticket${enriched.length === 1 ? '' : 's'} auto-enriched** (${elapsedSec}s, ${totalTokens} AI tokens)`, '', ...enriched.map((t) => - `• **${t.key}** [${t.kind === 'phone' ? 'Phone' : 'AV'}, store ${t.storeNum}] — ${t.summary}\n` + + `• ${iconForTicket(t.components, t.kind)} **${t.key}** [store ${t.storeNum}] — ${t.summary}\n` + ` _AI: ${t.reason}_`, ), ];