frontend/src/panels/config/logs/dialog-system-log-detail.ts
Wendelin a7b1c45c00
Replace simple-tooltip with ha-tooltip (#24384)
* Start with simple-tooltip migration

* Remove simple-tooltip

* Fix tooltip in hassio-repositories

* Remove space

* Update hassio/src/dialogs/repositories/dialog-hassio-repositories.ts

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

* Update src/components/ha-icon-overflow-menu.ts

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

* Update src/components/ha-target-picker.ts

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

* Update src/components/media-player/ha-media-player-browse.ts

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

* Update src/components/ha-target-picker.ts

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

* Fix content props

* Use ha-tooltip in data-table-icon

* Update src/panels/config/areas/ha-config-area-page.ts

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

* Update src/panels/config/devices/ha-config-device-page.ts

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

* Update src/panels/config/integrations/ha-integration-card.ts

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

* Update src/panels/config/integrations/ha-integration-card.ts

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

* Update src/panels/config/integrations/ha-integration-list-item.ts

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

* Update src/panels/config/integrations/ha-integration-list-item.ts

Co-authored-by: Bram Kragten <mail@bramkragten.nl>

---------

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2025-02-25 14:58:38 +01:00

275 lines
8.6 KiB
TypeScript

import { mdiClose, mdiContentCopy } from "@mdi/js";
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { property, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import { copyToClipboard } from "../../../common/util/copy-clipboard";
import "../../../components/ha-alert";
import "../../../components/ha-dialog";
import "../../../components/ha-dialog-header";
import "../../../components/ha-icon-button";
import "../../../components/ha-svg-icon";
import type { IntegrationManifest } from "../../../data/integration";
import {
domainToName,
fetchIntegrationManifest,
integrationIssuesUrl,
} from "../../../data/integration";
import {
getLoggedErrorIntegration,
isCustomIntegrationError,
} from "../../../data/system_log";
import { haStyleDialog } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
import { documentationUrl } from "../../../util/documentation-url";
import { showToast } from "../../../util/toast";
import type { SystemLogDetailDialogParams } from "./show-dialog-system-log-detail";
import { formatSystemLogTime } from "./util";
class DialogSystemLogDetail extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _params?: SystemLogDetailDialogParams;
@state() private _manifest?: IntegrationManifest;
public async showDialog(params: SystemLogDetailDialogParams): Promise<void> {
this._params = params;
this._manifest = undefined;
await this.updateComplete;
}
public closeDialog() {
this._params = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
protected updated(changedProps) {
super.updated(changedProps);
if (!changedProps.has("_params") || !this._params) {
return;
}
const integration = getLoggedErrorIntegration(this._params.item);
if (integration) {
this._fetchManifest(integration);
}
}
protected render() {
if (!this._params) {
return nothing;
}
const item = this._params.item;
const integration = getLoggedErrorIntegration(item);
const showDocumentation =
this._manifest &&
(this._manifest.is_built_in ||
// Custom components with our official docs should not link to our docs
!this._manifest.documentation.includes("://www.home-assistant.io"));
const title = this.hass.localize("ui.panel.config.logs.details", {
level: html`<span class=${item.level}
>${this.hass.localize(`ui.panel.config.logs.level.${item.level}`)}</span
>`,
});
return html`
<ha-dialog open @closed=${this.closeDialog} hideActions .heading=${title}>
<ha-dialog-header slot="heading">
<ha-icon-button
slot="navigationIcon"
dialogAction="cancel"
.label=${this.hass.localize("ui.common.close")}
.path=${mdiClose}
></ha-icon-button>
<span slot="title">${title}</span>
<ha-icon-button
id="copy"
@click=${this._copyLog}
slot="actionItems"
.label=${this.hass.localize("ui.panel.config.logs.copy")}
.path=${mdiContentCopy}
></ha-icon-button>
</ha-dialog-header>
${this.isCustomIntegration
? html`<ha-alert alert-type="warning">
${this.hass.localize(
"ui.panel.config.logs.error_from_custom_integration"
)}
</ha-alert>`
: ""}
<div class="contents" tabindex="-1" dialogInitialFocus>
<p>
${this.hass.localize("ui.panel.config.logs.detail.logger")}:
${item.name}<br />
${this.hass.localize("ui.panel.config.logs.detail.source")}:
${item.source.join(":")}
${integration
? html`
<br />
${this.hass.localize(
"ui.panel.config.logs.detail.integration"
)}:
${domainToName(this.hass.localize, integration)}
${!this._manifest ||
// Can happen with custom integrations
!showDocumentation
? ""
: html`
(<a
href=${this._manifest.is_built_in
? documentationUrl(
this.hass,
`/integrations/${this._manifest.domain}`
)
: this._manifest.documentation}
target="_blank"
rel="noreferrer"
>${this.hass.localize(
"ui.panel.config.logs.detail.documentation"
)}</a
>${this._manifest.is_built_in ||
this._manifest.issue_tracker
? html`,
<a
href=${integrationIssuesUrl(
integration,
this._manifest
)}
target="_blank"
rel="noreferrer"
>${this.hass.localize(
"ui.panel.config.logs.detail.issues"
)}</a
>`
: ""})
`}
`
: ""}
<br />
${item.count > 0
? html`
${this.hass.localize(
"ui.panel.config.logs.detail.first_occurred"
)}:
${formatSystemLogTime(
item.first_occurred,
this.hass!.locale,
this.hass!.config
)}
(${item.count}
${this.hass.localize(
"ui.panel.config.logs.detail.occurrences"
)}) <br />
`
: ""}
${this.hass.localize("ui.panel.config.logs.detail.last_logged")}:
${formatSystemLogTime(
item.timestamp,
this.hass!.locale,
this.hass!.config
)}
</p>
${item.message.length > 1
? html`
<ul>
${item.message.map((msg) => html` <li>${msg}</li> `)}
</ul>
`
: item.message[0]}
${item.exception ? html` <pre>${item.exception}</pre> ` : nothing}
</div>
</ha-dialog>
`;
}
private get isCustomIntegration(): boolean {
return this._manifest
? !this._manifest.is_built_in
: isCustomIntegrationError(this._params!.item);
}
private async _fetchManifest(integration: string) {
try {
this._manifest = await fetchIntegrationManifest(this.hass, integration);
} catch (_err: any) {
// Ignore if loading manifest fails. Probably bad JSON in manifest
}
}
private async _copyLog(): Promise<void> {
const copyElement = this.shadowRoot?.querySelector(
".contents"
) as HTMLElement;
let text = copyElement.innerText;
if (this.isCustomIntegration) {
text =
this.hass.localize(
"ui.panel.config.logs.error_from_custom_integration"
) +
"\n\n" +
text;
}
await copyToClipboard(text);
showToast(this, {
message: this.hass.localize("ui.common.copied_clipboard"),
});
}
static get styles(): CSSResultGroup {
return [
haStyleDialog,
css`
ha-dialog {
--dialog-content-padding: 0px;
}
a {
color: var(--primary-color);
}
p {
margin-top: 0;
}
pre {
margin-bottom: 0;
font-family: var(--code-font-family, monospace);
}
ha-alert {
display: block;
margin: -4px 0;
}
.contents {
padding: 16px;
outline: none;
direction: ltr;
}
.error {
color: var(--error-color);
}
.warning {
color: var(--warning-color);
}
@media all and (min-width: 451px) and (min-height: 501px) {
ha-dialog {
--mdc-dialog-max-width: 90vw;
}
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"dialog-system-log-detail": DialogSystemLogDetail;
}
}
customElements.define("dialog-system-log-detail", DialogSystemLogDetail);