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.
This commit is contained in:
Joseph McQueen 2026-07-21 15:53:14 -04:00
parent 7c7d0a898c
commit a807f22b43

View file

@ -88,6 +88,38 @@ export const COMPONENT_ROUTES = {
'Audio Visual': { kind: 'av', collect: collectDeviceStatus }, '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 // 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 // and easy to audit against the spec. Any status/component change lives
// here. // here.
@ -263,7 +295,7 @@ export async function pollNewTickets({ prime = false } = {}) {
return { enriched: 0, skipped: issues.length - primed, primed }; 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 } const skipped = []; // { key, reason }
let totalTokens = 0; let totalTokens = 0;
for (const issue of issues) { for (const issue of issues) {
@ -369,6 +401,10 @@ export async function pollNewTickets({ prime = false } = {}) {
storeNum: classification.storeNum, storeNum: classification.storeNum,
kind: classification.kind, kind: classification.kind,
reason: classification.reason, 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) { } catch (err) {
logger('jira:poller', `${key}: enrichment failed — ${err.message}`, 'error'); 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.length} new ticket${enriched.length === 1 ? '' : 's'} auto-enriched** (${elapsedSec}s, ${totalTokens} AI tokens)`,
'', '',
...enriched.map((t) => ...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}_`, ` _AI: ${t.reason}_`,
), ),
]; ];