feat: version 4.15.0 -- Equipos: pestaña Licenciamiento (Windows y Office)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Milton Gonzalez
2026-10-30 11:47:57 -05:00
parent b2c25c4fe5
commit 34183d15ce
8 changed files with 125 additions and 3 deletions
+30
View File
@@ -497,6 +497,21 @@ export const messages = {
eq_before: 'Antes',
eq_after: 'Después',
eq_history_empty: 'Sin cambios registrados entre reportes.',
eq_tab_licenses: 'Licenciamiento',
eq_lic_windows: 'Windows',
eq_lic_office: 'Office',
eq_lic_office_installed: 'Office instalado',
eq_lic_edition: 'Edición',
eq_lic_product_id: 'Product ID',
eq_lic_key: 'Clave (parcial)',
eq_lic_channel: 'Canal',
eq_lic_owner: 'Registrado a',
eq_lic_oem: 'OEM',
eq_lic_not_oem: 'Retail / Volumen',
eq_lic_msdn: 'MSDN: solo desarrollo y pruebas',
eq_lic_no_windows: 'El agente no reportó licencia de Windows (equipo sin Windows o reporte anterior).',
eq_lic_no_office: 'El agente no reportó licencias de Office.',
eq_lic_hint: 'Datos que reporta el agente desde el registro: no indican si la licencia está activada. Microsoft 365 por suscripción se licencia por usuario y no aparece aquí. La clave se muestra parcial.',
eq_change_field: 'Dato modificado',
eq_change_disk_added: 'Disco agregado',
eq_change_disk_removed: 'Disco retirado',
@@ -1264,6 +1279,21 @@ export const messages = {
eq_before: 'Before',
eq_after: 'After',
eq_history_empty: 'No changes recorded between reports.',
eq_tab_licenses: 'Licensing',
eq_lic_windows: 'Windows',
eq_lic_office: 'Office',
eq_lic_office_installed: 'Installed Office',
eq_lic_edition: 'Edition',
eq_lic_product_id: 'Product ID',
eq_lic_key: 'Key (partial)',
eq_lic_channel: 'Channel',
eq_lic_owner: 'Registered to',
eq_lic_oem: 'OEM',
eq_lic_not_oem: 'Retail / Volume',
eq_lic_msdn: 'MSDN: development and testing only',
eq_lic_no_windows: 'The agent did not report a Windows license (non-Windows device or older report).',
eq_lic_no_office: 'The agent did not report Office licenses.',
eq_lic_hint: 'Data reported by the agent from the registry: it does not tell whether the license is activated. Microsoft 365 subscriptions are licensed per user and do not show here. The key is shown partially.',
eq_change_field: 'Field changed',
eq_change_disk_added: 'Disk added',
eq_change_disk_removed: 'Disk removed',
@@ -125,6 +125,7 @@ function DeviceDetail({ id, onClose, onChanged, canEdit, tab = 'general', onTabC
{ key: 'hardware', label: t('eq_tab_hardware') },
{ key: 'network', label: t('eq_tab_network'), count: device.networks.length },
{ key: 'software', label: t('eq_tab_software'), count: device.software.filter((r) => showComponents || !r.is_component).length },
{ key: 'licenses', label: t('eq_tab_licenses'), count: (device.extra?.licenses?.windows ? 1 : 0) + (device.extra?.licenses?.office?.length || 0) },
{ key: 'history', label: t('eq_tab_history'), count: device.changes.length },
];
@@ -193,6 +194,17 @@ function DeviceDetail({ id, onClose, onChanged, canEdit, tab = 'general', onTabC
const localGroups = only(allLocalGroups, (r) => [r.name, r.sid]);
const networks = only(device.networks, (r) => [r.description, r.ip, r.mask, r.gateway, r.mac, r.status]);
const software = only(device.software.filter((r) => showComponents || !r.is_component), (r) => [r.name, r.version, r.publisher]);
const win = device.extra?.licenses?.windows;
const channel = (oem) => (oem == null ? null : oem ? t('eq_lic_oem') : t('eq_lic_not_oem'));
const winRows = win ? [
[t('eq_lic_edition'), val([win.edition, win.version].filter(Boolean).join(' ')), `${win.edition || ''} ${win.version || ''}`],
[t('eq_lic_product_id'), val(win.product_id), win.product_id],
[t('eq_lic_key'), <code>{val(win.key)}</code>, win.key],
[t('eq_lic_channel'), val(channel(win.oem)), channel(win.oem)],
[t('eq_lic_owner'), val([win.owner, win.company].filter(Boolean).join(' · ')), `${win.owner || ''} ${win.company || ''}`],
] : [];
const officeLicenses = only(device.extra?.licenses?.office || [], (r) => [r.name, r.full_name, r.product_id, r.key]);
const officeInstalled = only(device.software.filter((r) => !r.is_component && /^microsoft (office|365)\b/i.test(r.name)), (r) => [r.name, r.version]);
const changes = only(device.changes, (r) => [fmtDate(r.created_at), t(`eq_change_${r.change_type}`), r.field, r.old_value, r.new_value]);
return (
@@ -297,6 +309,27 @@ function DeviceDetail({ id, onClose, onChanged, canEdit, tab = 'general', onTabC
]} />
)}
{tab === 'licenses' && (
<>
<h4>{t('eq_lic_windows')}</h4>
{win ? showRows(winRows) : <div className="text-muted">{t('eq_lic_no_windows')}</div>}
{!q && officeLicenses.length === 0 ? (
<><h4>{t('eq_lic_office')}</h4><div className="text-muted">{t('eq_lic_no_office')}</div></>
) : hwSection(t('eq_lic_office'), officeLicenses, [
{ key: 'name', label: t('common_name'), render: (r) => (
<>{r.name} {r.msdn && <Badge color="warning">{t('eq_lic_msdn')}</Badge>}</>
) },
{ key: 'full_name', label: t('eq_lic_channel') },
{ key: 'product_id', label: t('eq_lic_product_id') },
{ key: 'key', label: t('eq_lic_key'), render: (r) => <code>{dash(r.key)}</code> },
])}
{hwSection(t('eq_lic_office_installed'), officeInstalled, [
{ key: 'name', label: t('common_name') }, { key: 'version', label: t('eq_version') },
])}
<div className="text-muted" style={{ fontSize: '.72rem', marginTop: 12 }}>{t('eq_lic_hint')}</div>
</>
)}
{tab === 'history' && (
<SimpleTable empty={q ? emptyText : t('eq_history_empty')} rows={changes} columns={[
{ key: 'created_at', label: t('eq_when'), render: (r) => fmtDate(r.created_at) },