home-assistant-frontend/src/panels/config/users/dialog-add-user.ts

312 lines
8.4 KiB
TypeScript

import "@material/mwc-button";
import "@polymer/paper-input/paper-input";
import {
css,
CSSResultGroup,
html,
LitElement,
PropertyValues,
nothing,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import "../../../components/ha-circular-progress";
import { createCloseHeading } from "../../../components/ha-dialog";
import "../../../components/ha-formfield";
import "../../../components/ha-switch";
import { createAuthForUser } from "../../../data/auth";
import {
createUser,
deleteUser,
SYSTEM_GROUP_ID_ADMIN,
SYSTEM_GROUP_ID_USER,
User,
} from "../../../data/user";
import { ValueChangedEvent, HomeAssistant } from "../../../types";
import { haStyleDialog } from "../../../resources/styles";
import { AddUserDialogParams } from "./show-dialog-add-user";
@customElement("dialog-add-user")
export class DialogAddUser extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _loading = false;
// Error message when can't talk to server etc
@state() private _error?: string;
@state() private _params?: AddUserDialogParams;
@state() private _name?: string;
@state() private _username?: string;
@state() private _password?: string;
@state() private _passwordConfirm?: string;
@state() private _isAdmin?: boolean;
@state() private _localOnly?: boolean;
@state() private _allowChangeName = true;
public showDialog(params: AddUserDialogParams) {
this._params = params;
this._name = this._params.name || "";
this._username = "";
this._password = "";
this._passwordConfirm = "";
this._isAdmin = false;
this._localOnly = false;
this._error = undefined;
this._loading = false;
if (this._params.name) {
this._allowChangeName = false;
this._maybePopulateUsername();
} else {
this._allowChangeName = true;
}
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
this.addEventListener("keypress", (ev) => {
if (ev.key === "Enter") {
this._createUser(ev);
}
});
}
protected render() {
if (!this._params) {
return nothing;
}
return html`
<ha-dialog
open
@closed=${this._close}
scrimClickAction
escapeKeyAction
.heading=${createCloseHeading(
this.hass,
this.hass.localize("ui.panel.config.users.add_user.caption")
)}
>
<div>
${this._error ? html` <div class="error">${this._error}</div> ` : ""}
${this._allowChangeName
? html` <paper-input
class="name"
name="name"
.label=${this.hass.localize(
"ui.panel.config.users.editor.name"
)}
.value=${this._name}
required
auto-validate
autocapitalize="on"
.errorMessage=${this.hass.localize("ui.common.error_required")}
@value-changed=${this._handleValueChanged}
@blur=${this._maybePopulateUsername}
dialogInitialFocus
></paper-input>`
: ""}
<paper-input
class="username"
name="username"
.label=${this.hass.localize(
"ui.panel.config.users.editor.username"
)}
.value=${this._username}
required
auto-validate
autocapitalize="none"
@value-changed=${this._handleValueChanged}
.errorMessage=${this.hass.localize("ui.common.error_required")}
dialogInitialFocus
></paper-input>
<paper-input
.label=${this.hass.localize(
"ui.panel.config.users.add_user.password"
)}
type="password"
name="password"
.value=${this._password}
required
auto-validate
@value-changed=${this._handleValueChanged}
.errorMessage=${this.hass.localize("ui.common.error_required")}
></paper-input>
<paper-input
label=${this.hass.localize(
"ui.panel.config.users.add_user.password_confirm"
)}
name="passwordConfirm"
.value=${this._passwordConfirm}
@value-changed=${this._handleValueChanged}
required
type="password"
.invalid=${this._password !== "" &&
this._passwordConfirm !== "" &&
this._passwordConfirm !== this._password}
.errorMessage=${this.hass.localize(
"ui.panel.config.users.add_user.password_not_match"
)}
></paper-input>
<div class="row">
<ha-formfield
.label=${this.hass.localize(
"ui.panel.config.users.editor.local_only"
)}
.dir=${computeRTLDirection(this.hass)}
>
<ha-switch
.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")}
.dir=${computeRTLDirection(this.hass)}
>
<ha-switch
.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>
${this._loading
? html`
<div slot="primaryAction" class="submit-spinner">
<ha-circular-progress active></ha-circular-progress>
</div>
`
: html`
<mwc-button
slot="primaryAction"
.disabled=${!this._name ||
!this._username ||
!this._password ||
this._password !== this._passwordConfirm}
@click=${this._createUser}
>
${this.hass.localize("ui.panel.config.users.add_user.create")}
</mwc-button>
`}
</ha-dialog>
`;
}
private _close() {
this._params = undefined;
}
private _maybePopulateUsername() {
if (this._username || !this._name) {
return;
}
const parts = this._name.split(" ");
if (parts.length) {
this._username = parts[0].toLowerCase();
}
}
private _handleValueChanged(ev: ValueChangedEvent<string>): void {
this._error = undefined;
const name = (ev.target as any).name;
this[`_${name}`] = ev.detail.value;
}
private async _adminChanged(ev): Promise<void> {
this._isAdmin = ev.target.checked;
}
private _localOnlyChanged(ev): void {
this._localOnly = ev.target.checked;
}
private async _createUser(ev) {
ev.preventDefault();
if (!this._name || !this._username || !this._password) {
return;
}
this._loading = true;
this._error = "";
let user: User;
try {
const userResponse = await createUser(
this.hass,
this._name,
[this._isAdmin ? SYSTEM_GROUP_ID_ADMIN : SYSTEM_GROUP_ID_USER],
this._localOnly
);
user = userResponse.user;
} catch (err: any) {
this._loading = false;
this._error = err.message;
return;
}
try {
await createAuthForUser(
this.hass,
user.id,
this._username,
this._password
);
} catch (err: any) {
await deleteUser(this.hass, user.id);
this._loading = false;
this._error = err.message;
return;
}
user.username = this._username;
this._params!.userAddedCallback(user);
this._close();
}
static get styles(): CSSResultGroup {
return [
haStyleDialog,
css`
ha-dialog {
--mdc-dialog-max-width: 500px;
--dialog-z-index: 10;
}
.row {
display: flex;
padding: 8px 0;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"dialog-add-user": DialogAddUser;
}
}