330 lines
9.5 KiB
TypeScript
330 lines
9.5 KiB
TypeScript
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
|
import "@material/mwc-button";
|
|
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
|
import { customElement, property, state } from "lit/decorators";
|
|
|
|
import { createCloseHeading } from "../../../components/ha-dialog";
|
|
import "../../../components/ha-formfield";
|
|
import "../../../components/ha-help-tooltip";
|
|
import "../../../components/ha-label";
|
|
import "../../../components/ha-svg-icon";
|
|
import "../../../components/ha-switch";
|
|
import "../../../components/ha-textfield";
|
|
import {
|
|
computeUserBadges,
|
|
SYSTEM_GROUP_ID_ADMIN,
|
|
SYSTEM_GROUP_ID_USER,
|
|
} from "../../../data/user";
|
|
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
|
import { haStyleDialog } from "../../../resources/styles";
|
|
import { HomeAssistant } from "../../../types";
|
|
import { showAdminChangePasswordDialog } from "./show-dialog-admin-change-password";
|
|
import { UserDetailDialogParams } from "./show-dialog-user-detail";
|
|
|
|
@customElement("dialog-user-detail")
|
|
class DialogUserDetail extends LitElement {
|
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
|
|
|
@state() private _name!: string;
|
|
|
|
@state() private _isAdmin?: boolean;
|
|
|
|
@state() private _localOnly?: boolean;
|
|
|
|
@state() private _isActive?: boolean;
|
|
|
|
@state() private _error?: string;
|
|
|
|
@state() private _params?: UserDetailDialogParams;
|
|
|
|
@state() private _submitting = false;
|
|
|
|
public async showDialog(params: UserDetailDialogParams): Promise<void> {
|
|
this._params = params;
|
|
this._error = undefined;
|
|
this._name = params.entry.name || "";
|
|
this._isAdmin = params.entry.group_ids.includes(SYSTEM_GROUP_ID_ADMIN);
|
|
this._localOnly = params.entry.local_only;
|
|
this._isActive = params.entry.is_active;
|
|
await this.updateComplete;
|
|
}
|
|
|
|
protected render() {
|
|
if (!this._params) {
|
|
return nothing;
|
|
}
|
|
const user = this._params.entry;
|
|
const badges = computeUserBadges(this.hass, user, true);
|
|
return html`
|
|
<ha-dialog
|
|
open
|
|
@closed=${this._close}
|
|
scrimClickAction
|
|
escapeKeyAction
|
|
.heading=${createCloseHeading(this.hass, user.name)}
|
|
>
|
|
<div>
|
|
${this._error ? html` <div class="error">${this._error}</div> ` : ""}
|
|
<div class="secondary">
|
|
${this.hass.localize("ui.panel.config.users.editor.id")}:
|
|
${user.id}<br />
|
|
${this.hass.localize("ui.panel.config.users.editor.username")}:
|
|
${user.username}
|
|
</div>
|
|
${badges.length === 0
|
|
? ""
|
|
: html`
|
|
<div class="badge-container">
|
|
${badges.map(
|
|
([icon, label]) => html`
|
|
<ha-label>
|
|
<ha-svg-icon slot="icon" .path=${icon}></ha-svg-icon>
|
|
${label}
|
|
</ha-label>
|
|
`
|
|
)}
|
|
</div>
|
|
`}
|
|
<div class="form">
|
|
<ha-textfield
|
|
dialogInitialFocus
|
|
.value=${this._name}
|
|
.disabled=${user.system_generated}
|
|
@input=${this._nameChanged}
|
|
.label=${this.hass!.localize("ui.panel.config.users.editor.name")}
|
|
></ha-textfield>
|
|
<div class="row">
|
|
<ha-formfield
|
|
.label=${this.hass.localize(
|
|
"ui.panel.config.users.editor.local_only"
|
|
)}
|
|
>
|
|
<ha-switch
|
|
.disabled=${user.system_generated}
|
|
.checked=${this._localOnly}
|
|
@change=${this._localOnlyChanged}
|
|
>
|
|
</ha-switch>
|
|
</ha-formfield>
|
|
</div>
|
|
<div class="row">
|
|
<ha-formfield
|
|
.label=${this.hass.localize(
|
|
"ui.panel.config.users.editor.admin"
|
|
)}
|
|
>
|
|
<ha-switch
|
|
.disabled=${user.system_generated || user.is_owner}
|
|
.checked=${this._isAdmin}
|
|
@change=${this._adminChanged}
|
|
>
|
|
</ha-switch>
|
|
</ha-formfield>
|
|
</div>
|
|
${!this._isAdmin
|
|
? html`
|
|
<br />
|
|
${this.hass.localize(
|
|
"ui.panel.config.users.users_privileges_note"
|
|
)}
|
|
`
|
|
: ""}
|
|
<div class="row">
|
|
<ha-formfield
|
|
.label=${this.hass.localize(
|
|
"ui.panel.config.users.editor.active"
|
|
)}
|
|
>
|
|
<ha-switch
|
|
.disabled=${user.system_generated || user.is_owner}
|
|
.checked=${this._isActive}
|
|
@change=${this._activeChanged}
|
|
>
|
|
</ha-switch>
|
|
</ha-formfield>
|
|
<ha-help-tooltip
|
|
.label=${this.hass.localize(
|
|
"ui.panel.config.users.editor.active_tooltip"
|
|
)}
|
|
>
|
|
</ha-help-tooltip>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div slot="secondaryAction">
|
|
<mwc-button
|
|
class="warning"
|
|
@click=${this._deleteEntry}
|
|
.disabled=${this._submitting ||
|
|
user.system_generated ||
|
|
user.is_owner}
|
|
>
|
|
${this.hass!.localize("ui.panel.config.users.editor.delete_user")}
|
|
</mwc-button>
|
|
${user.system_generated
|
|
? html`
|
|
<simple-tooltip animation-delay="0" position="right">
|
|
${this.hass.localize(
|
|
"ui.panel.config.users.editor.system_generated_users_not_removable"
|
|
)}
|
|
</simple-tooltip>
|
|
`
|
|
: ""}
|
|
${!user.system_generated && this.hass.user?.is_owner
|
|
? html`<mwc-button @click=${this._changePassword}>
|
|
${this.hass.localize(
|
|
"ui.panel.config.users.editor.change_password"
|
|
)}
|
|
</mwc-button>`
|
|
: ""}
|
|
</div>
|
|
|
|
<div slot="primaryAction">
|
|
<mwc-button
|
|
@click=${this._updateEntry}
|
|
.disabled=${!this._name ||
|
|
this._submitting ||
|
|
user.system_generated}
|
|
>
|
|
${this.hass!.localize("ui.panel.config.users.editor.update_user")}
|
|
</mwc-button>
|
|
${user.system_generated
|
|
? html`
|
|
<simple-tooltip animation-delay="0" position="left">
|
|
${this.hass.localize(
|
|
"ui.panel.config.users.editor.system_generated_users_not_editable"
|
|
)}
|
|
</simple-tooltip>
|
|
`
|
|
: ""}
|
|
</div>
|
|
</ha-dialog>
|
|
`;
|
|
}
|
|
|
|
private _nameChanged(ev) {
|
|
this._error = undefined;
|
|
this._name = ev.target.value;
|
|
}
|
|
|
|
private _adminChanged(ev): void {
|
|
this._isAdmin = ev.target.checked;
|
|
}
|
|
|
|
private _localOnlyChanged(ev): void {
|
|
this._localOnly = ev.target.checked;
|
|
}
|
|
|
|
private _activeChanged(ev): void {
|
|
this._isActive = ev.target.checked;
|
|
}
|
|
|
|
private async _updateEntry() {
|
|
this._submitting = true;
|
|
try {
|
|
await this._params!.updateEntry({
|
|
name: this._name.trim(),
|
|
is_active: this._isActive,
|
|
group_ids: [
|
|
this._isAdmin ? SYSTEM_GROUP_ID_ADMIN : SYSTEM_GROUP_ID_USER,
|
|
],
|
|
local_only: this._localOnly,
|
|
});
|
|
this._close();
|
|
} catch (err: any) {
|
|
this._error = err?.message || "Unknown error";
|
|
} finally {
|
|
this._submitting = false;
|
|
}
|
|
}
|
|
|
|
private async _deleteEntry() {
|
|
this._submitting = true;
|
|
try {
|
|
if (await this._params!.removeEntry()) {
|
|
this._params = undefined;
|
|
}
|
|
} finally {
|
|
this._submitting = false;
|
|
}
|
|
}
|
|
|
|
private async _changePassword() {
|
|
const credential = this._params?.entry.credentials.find(
|
|
(cred) => cred.type === "homeassistant"
|
|
);
|
|
if (!credential) {
|
|
showAlertDialog(this, {
|
|
title: "No Home Assistant credentials found.",
|
|
});
|
|
return;
|
|
}
|
|
|
|
showAdminChangePasswordDialog(this, { userId: this._params!.entry.id });
|
|
}
|
|
|
|
private _close(): void {
|
|
this._params = undefined;
|
|
}
|
|
|
|
static get styles(): CSSResultGroup {
|
|
return [
|
|
haStyleDialog,
|
|
css`
|
|
ha-dialog {
|
|
--mdc-dialog-max-width: 500px;
|
|
}
|
|
.form {
|
|
padding-top: 16px;
|
|
}
|
|
.secondary {
|
|
color: var(--secondary-text-color);
|
|
}
|
|
ha-textfield {
|
|
display: block;
|
|
}
|
|
.badge-container {
|
|
margin-top: 4px;
|
|
}
|
|
.badge-container > * {
|
|
margin-top: 4px;
|
|
margin-bottom: 4px;
|
|
margin-right: 4px;
|
|
margin-left: 0;
|
|
margin-inline-end: 4px;
|
|
margin-inline-start: 0;
|
|
}
|
|
.state {
|
|
background-color: rgba(var(--rgb-primary-text-color), 0.15);
|
|
border-radius: 16px;
|
|
padding: 4px 8px;
|
|
margin-top: 8px;
|
|
display: inline-block;
|
|
}
|
|
.state:not(:first-child) {
|
|
margin-left: 8px;
|
|
margin-inline-start: 8px;
|
|
margin-inline-end: initial;
|
|
}
|
|
.row {
|
|
display: flex;
|
|
padding: 8px 0;
|
|
}
|
|
ha-help-tooltip {
|
|
margin-left: 4px;
|
|
margin-inline-start: 4px;
|
|
margin-inline-end: initial;
|
|
position: relative;
|
|
}
|
|
`,
|
|
];
|
|
}
|
|
}
|
|
|
|
declare global {
|
|
interface HTMLElementTagNameMap {
|
|
"dialog-user-detail": DialogUserDetail;
|
|
}
|
|
}
|