collabSupport/services/renderers/mppPhoneFormatters.js
jmcqueen 7dc326c404 Split voice commands into status vs diag and redesign MPP phone output.
Replace /phonestatus follow-ups with /voicestatus, /wanstatus, /phonediag, and /dectdiag; extend /voicediag with relay probes and section-based MPP diagnostics.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 13:55:41 -04:00

133 lines
4.3 KiB
JavaScript

// services/renderers/mppPhoneFormatters.js
//
// Display helpers for MPP phone diagnostics follow-up messages.
import { formatMicCertForDisplay } from '../../integrations/cisco-mpp-phone/downloadStatusJson.js';
export function shortenFirmware(fw) {
if (!fw || typeof fw !== 'string') return '?';
const m = fw.match(/(sip78xx[^\s]+)/i);
return m ? m[1] : (fw.length > 36 ? `${fw.slice(0, 36)}` : fw);
}
/** @param {string|null|undefined} elapsedTime */
export function formatUptime(elapsedTime) {
if (!elapsedTime || typeof elapsedTime !== 'string') return null;
const trimmed = elapsedTime.trim();
if (!trimmed) return null;
const dayHour = trimmed.match(/(\d+)\s+days?\s+and\s+(\d+):(\d+):(\d+)/i);
if (dayHour) {
const days = Number(dayHour[1]);
const hours = Number(dayHour[2]);
if (days > 0) return `${days}d ${hours}h`;
if (hours > 0) return `${hours}h`;
return `${Number(dayHour[3])}m`;
}
const hms = trimmed.match(/^(\d+):(\d+):(\d+)$/);
if (hms) {
const hours = Number(hms[1]);
const mins = Number(hms[2]);
if (hours > 0) return `${hours}h ${mins}m`;
return `${mins}m`;
}
return trimmed.length > 24 ? `${trimmed.slice(0, 24)}` : trimmed;
}
/** @param {object} phoneStatus @param {object} neighbor */
export function formatLinkSpeed(phoneStatus = {}, neighbor = {}) {
const fromStatus = phoneStatus.linkSpeed || phoneStatus.linkConfig || null;
if (fromStatus && fromStatus !== 'Disabled') return String(fromStatus).trim();
const fromLldp = neighbor.portSpeed ? String(neighbor.portSpeed).trim() : null;
if (fromLldp) {
const compact = fromLldp.replace(/\s+/g, ' ');
if (/^\d+F$/i.test(compact)) return compact.replace(/F$/i, 'M Full');
return compact;
}
return null;
}
export function formatHeaderStatusLine(phoneStatus, neighbor) {
const uptime = formatUptime(phoneStatus?.elapsedTime);
const link = formatLinkSpeed(phoneStatus, neighbor);
const parts = [];
if (uptime) parts.push(`Uptime ${uptime}`);
if (link) parts.push(link);
return parts.length ? parts.join(' · ') : null;
}
export function isLineRegistered(line) {
const state = String(line?.registrationState || '').toLowerCase();
if (!state) return false;
if (state.includes('not registered') || state.includes('fail')) return false;
return state.includes('registered');
}
export function headerIconForLine(line) {
if (!line?.registrationState) return '⚠️';
return isLineRegistered(line) ? '✅' : '❌';
}
export function formatRebootEntry(raw) {
if (!raw) return null;
const m = String(raw).match(/^(\w+)\(([^)]+)\)/);
if (m) return `${m[1]} @ ${m[2]}`;
return String(raw);
}
export function hostFromUrl(url) {
if (!url || typeof url !== 'string') return '?';
try {
const normalized = url.startsWith('http') ? url : `https://${url}`;
return new URL(normalized).hostname;
} catch {
return url.length > 30 ? `${url.slice(0, 30)}` : url;
}
}
export function formatProvisioningResync(entry) {
if (!entry) return null;
const host = entry.url ? hostFromUrl(entry.url) : '?';
const result = entry.succeeded ? 'OK' : (entry.failed ? 'FAILED' : (entry.result || '?'));
return `Resync: ${host} · ${result}`;
}
export function formatFirmwareUpgrade(entry) {
if (!entry) return null;
if (entry.succeeded) return 'Firmware upgrade: OK';
if (entry.failed) return `Firmware upgrade: ${entry.result || 'FAILED'}`;
return null;
}
/**
* @param {object} mpp buildMppProbeView output
* @param {object} parsed
* @returns {string[]}
*/
export function collectIssues(mpp, parsed) {
const issues = [];
const download = mpp?.download;
const mic = formatMicCertForDisplay(download?.micCert);
if (mic) issues.push(mic);
if (download?.latestProvisioning?.failed) {
const host = download.latestProvisioning.url
? hostFromUrl(download.latestProvisioning.url)
: '?';
issues.push(`Provisioning resync failed — ${host}`);
}
if (download?.latestFirmwareUpgrade?.failed) {
issues.push(`Firmware upgrade failed — ${download.latestFirmwareUpgrade.result || 'unknown'}`);
}
const line1 = parsed?.lines?.find((l) => l.index === 1) || parsed?.lines?.[0];
if (line1?.registrationState && !isLineRegistered(line1)) {
issues.push(`Ext ${line1.index}: ${line1.registrationState}`);
}
return issues;
}