feat: dispositivos e ajustes de csat e relatórios
This commit is contained in:
parent
25d2a9b062
commit
e0ef66555d
86 changed files with 5811 additions and 992 deletions
|
|
@ -15,7 +15,7 @@ export async function ensureMachineAccount(params: EnsureMachineAccountParams) {
|
|||
const context = await auth.$context
|
||||
|
||||
const passwordHash = await context.password.hash(machineToken)
|
||||
const machineName = `Máquina ${hostname}`
|
||||
const machineName = `Dispositivo ${hostname}`
|
||||
|
||||
const user = await prisma.authUser.upsert({
|
||||
where: { email: machineEmail },
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export type MachineSessionContext = {
|
|||
}
|
||||
|
||||
export class MachineInactiveError extends Error {
|
||||
constructor(message = "Máquina desativada") {
|
||||
constructor(message = "Dispositivo desativada") {
|
||||
super(message)
|
||||
this.name = "MachineInactiveError"
|
||||
}
|
||||
|
|
@ -45,7 +45,7 @@ export async function createMachineSession(machineToken: string, rememberMe = tr
|
|||
|
||||
const client = new ConvexHttpClient(convexUrl)
|
||||
|
||||
const resolved = await client.mutation(api.machines.resolveToken, { machineToken })
|
||||
const resolved = await client.mutation(api.devices.resolveToken, { machineToken })
|
||||
let machineEmail = resolved.machine.authEmail ?? null
|
||||
|
||||
const machineActive = resolved.machine.isActive ?? true
|
||||
|
|
@ -62,7 +62,7 @@ export async function createMachineSession(machineToken: string, rememberMe = tr
|
|||
persona: (resolved.machine.persona ?? null) ?? undefined,
|
||||
})
|
||||
|
||||
await client.mutation(api.machines.linkAuthAccount, {
|
||||
await client.mutation(api.devices.linkAuthAccount, {
|
||||
machineId: resolved.machine._id as Id<"machines">,
|
||||
authUserId: account.authUserId,
|
||||
authEmail: account.authEmail,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,15 @@
|
|||
import { buildXlsxWorkbook, type WorksheetConfig } from "@/lib/xlsx"
|
||||
import type { Id } from "@/convex/_generated/dataModel"
|
||||
import { DEVICE_INVENTORY_COLUMN_METADATA, type DeviceInventoryColumnConfig } from "@/lib/device-inventory-columns"
|
||||
import { buildXlsxWorkbook, type WorksheetConfig } from "@/lib/xlsx"
|
||||
|
||||
type DeviceCustomField = {
|
||||
fieldId: Id<"deviceFields">
|
||||
fieldKey: string
|
||||
label: string
|
||||
type: string
|
||||
value: unknown
|
||||
displayValue?: string
|
||||
}
|
||||
|
||||
type LinkedUser = {
|
||||
id: string
|
||||
|
|
@ -11,6 +21,11 @@ export type MachineInventoryRecord = {
|
|||
id: Id<"machines">
|
||||
tenantId: string
|
||||
hostname: string
|
||||
displayName: string | null
|
||||
deviceType: string | null
|
||||
devicePlatform: string | null
|
||||
deviceProfile?: Record<string, unknown> | null
|
||||
managementMode: string | null
|
||||
companyId: Id<"companies"> | null
|
||||
companySlug: string | null
|
||||
companyName: string | null
|
||||
|
|
@ -36,6 +51,7 @@ export type MachineInventoryRecord = {
|
|||
lastPostureAt?: number | null
|
||||
remoteAccess?: unknown
|
||||
linkedUsers?: LinkedUser[]
|
||||
customFields?: DeviceCustomField[]
|
||||
}
|
||||
|
||||
type WorkbookOptions = {
|
||||
|
|
@ -43,6 +59,7 @@ type WorkbookOptions = {
|
|||
generatedBy?: string | null
|
||||
companyFilterLabel?: string | null
|
||||
generatedAt?: Date
|
||||
columns?: DeviceInventoryColumnConfig[]
|
||||
}
|
||||
|
||||
type SoftwareEntry = {
|
||||
|
|
@ -54,59 +71,270 @@ type SoftwareEntry = {
|
|||
installedOn: string | null
|
||||
}
|
||||
|
||||
const INVENTORY_HEADERS = [
|
||||
"Hostname",
|
||||
"Empresa",
|
||||
"Status",
|
||||
"Persona",
|
||||
"Ativa",
|
||||
"Último heartbeat",
|
||||
"Responsável",
|
||||
"E-mail responsável",
|
||||
"Usuários vinculados",
|
||||
"E-mail autenticado",
|
||||
"Sistema operacional",
|
||||
"Versão SO",
|
||||
"Arquitetura",
|
||||
"Fabricante",
|
||||
"Modelo",
|
||||
"Serial hardware",
|
||||
"Processador",
|
||||
"Cores físicas",
|
||||
"Cores lógicas",
|
||||
"Memória (GiB)",
|
||||
"GPUs",
|
||||
"Labels",
|
||||
"MACs",
|
||||
"Seriais",
|
||||
"IP principal",
|
||||
"IP público",
|
||||
"Registrada via",
|
||||
"Token expira em",
|
||||
"Token último uso",
|
||||
"Uso do token",
|
||||
"Criada em",
|
||||
"Atualizada em",
|
||||
"Softwares instalados",
|
||||
"Build SO",
|
||||
"Licença ativada",
|
||||
"Experiência SO",
|
||||
"Domínio",
|
||||
"Grupo de trabalho",
|
||||
"Nome do dispositivo",
|
||||
"Serial placa-mãe",
|
||||
"Colaborador (nome)",
|
||||
"Colaborador (e-mail)",
|
||||
"Acessos remotos",
|
||||
"Fleet ID",
|
||||
"Equipe Fleet",
|
||||
"Fleet atualizado em",
|
||||
] as const
|
||||
type InventoryColumnDefinition = {
|
||||
key: string
|
||||
label: string
|
||||
width: number
|
||||
getValue: (machine: MachineInventoryRecord, derived: MachineDerivedData) => unknown
|
||||
}
|
||||
|
||||
const INVENTORY_COLUMN_WIDTHS = [
|
||||
22, 26, 16, 14, 10, 20, 22, 24, 28, 24, 20, 18, 14, 18, 22, 22, 24, 12, 12, 14, 26, 20, 24, 24, 18, 18, 18, 20, 20, 14, 20, 20, 18,
|
||||
18, 18, 20, 20, 20, 26, 24, 24, 26, 16, 18, 18, 20,
|
||||
] as const
|
||||
type MachineDerivedData = {
|
||||
inventory: Record<string, unknown>
|
||||
hardware: ReturnType<typeof extractHardware>
|
||||
gpuNames: string[]
|
||||
labels: string[]
|
||||
primaryIp: string | null
|
||||
publicIp: string | null
|
||||
softwareEntries: SoftwareEntry[]
|
||||
linkedUsersLabel: string | null
|
||||
systemInfo: ReturnType<typeof extractSystemInfo>
|
||||
collaborator: ReturnType<typeof extractCollaborator>
|
||||
remoteAccessCount: number
|
||||
fleetInfo: ReturnType<typeof extractFleetInfo>
|
||||
customFieldByKey: Record<string, DeviceCustomField>
|
||||
customFieldById: Record<string, DeviceCustomField>
|
||||
}
|
||||
|
||||
const COLUMN_VALUE_RESOLVERS: Record<string, (machine: MachineInventoryRecord, derived: MachineDerivedData) => unknown> = {
|
||||
displayName: (machine) => machine.displayName ?? machine.hostname,
|
||||
hostname: (machine) => machine.hostname,
|
||||
deviceType: (machine) => describeDeviceType(machine.deviceType),
|
||||
devicePlatform: (machine) => machine.devicePlatform ?? null,
|
||||
company: (machine) => machine.companyName ?? null,
|
||||
status: (machine) => describeStatus(machine.status),
|
||||
persona: (machine) => describePersona(machine.persona),
|
||||
active: (machine) => yesNo(machine.isActive),
|
||||
lastHeartbeat: (machine) => formatDateTime(machine.lastHeartbeatAt),
|
||||
assignedUser: (machine) => machine.assignedUserName ?? machine.assignedUserEmail ?? null,
|
||||
assignedEmail: (machine) => machine.assignedUserEmail ?? null,
|
||||
linkedUsers: (_machine, derived) => derived.linkedUsersLabel,
|
||||
authEmail: (machine) => machine.authEmail ?? null,
|
||||
osName: (machine) => machine.osName,
|
||||
osVersion: (machine) => machine.osVersion ?? null,
|
||||
architecture: (machine) => machine.architecture ?? null,
|
||||
hardwareVendor: (_machine, derived) => derived.hardware.vendor ?? derived.systemInfo.systemManufacturer ?? null,
|
||||
hardwareModel: (_machine, derived) => derived.hardware.model ?? derived.systemInfo.systemModel ?? null,
|
||||
hardwareSerial: (_machine, derived) => derived.hardware.serial ?? derived.systemInfo.boardSerial ?? null,
|
||||
cpu: (_machine, derived) => derived.hardware.cpuType ?? null,
|
||||
physicalCores: (_machine, derived) => derived.hardware.physicalCores ?? null,
|
||||
logicalCores: (_machine, derived) => derived.hardware.logicalCores ?? null,
|
||||
memoryGiB: (_machine, derived) => derived.hardware.memoryGiB ?? null,
|
||||
gpus: (_machine, derived) => (derived.gpuNames.length > 0 ? derived.gpuNames.join(", ") : null),
|
||||
labels: (_machine, derived) => (derived.labels.length > 0 ? derived.labels.join(", ") : null),
|
||||
macs: (machine) => (machine.macAddresses.length > 0 ? machine.macAddresses.join(", ") : null),
|
||||
serials: (machine) => (machine.serialNumbers.length > 0 ? machine.serialNumbers.join(", ") : null),
|
||||
primaryIp: (_machine, derived) => derived.primaryIp ?? null,
|
||||
publicIp: (_machine, derived) => derived.publicIp ?? null,
|
||||
registeredBy: (machine) => machine.registeredBy ?? null,
|
||||
tokenExpiresAt: (machine) => (machine.token?.expiresAt ? formatDateTime(machine.token.expiresAt) : null),
|
||||
tokenLastUsedAt: (machine) => (machine.token?.lastUsedAt ? formatDateTime(machine.token.lastUsedAt) : null),
|
||||
tokenUsageCount: (machine) => machine.token?.usageCount ?? 0,
|
||||
createdAt: (machine) => formatDateTime(machine.createdAt),
|
||||
updatedAt: (machine) => formatDateTime(machine.updatedAt),
|
||||
softwareCount: (_machine, derived) => derived.softwareEntries.length,
|
||||
osBuild: (_machine, derived) => derived.systemInfo.osBuild ?? null,
|
||||
osLicense: (_machine, derived) => derived.systemInfo.license ?? null,
|
||||
osExperience: (_machine, derived) => derived.systemInfo.experience ?? null,
|
||||
domain: (_machine, derived) => derived.systemInfo.domain ?? null,
|
||||
workgroup: (_machine, derived) => derived.systemInfo.workgroup ?? null,
|
||||
deviceName: (_machine, derived) => derived.systemInfo.deviceName ?? null,
|
||||
boardSerial: (_machine, derived) => derived.systemInfo.boardSerial ?? derived.hardware.serial ?? null,
|
||||
collaboratorName: (_machine, derived) => derived.collaborator?.name ?? null,
|
||||
collaboratorEmail: (_machine, derived) => derived.collaborator?.email ?? null,
|
||||
remoteAccessCount: (_machine, derived) => derived.remoteAccessCount,
|
||||
fleetId: (_machine, derived) => derived.fleetInfo?.id ?? null,
|
||||
fleetTeam: (_machine, derived) => derived.fleetInfo?.team ?? null,
|
||||
fleetUpdatedAt: (_machine, derived) =>
|
||||
derived.fleetInfo?.updatedAt ? formatDateTime(derived.fleetInfo.updatedAt) : null,
|
||||
managementMode: (machine) => describeManagementMode(machine.managementMode),
|
||||
}
|
||||
|
||||
const DEFAULT_COLUMN_CONFIG: DeviceInventoryColumnConfig[] = DEVICE_INVENTORY_COLUMN_METADATA.filter((meta) => meta.default !== false).map(
|
||||
(meta) => ({ key: meta.key })
|
||||
)
|
||||
|
||||
const INVENTORY_COLUMN_DEFINITIONS: InventoryColumnDefinition[] = DEVICE_INVENTORY_COLUMN_METADATA.map((meta) => ({
|
||||
key: meta.key,
|
||||
label: meta.label,
|
||||
width: meta.width,
|
||||
getValue: COLUMN_VALUE_RESOLVERS[meta.key] ?? (() => null),
|
||||
}))
|
||||
|
||||
const INVENTORY_COLUMN_MAP: Record<string, InventoryColumnDefinition> = Object.fromEntries(
|
||||
INVENTORY_COLUMN_DEFINITIONS.map((column) => [column.key, column]),
|
||||
)
|
||||
|
||||
const CUSTOM_FIELD_KEY_PREFIX = "custom:"
|
||||
const CUSTOM_FIELD_ID_PREFIX = "custom#"
|
||||
|
||||
function deriveMachineData(machine: MachineInventoryRecord): MachineDerivedData {
|
||||
const inventory = toRecord(machine.inventory) ?? {}
|
||||
const hardware = extractHardware(inventory)
|
||||
const gpuNames = extractGpuNames(inventory)
|
||||
const labels = extractLabels(inventory)
|
||||
const primaryIp = extractPrimaryIp(inventory)
|
||||
const publicIp = extractPublicIp(inventory)
|
||||
const softwareEntries = extractSoftwareEntries(machine.hostname, inventory)
|
||||
const linkedUsersLabel = summarizeLinkedUsers(machine.linkedUsers)
|
||||
const systemInfo = extractSystemInfo(inventory)
|
||||
const collaborator = extractCollaborator(machine, inventory)
|
||||
const remoteAccessCount = collectRemoteAccessEntries(machine).length
|
||||
const fleetInfo = extractFleetInfo(inventory)
|
||||
const customFieldByKey: Record<string, DeviceCustomField> = {}
|
||||
const customFieldById: Record<string, DeviceCustomField> = {}
|
||||
for (const field of machine.customFields ?? []) {
|
||||
customFieldByKey[field.fieldKey] = field
|
||||
customFieldById[String(field.fieldId)] = field
|
||||
}
|
||||
return {
|
||||
inventory,
|
||||
hardware,
|
||||
gpuNames,
|
||||
labels,
|
||||
primaryIp,
|
||||
publicIp,
|
||||
softwareEntries,
|
||||
linkedUsersLabel,
|
||||
systemInfo,
|
||||
collaborator,
|
||||
remoteAccessCount,
|
||||
fleetInfo,
|
||||
customFieldByKey,
|
||||
customFieldById,
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeColumnConfig(columns?: DeviceInventoryColumnConfig[]): DeviceInventoryColumnConfig[] {
|
||||
if (!columns || columns.length === 0) {
|
||||
return [...DEFAULT_COLUMN_CONFIG]
|
||||
}
|
||||
const seen = new Set<string>()
|
||||
const normalized: DeviceInventoryColumnConfig[] = []
|
||||
for (const column of columns) {
|
||||
const key = column.key.trim()
|
||||
if (!key) continue
|
||||
if (seen.has(key)) continue
|
||||
if (!isCustomFieldKey(key) && !INVENTORY_COLUMN_MAP[key]) continue
|
||||
seen.add(key)
|
||||
normalized.push({
|
||||
key,
|
||||
label: column.label?.trim() || undefined,
|
||||
})
|
||||
}
|
||||
return normalized.length > 0 ? normalized : [...DEFAULT_COLUMN_CONFIG]
|
||||
}
|
||||
|
||||
function resolveColumnLabel(column: DeviceInventoryColumnConfig, derivedList: MachineDerivedData[]): string {
|
||||
if (column.label) return column.label
|
||||
const definition = INVENTORY_COLUMN_MAP[column.key]
|
||||
if (definition) return definition.label
|
||||
if (isCustomFieldKey(column.key)) {
|
||||
for (const derived of derivedList) {
|
||||
const field = lookupCustomField(column.key, derived)
|
||||
if (field) return field.label
|
||||
}
|
||||
return "Campo personalizado"
|
||||
}
|
||||
return column.key
|
||||
}
|
||||
|
||||
function resolveColumnWidth(key: string): number {
|
||||
const definition = INVENTORY_COLUMN_MAP[key]
|
||||
if (definition) return definition.width
|
||||
if (isCustomFieldKey(key)) return 28
|
||||
return 20
|
||||
}
|
||||
|
||||
function isCustomFieldKey(key: string): boolean {
|
||||
return key.startsWith(CUSTOM_FIELD_KEY_PREFIX) || key.startsWith(CUSTOM_FIELD_ID_PREFIX)
|
||||
}
|
||||
|
||||
function lookupCustomField(key: string, derived: MachineDerivedData): DeviceCustomField | null {
|
||||
if (key.startsWith(CUSTOM_FIELD_KEY_PREFIX)) {
|
||||
const fieldKey = key.slice(CUSTOM_FIELD_KEY_PREFIX.length)
|
||||
return derived.customFieldByKey[fieldKey] ?? null
|
||||
}
|
||||
if (key.startsWith(CUSTOM_FIELD_ID_PREFIX)) {
|
||||
const fieldId = key.slice(CUSTOM_FIELD_ID_PREFIX.length)
|
||||
return derived.customFieldById[fieldId] ?? null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function formatCustomFieldValue(field: DeviceCustomField): string {
|
||||
if (field.value === null || field.value === undefined) {
|
||||
return "—"
|
||||
}
|
||||
if (field.displayValue !== undefined && field.displayValue !== null) {
|
||||
const text = String(field.displayValue).trim()
|
||||
if (text.length > 0) return text
|
||||
}
|
||||
switch (field.type) {
|
||||
case "boolean":
|
||||
return yesNo(Boolean(field.value))
|
||||
case "number": {
|
||||
const num = typeof field.value === "number" ? field.value : Number(field.value)
|
||||
return Number.isFinite(num) ? String(num) : String(field.value)
|
||||
}
|
||||
case "date": {
|
||||
const date = new Date(field.value as string | number)
|
||||
if (Number.isNaN(date.getTime())) return String(field.value)
|
||||
return date.toISOString().slice(0, 10)
|
||||
}
|
||||
default:
|
||||
return String(field.value)
|
||||
}
|
||||
}
|
||||
|
||||
function resolveColumnValue(
|
||||
key: string,
|
||||
machine: MachineInventoryRecord,
|
||||
derived: MachineDerivedData
|
||||
): unknown {
|
||||
if (isCustomFieldKey(key)) {
|
||||
const field = lookupCustomField(key, derived)
|
||||
if (!field) return "—"
|
||||
return formatCustomFieldValue(field)
|
||||
}
|
||||
const definition = INVENTORY_COLUMN_MAP[key]
|
||||
if (!definition) return "—"
|
||||
return definition.getValue(machine, derived)
|
||||
}
|
||||
|
||||
function formatInventoryCell(value: unknown): unknown {
|
||||
if (value === null || value === undefined) return "—"
|
||||
if (typeof value === "string") {
|
||||
const trimmed = value.trim()
|
||||
return trimmed.length > 0 ? trimmed : "—"
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
function describeDeviceType(type: string | null | undefined): string {
|
||||
const normalized = (type ?? "").toLowerCase()
|
||||
switch (normalized) {
|
||||
case "desktop":
|
||||
return "Desktop"
|
||||
case "mobile":
|
||||
return "Celular"
|
||||
case "tablet":
|
||||
return "Tablet"
|
||||
default:
|
||||
return "Desconhecido"
|
||||
}
|
||||
}
|
||||
|
||||
function describeManagementMode(mode: string | null | undefined): string {
|
||||
const normalized = (mode ?? "").toLowerCase()
|
||||
switch (normalized) {
|
||||
case "agent":
|
||||
return "Agente"
|
||||
case "manual":
|
||||
return "Manual"
|
||||
default:
|
||||
return "—"
|
||||
}
|
||||
}
|
||||
|
||||
const SOFTWARE_HEADERS = ["Hostname", "Aplicativo", "Versão", "Origem", "Publicador", "Instalado em"] as const
|
||||
const SOFTWARE_COLUMN_WIDTHS = [22, 36, 18, 18, 22, 20] as const
|
||||
|
|
@ -173,7 +401,7 @@ const STATUS_LABELS: Record<string, string> = {
|
|||
const PERSONA_LABELS: Record<string, string> = {
|
||||
collaborator: "Colaborador",
|
||||
manager: "Gestor",
|
||||
machine: "Máquina",
|
||||
machine: "Dispositivo",
|
||||
}
|
||||
|
||||
const SUMMARY_STATUS_ORDER = ["Online", "Sem sinal", "Offline", "Manutenção", "Bloqueada", "Desativada", "Desconhecido"]
|
||||
|
|
@ -186,7 +414,14 @@ export function buildMachinesInventoryWorkbook(
|
|||
): Buffer {
|
||||
const generatedAt = options.generatedAt ?? new Date()
|
||||
const summaryRows = buildSummaryRows(machines, options, generatedAt)
|
||||
const inventoryRows = machines.map((machine) => flattenMachine(machine))
|
||||
const columnConfig = normalizeColumnConfig(options.columns)
|
||||
const derivedList = machines.map((machine) => deriveMachineData(machine))
|
||||
const headers = columnConfig.map((column) => resolveColumnLabel(column, derivedList))
|
||||
const columnWidths = columnConfig.map((column) => resolveColumnWidth(column.key))
|
||||
const inventoryRows = machines.map((machine, index) => {
|
||||
const derived = derivedList[index]
|
||||
return columnConfig.map((column) => formatInventoryCell(resolveColumnValue(column.key, machine, derived)))
|
||||
})
|
||||
const linksRows = buildLinkedUsersRows(machines)
|
||||
const softwareRows = buildSoftwareRows(machines)
|
||||
const partitionRows = buildPartitionRows(machines)
|
||||
|
|
@ -210,9 +445,9 @@ export function buildMachinesInventoryWorkbook(
|
|||
|
||||
sheets.push({
|
||||
name: "Inventário",
|
||||
headers: [...INVENTORY_HEADERS],
|
||||
headers,
|
||||
rows: inventoryRows,
|
||||
columnWidths: [...INVENTORY_COLUMN_WIDTHS],
|
||||
columnWidths,
|
||||
freezePane: { rowSplit: 1 },
|
||||
autoFilter: true,
|
||||
})
|
||||
|
|
@ -353,11 +588,28 @@ function buildSummaryRows(
|
|||
rows.push(["Filtro de empresa", options.companyFilterLabel])
|
||||
}
|
||||
|
||||
rows.push(["Total de máquinas", machines.length])
|
||||
rows.push(["Total de dispositivos", machines.length])
|
||||
|
||||
const activeCount = machines.filter((machine) => machine.isActive).length
|
||||
rows.push(["Máquinas ativas", activeCount])
|
||||
rows.push(["Máquinas inativas", machines.length - activeCount])
|
||||
rows.push(["Dispositivos ativos", activeCount])
|
||||
rows.push(["Dispositivos inativos", machines.length - activeCount])
|
||||
|
||||
const typeCounts = new Map<string, number>()
|
||||
machines.forEach((machine) => {
|
||||
const label = describeDeviceType(machine.deviceType)
|
||||
typeCounts.set(label, (typeCounts.get(label) ?? 0) + 1)
|
||||
})
|
||||
const DEVICE_TYPE_ORDER = ["Desktop", "Celular", "Tablet", "Desconhecido"]
|
||||
Array.from(typeCounts.entries())
|
||||
.sort((a, b) => {
|
||||
const idxA = DEVICE_TYPE_ORDER.indexOf(a[0])
|
||||
const idxB = DEVICE_TYPE_ORDER.indexOf(b[0])
|
||||
if (idxA === -1 && idxB === -1) return a[0].localeCompare(b[0], "pt-BR")
|
||||
if (idxA === -1) return 1
|
||||
if (idxB === -1) return -1
|
||||
return idxA - idxB
|
||||
})
|
||||
.forEach(([label, total]) => rows.push([`Tipo: ${label}`, total]))
|
||||
|
||||
const statusCounts = new Map<string, number>()
|
||||
machines.forEach((machine) => {
|
||||
|
|
@ -396,70 +648,6 @@ function buildSummaryRows(
|
|||
return rows
|
||||
}
|
||||
|
||||
function flattenMachine(machine: MachineInventoryRecord): WorksheetRow {
|
||||
const inventory = toRecord(machine.inventory)
|
||||
const hardware = extractHardware(inventory)
|
||||
const gpuNames = extractGpuNames(inventory)
|
||||
const labels = extractLabels(inventory)
|
||||
const primaryIp = extractPrimaryIp(inventory)
|
||||
const publicIp = extractPublicIp(inventory)
|
||||
const softwareEntries = extractSoftwareEntries(machine.hostname, inventory)
|
||||
const linkedUsers = summarizeLinkedUsers(machine.linkedUsers)
|
||||
const systemInfo = extractSystemInfo(inventory)
|
||||
const collaborator = extractCollaborator(machine, inventory)
|
||||
const remoteAccessCount = collectRemoteAccessEntries(machine).length
|
||||
const fleetInfo = extractFleetInfo(inventory)
|
||||
|
||||
return [
|
||||
machine.hostname,
|
||||
machine.companyName ?? "—",
|
||||
describeStatus(machine.status),
|
||||
describePersona(machine.persona),
|
||||
yesNo(machine.isActive),
|
||||
formatDateTime(machine.lastHeartbeatAt),
|
||||
machine.assignedUserName ?? machine.assignedUserEmail ?? "—",
|
||||
machine.assignedUserEmail ?? "—",
|
||||
linkedUsers ?? "—",
|
||||
machine.authEmail ?? "—",
|
||||
machine.osName,
|
||||
machine.osVersion ?? "—",
|
||||
machine.architecture ?? "—",
|
||||
hardware.vendor ?? systemInfo.systemManufacturer ?? "—",
|
||||
hardware.model ?? systemInfo.systemModel ?? "—",
|
||||
hardware.serial ?? systemInfo.boardSerial ?? "—",
|
||||
hardware.cpuType ?? "—",
|
||||
hardware.physicalCores ?? "—",
|
||||
hardware.logicalCores ?? "—",
|
||||
hardware.memoryGiB ?? "—",
|
||||
gpuNames.length > 0 ? gpuNames.join(", ") : "—",
|
||||
labels.length > 0 ? labels.join(", ") : "—",
|
||||
machine.macAddresses.length > 0 ? machine.macAddresses.join(", ") : "—",
|
||||
machine.serialNumbers.length > 0 ? machine.serialNumbers.join(", ") : "—",
|
||||
primaryIp ?? "—",
|
||||
publicIp ?? "—",
|
||||
machine.registeredBy ?? "—",
|
||||
machine.token?.expiresAt ? formatDateTime(machine.token.expiresAt) ?? "—" : "—",
|
||||
machine.token?.lastUsedAt ? formatDateTime(machine.token.lastUsedAt) ?? "—" : "—",
|
||||
machine.token?.usageCount ?? 0,
|
||||
formatDateTime(machine.createdAt) ?? "—",
|
||||
formatDateTime(machine.updatedAt) ?? "—",
|
||||
softwareEntries.length,
|
||||
systemInfo.osBuild ?? "—",
|
||||
systemInfo.license ?? "—",
|
||||
systemInfo.experience ?? "—",
|
||||
systemInfo.domain ?? "—",
|
||||
systemInfo.workgroup ?? "—",
|
||||
systemInfo.deviceName ?? "—",
|
||||
systemInfo.boardSerial ?? hardware.serial ?? "—",
|
||||
collaborator?.name ?? "—",
|
||||
collaborator?.email ?? "—",
|
||||
remoteAccessCount,
|
||||
fleetInfo?.id ?? "—",
|
||||
fleetInfo?.team ?? "—",
|
||||
fleetInfo?.updatedAt ? formatDateTime(fleetInfo.updatedAt) ?? "—" : "—",
|
||||
]
|
||||
}
|
||||
|
||||
function buildLinkedUsersRows(machines: MachineInventoryRecord[]): Array<[string, string | null, string | null, string | null]> {
|
||||
const rows: Array<[string, string | null, string | null, string | null]> = []
|
||||
machines.forEach((machine) => {
|
||||
|
|
|
|||
|
|
@ -408,7 +408,7 @@ function TicketPdfDocument({ ticket, logoDataUrl }: { ticket: TicketWithDetails;
|
|||
]
|
||||
if (ticket.machine) {
|
||||
const machineLabel = ticket.machine.hostname ?? (ticket.machine.id ? `ID ${ticket.machine.id}` : "—")
|
||||
rightMeta.push({ label: "Máquina", value: machineLabel })
|
||||
rightMeta.push({ label: "Dispositivo", value: machineLabel })
|
||||
}
|
||||
if (ticket.resolvedAt) {
|
||||
rightMeta.push({ label: "Resolvido em", value: formatDateTime(ticket.resolvedAt) })
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue