import { LitElement, html, css } from "lit"; import { customElement, property, state } from "lit/decorators.js"; import { when } from "lit/directives/when.js"; import { msg, localized, str } from "@lit/localize"; import { truncate } from "../utils/css"; import type { APIPaginatedList } from "../types/api"; type CrawlLog = { timestamp: string; logLevel: "error"; details: Record; context: string; message: string; }; @localized() @customElement("btrix-crawl-logs") export class CrawlLogs extends LitElement { static styles = [ truncate, css` btrix-numbered-list { font-size: var(--sl-font-size-x-small); } .row { display: grid; grid-template-columns: 9rem 4rem 15rem 1fr; line-height: 1.3; } .cell { padding-left: var(--sl-spacing-x-small); padding-right: var(--sl-spacing-x-small); } .tag { display: inline-block; border-radius: var(--sl-border-radius-small); padding: var(--sl-spacing-3x-small) var(--sl-spacing-2x-small); text-transform: capitalize; /* TODO handle non-errors */ background-color: var(--danger); color: var(--sl-color-neutral-0); } footer { display: flex; justify-content: center; margin-top: var(--sl-spacing-large); margin-bottom: var(--sl-spacing-x-large); } .message { white-space: pre-wrap; } .url { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } a { color: inherit; } a:hover { text-decoration: none; } pre { white-space: pre-wrap; font-family: var(--sl-font-mono); font-size: var(--sl-font-size-x-small); margin: 0; padding: var(--sl-spacing-small); border: 1px solid var(--sl-panel-border-color); border-radius: var(--sl-border-radius-medium); } `, ]; @property({ type: Object }) logs?: APIPaginatedList; @property({ type: Boolean }) paginate = false; @state() private selectedLog: | (CrawlLog & { index: number; }) | null = null; render() { if (!this.logs) return; return html`
${msg("Date")}
${msg("Level")}
${msg("Error Message")}
${msg("Page URL")}
${this.logs.items.map((log: CrawlLog, idx) => { const selected = this.selectedLog?.index === idx; return html` { this.selectedLog = { index: idx, ...log, }; }} >
${idx + 1}.
${log.logLevel}
${log.message}
`; })}
${this.paginate ? html`` : ""} (this.selectedLog = null)} >${this.renderLogDetails()} `; } private renderLogDetails() { if (!this.selectedLog) return; const { details } = this.selectedLog; return html` ${this.selectedLog.timestamp} ${Object.entries(details).map( ([key, value]) => html` ${key === "stack" || (typeof value !== "string" && typeof value !== "number") ? this.renderPre(value) : value ?? "--"} ` )} `; } private renderPre(value: any) { let str = value; if (typeof value !== "string") { str = JSON.stringify(value, null, 2); } return html`
${str}
`; } }