mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 19:56:42 +00:00
Allow changing group (#2908)
* Allow changing group * Styling + rename * Fix type
This commit is contained in:
parent
1890dd8683
commit
86548052e5
@ -22,6 +22,7 @@ import { DEFAULT_PANEL } from "../common/const";
|
|||||||
const computeUrl = (urlPath) => `/${urlPath}`;
|
const computeUrl = (urlPath) => `/${urlPath}`;
|
||||||
|
|
||||||
const computePanels = (hass: HomeAssistant) => {
|
const computePanels = (hass: HomeAssistant) => {
|
||||||
|
const isAdmin = hass.user.is_admin;
|
||||||
const panels = hass.panels;
|
const panels = hass.panels;
|
||||||
const sortValue = {
|
const sortValue = {
|
||||||
map: 1,
|
map: 1,
|
||||||
@ -30,9 +31,9 @@ const computePanels = (hass: HomeAssistant) => {
|
|||||||
};
|
};
|
||||||
const result: Panel[] = [];
|
const result: Panel[] = [];
|
||||||
|
|
||||||
Object.keys(panels).forEach((key) => {
|
Object.values(panels).forEach((panel) => {
|
||||||
if (panels[key].title) {
|
if (panel.title && (panel.component_name !== "config" || isAdmin)) {
|
||||||
result.push(panels[key]);
|
result.push(panel);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -129,62 +130,66 @@ class HaSidebar extends LitElement {
|
|||||||
: html``}
|
: html``}
|
||||||
</paper-listbox>
|
</paper-listbox>
|
||||||
|
|
||||||
<div>
|
${!hass.user.is_admin
|
||||||
<div class="divider"></div>
|
? ""
|
||||||
|
: html`
|
||||||
|
<div>
|
||||||
|
<div class="divider"></div>
|
||||||
|
|
||||||
<div class="subheader">
|
<div class="subheader">
|
||||||
${hass.localize("ui.sidebar.developer_tools")}
|
${hass.localize("ui.sidebar.developer_tools")}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dev-tools">
|
<div class="dev-tools">
|
||||||
<a href="/dev-service" tabindex="-1">
|
<a href="/dev-service" tabindex="-1">
|
||||||
<paper-icon-button
|
|
||||||
icon="hass:remote"
|
|
||||||
alt="${hass.localize("panel.dev-services")}"
|
|
||||||
title="${hass.localize("panel.dev-services")}"
|
|
||||||
></paper-icon-button>
|
|
||||||
</a>
|
|
||||||
<a href="/dev-state" tabindex="-1">
|
|
||||||
<paper-icon-button
|
|
||||||
icon="hass:code-tags"
|
|
||||||
alt="${hass.localize("panel.dev-states")}"
|
|
||||||
title="${hass.localize("panel.dev-states")}"
|
|
||||||
></paper-icon-button>
|
|
||||||
</a>
|
|
||||||
<a href="/dev-event" tabindex="-1">
|
|
||||||
<paper-icon-button
|
|
||||||
icon="hass:radio-tower"
|
|
||||||
alt="${hass.localize("panel.dev-events")}"
|
|
||||||
title="${hass.localize("panel.dev-events")}"
|
|
||||||
></paper-icon-button>
|
|
||||||
</a>
|
|
||||||
<a href="/dev-template" tabindex="-1">
|
|
||||||
<paper-icon-button
|
|
||||||
icon="hass:file-xml"
|
|
||||||
alt="${hass.localize("panel.dev-templates")}"
|
|
||||||
title="${hass.localize("panel.dev-templates")}"
|
|
||||||
></paper-icon-button>
|
|
||||||
</a>
|
|
||||||
${isComponentLoaded(hass, "mqtt")
|
|
||||||
? html`
|
|
||||||
<a href="/dev-mqtt" tabindex="-1">
|
|
||||||
<paper-icon-button
|
<paper-icon-button
|
||||||
icon="hass:altimeter"
|
icon="hass:remote"
|
||||||
alt="${hass.localize("panel.dev-mqtt")}"
|
alt="${hass.localize("panel.dev-services")}"
|
||||||
title="${hass.localize("panel.dev-mqtt")}"
|
title="${hass.localize("panel.dev-services")}"
|
||||||
></paper-icon-button>
|
></paper-icon-button>
|
||||||
</a>
|
</a>
|
||||||
`
|
<a href="/dev-state" tabindex="-1">
|
||||||
: html``}
|
<paper-icon-button
|
||||||
<a href="/dev-info" tabindex="-1">
|
icon="hass:code-tags"
|
||||||
<paper-icon-button
|
alt="${hass.localize("panel.dev-states")}"
|
||||||
icon="hass:information-outline"
|
title="${hass.localize("panel.dev-states")}"
|
||||||
alt="${hass.localize("panel.dev-info")}"
|
></paper-icon-button>
|
||||||
title="${hass.localize("panel.dev-info")}"
|
</a>
|
||||||
></paper-icon-button>
|
<a href="/dev-event" tabindex="-1">
|
||||||
</a>
|
<paper-icon-button
|
||||||
</div>
|
icon="hass:radio-tower"
|
||||||
</div>
|
alt="${hass.localize("panel.dev-events")}"
|
||||||
|
title="${hass.localize("panel.dev-events")}"
|
||||||
|
></paper-icon-button>
|
||||||
|
</a>
|
||||||
|
<a href="/dev-template" tabindex="-1">
|
||||||
|
<paper-icon-button
|
||||||
|
icon="hass:file-xml"
|
||||||
|
alt="${hass.localize("panel.dev-templates")}"
|
||||||
|
title="${hass.localize("panel.dev-templates")}"
|
||||||
|
></paper-icon-button>
|
||||||
|
</a>
|
||||||
|
${isComponentLoaded(hass, "mqtt")
|
||||||
|
? html`
|
||||||
|
<a href="/dev-mqtt" tabindex="-1">
|
||||||
|
<paper-icon-button
|
||||||
|
icon="hass:altimeter"
|
||||||
|
alt="${hass.localize("panel.dev-mqtt")}"
|
||||||
|
title="${hass.localize("panel.dev-mqtt")}"
|
||||||
|
></paper-icon-button>
|
||||||
|
</a>
|
||||||
|
`
|
||||||
|
: html``}
|
||||||
|
<a href="/dev-info" tabindex="-1">
|
||||||
|
<paper-icon-button
|
||||||
|
icon="hass:information-outline"
|
||||||
|
alt="${hass.localize("panel.dev-info")}"
|
||||||
|
title="${hass.localize("panel.dev-info")}"
|
||||||
|
></paper-icon-button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
customElement,
|
customElement,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { classMap } from "lit-html/directives/class-map";
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
import { User } from "../../data/auth";
|
import { User } from "../../data/user";
|
||||||
import { CurrentUser } from "../../types";
|
import { CurrentUser } from "../../types";
|
||||||
|
|
||||||
const computeInitials = (name: string) => {
|
const computeInitials = (name: string) => {
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import { User, fetchUsers } from "../../data/auth";
|
import { User, fetchUsers } from "../../data/user";
|
||||||
import compare from "../../common/string/compare";
|
import compare from "../../common/string/compare";
|
||||||
|
|
||||||
class HaEntityPicker extends LitElement {
|
class HaEntityPicker extends LitElement {
|
||||||
|
@ -1,26 +1,9 @@
|
|||||||
import { HomeAssistant } from "../types";
|
|
||||||
|
|
||||||
export interface AuthProvider {
|
export interface AuthProvider {
|
||||||
name: string;
|
name: string;
|
||||||
id: string;
|
id: string;
|
||||||
type: string;
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Credential {
|
export interface Credential {
|
||||||
type: string;
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface User {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
is_owner: boolean;
|
|
||||||
is_active: boolean;
|
|
||||||
system_generated: boolean;
|
|
||||||
group_ids: string[];
|
|
||||||
credentials: Credential[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const fetchUsers = async (hass: HomeAssistant) =>
|
|
||||||
hass.callWS<User[]>({
|
|
||||||
type: "config/auth/list",
|
|
||||||
});
|
|
||||||
|
49
src/data/user.ts
Normal file
49
src/data/user.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
import { Credential } from "./auth";
|
||||||
|
|
||||||
|
export const SYSTEM_GROUP_ID_ADMIN = "system-admin";
|
||||||
|
export const SYSTEM_GROUP_ID_USER = "system-users";
|
||||||
|
export const SYSTEM_GROUP_ID_READ_ONLY = "system-read-only";
|
||||||
|
|
||||||
|
export interface User {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
is_owner: boolean;
|
||||||
|
is_active: boolean;
|
||||||
|
system_generated: boolean;
|
||||||
|
group_ids: string[];
|
||||||
|
credentials: Credential[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UpdateUserParams {
|
||||||
|
name?: User["name"];
|
||||||
|
group_ids?: User["group_ids"];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fetchUsers = async (hass: HomeAssistant) =>
|
||||||
|
hass.callWS<User[]>({
|
||||||
|
type: "config/auth/list",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const createUser = async (hass: HomeAssistant, name: string) =>
|
||||||
|
hass.callWS<{ user: User }>({
|
||||||
|
type: "config/auth/create",
|
||||||
|
name,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const updateUser = async (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
userId: string,
|
||||||
|
params: UpdateUserParams
|
||||||
|
) =>
|
||||||
|
hass.callWS<{ user: User }>({
|
||||||
|
...params,
|
||||||
|
type: "config/auth/update",
|
||||||
|
user_id: userId,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const deleteUser = async (hass: HomeAssistant, userId: string) =>
|
||||||
|
hass.callWS<void>({
|
||||||
|
type: "config/auth/delete",
|
||||||
|
user_id: userId,
|
||||||
|
});
|
@ -130,6 +130,7 @@ export const provideHass = (
|
|||||||
user: {
|
user: {
|
||||||
credentials: [],
|
credentials: [],
|
||||||
id: "abcd",
|
id: "abcd",
|
||||||
|
is_admin: true,
|
||||||
is_owner: true,
|
is_owner: true,
|
||||||
mfa_modules: [],
|
mfa_modules: [],
|
||||||
name: "Demo User",
|
name: "Demo User",
|
||||||
|
@ -27,7 +27,7 @@ import {
|
|||||||
showPersonDetailDialog,
|
showPersonDetailDialog,
|
||||||
loadPersonDetailDialog,
|
loadPersonDetailDialog,
|
||||||
} from "./show-dialog-person-detail";
|
} from "./show-dialog-person-detail";
|
||||||
import { User, fetchUsers } from "../../../data/auth";
|
import { User, fetchUsers } from "../../../data/user";
|
||||||
|
|
||||||
class HaConfigPerson extends LitElement {
|
class HaConfigPerson extends LitElement {
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { Person, PersonMutableParams } from "../../../data/person";
|
import { Person, PersonMutableParams } from "../../../data/person";
|
||||||
import { User } from "../../../data/auth";
|
import { User } from "../../../data/user";
|
||||||
|
|
||||||
export interface PersonDetailDialogParams {
|
export interface PersonDetailDialogParams {
|
||||||
entry?: Person;
|
entry?: Person;
|
||||||
|
@ -66,7 +66,7 @@ class HaUserPicker extends EventsMixin(
|
|||||||
<paper-item-body two-line>
|
<paper-item-body two-line>
|
||||||
<div>[[_withDefault(user.name, 'Unnamed User')]]</div>
|
<div>[[_withDefault(user.name, 'Unnamed User')]]</div>
|
||||||
<div secondary="">
|
<div secondary="">
|
||||||
[[user.id]]
|
[[_computeGroup(localize, user)]]
|
||||||
<template is="dom-if" if="[[user.system_generated]]">
|
<template is="dom-if" if="[[user.system_generated]]">
|
||||||
- System Generated
|
- System Generated
|
||||||
</template>
|
</template>
|
||||||
@ -124,6 +124,10 @@ class HaUserPicker extends EventsMixin(
|
|||||||
return `/config/users/${user.id}`;
|
return `/config/users/${user.id}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_computeGroup(localize, user) {
|
||||||
|
return localize(`groups.${user.group_ids[0]}`);
|
||||||
|
}
|
||||||
|
|
||||||
_computeRTL(hass) {
|
_computeRTL(hass) {
|
||||||
return computeRTL(hass);
|
return computeRTL(hass);
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ import NavigateMixin from "../../../mixins/navigate-mixin";
|
|||||||
import "./ha-config-user-picker";
|
import "./ha-config-user-picker";
|
||||||
import "./ha-user-editor";
|
import "./ha-user-editor";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { fetchUsers } from "../../../data/auth";
|
import { fetchUsers } from "../../../data/user";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin NavigateMixin
|
* @appliesMixin NavigateMixin
|
||||||
|
@ -1,113 +0,0 @@
|
|||||||
import "@material/mwc-button";
|
|
||||||
import "@polymer/paper-card/paper-card";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
|
|
||||||
import "../../../layouts/hass-subpage";
|
|
||||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
|
||||||
import NavigateMixin from "../../../mixins/navigate-mixin";
|
|
||||||
import EventsMixin from "../../../mixins/events-mixin";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @appliesMixin LocalizeMixin
|
|
||||||
* @appliesMixin NavigateMixin
|
|
||||||
* @appliesMixin EventsMixin
|
|
||||||
*/
|
|
||||||
class HaUserEditor extends EventsMixin(
|
|
||||||
NavigateMixin(LocalizeMixin(PolymerElement))
|
|
||||||
) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="ha-style">
|
|
||||||
paper-card {
|
|
||||||
display: block;
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 0 auto 16px;
|
|
||||||
}
|
|
||||||
paper-card:first-child {
|
|
||||||
margin-top: 16px;
|
|
||||||
}
|
|
||||||
paper-card:last-child {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
hass-subpage paper-card:first-of-type {
|
|
||||||
direction: ltr;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<hass-subpage
|
|
||||||
header="[[localize('ui.panel.config.users.editor.caption')]]"
|
|
||||||
>
|
|
||||||
<paper-card heading="[[_computeName(user)]]">
|
|
||||||
<table class="card-content">
|
|
||||||
<tr>
|
|
||||||
<td>ID</td>
|
|
||||||
<td>[[user.id]]</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Owner</td>
|
|
||||||
<td>[[user.is_owner]]</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Active</td>
|
|
||||||
<td>[[user.is_active]]</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>System generated</td>
|
|
||||||
<td>[[user.system_generated]]</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</paper-card>
|
|
||||||
<paper-card>
|
|
||||||
<div class="card-actions">
|
|
||||||
<mwc-button
|
|
||||||
class="warning"
|
|
||||||
on-click="_deleteUser"
|
|
||||||
disabled="[[user.system_generated]]"
|
|
||||||
>
|
|
||||||
[[localize('ui.panel.config.users.editor.delete_user')]]
|
|
||||||
</mwc-button>
|
|
||||||
<template is="dom-if" if="[[user.system_generated]]">
|
|
||||||
Unable to remove system generated users.
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</paper-card>
|
|
||||||
</hass-subpage>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: Object,
|
|
||||||
user: Object,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_computeName(user) {
|
|
||||||
return user && (user.name || "Unnamed user");
|
|
||||||
}
|
|
||||||
|
|
||||||
async _deleteUser(ev) {
|
|
||||||
if (
|
|
||||||
!confirm(
|
|
||||||
`Are you sure you want to delete ${this._computeName(this.user)}`
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
ev.target.blur();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await this.hass.callWS({
|
|
||||||
type: "config/auth/delete",
|
|
||||||
user_id: this.user.id,
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
alert(err.code);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.fire("reload-users");
|
|
||||||
this.navigate("/config/users");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("ha-user-editor", HaUserEditor);
|
|
217
src/panels/config/users/ha-user-editor.ts
Normal file
217
src/panels/config/users/ha-user-editor.ts
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
html,
|
||||||
|
customElement,
|
||||||
|
CSSResultArray,
|
||||||
|
css,
|
||||||
|
property,
|
||||||
|
} from "lit-element";
|
||||||
|
import { until } from "lit-html/directives/until";
|
||||||
|
import "@material/mwc-button";
|
||||||
|
|
||||||
|
import "../../../layouts/hass-subpage";
|
||||||
|
import { haStyle } from "../../../resources/styles";
|
||||||
|
import "../../../components/ha-card";
|
||||||
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
import { navigate } from "../../../common/navigate";
|
||||||
|
import {
|
||||||
|
User,
|
||||||
|
deleteUser,
|
||||||
|
updateUser,
|
||||||
|
SYSTEM_GROUP_ID_USER,
|
||||||
|
SYSTEM_GROUP_ID_ADMIN,
|
||||||
|
} from "../../../data/user";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HASSDomEvents {
|
||||||
|
"reload-users": undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const GROUPS = [SYSTEM_GROUP_ID_USER, SYSTEM_GROUP_ID_ADMIN];
|
||||||
|
|
||||||
|
@customElement("ha-user-editor")
|
||||||
|
class HaUserEditor extends LitElement {
|
||||||
|
@property() public hass?: HomeAssistant;
|
||||||
|
@property() public user?: User;
|
||||||
|
|
||||||
|
protected render(): TemplateResult | void {
|
||||||
|
const hass = this.hass;
|
||||||
|
const user = this.user;
|
||||||
|
if (!hass || !user) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<hass-subpage
|
||||||
|
.header=${hass.localize("ui.panel.config.users.editor.caption")}
|
||||||
|
>
|
||||||
|
<ha-card .header=${this._name}>
|
||||||
|
<table class="card-content">
|
||||||
|
<tr>
|
||||||
|
<td>ID</td>
|
||||||
|
<td>${user.id}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Owner</td>
|
||||||
|
<td>${user.is_owner}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Group</td>
|
||||||
|
<td>
|
||||||
|
<select
|
||||||
|
@change=${this._handleGroupChange}
|
||||||
|
.value=${until(
|
||||||
|
this.updateComplete.then(() => user.group_ids[0])
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
${GROUPS.map(
|
||||||
|
(groupId) => html`
|
||||||
|
<option value=${groupId}>
|
||||||
|
${hass.localize(`groups.${groupId}`)}
|
||||||
|
</option>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
${user.group_ids[0] === SYSTEM_GROUP_ID_USER
|
||||||
|
? html`
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="user-experiment">
|
||||||
|
The users group is a work in progress. The user will be
|
||||||
|
unable to administer the instance via the UI. We're still
|
||||||
|
auditing all management API endpoints to ensure that they
|
||||||
|
correctly limit access to administrators.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>Active</td>
|
||||||
|
<td>${user.is_active}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>System generated</td>
|
||||||
|
<td>${user.system_generated}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="card-actions">
|
||||||
|
<mwc-button @click=${this._handleRenameUser}>
|
||||||
|
${hass.localize("ui.panel.config.users.editor.rename_user")}
|
||||||
|
</mwc-button>
|
||||||
|
<mwc-button
|
||||||
|
class="warning"
|
||||||
|
@click=${this._deleteUser}
|
||||||
|
.disabled=${user.system_generated}
|
||||||
|
>
|
||||||
|
${hass.localize("ui.panel.config.users.editor.delete_user")}
|
||||||
|
</mwc-button>
|
||||||
|
${user.system_generated
|
||||||
|
? html`
|
||||||
|
Unable to remove system generated users.
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
</hass-subpage>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _name() {
|
||||||
|
return this.user && (this.user.name || "Unnamed user");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _handleRenameUser(ev): Promise<void> {
|
||||||
|
ev.currentTarget.blur();
|
||||||
|
const newName = prompt("New name?", this.user!.name);
|
||||||
|
if (newName === null || newName === this.user!.name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await updateUser(this.hass!, this.user!.id, {
|
||||||
|
name: newName,
|
||||||
|
});
|
||||||
|
fireEvent(this, "reload-users");
|
||||||
|
} catch (err) {
|
||||||
|
alert(`User rename failed: ${err.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _handleGroupChange(ev): Promise<void> {
|
||||||
|
const selectEl = ev.currentTarget as HTMLSelectElement;
|
||||||
|
const newGroup = selectEl.value;
|
||||||
|
try {
|
||||||
|
await updateUser(this.hass!, this.user!.id, {
|
||||||
|
group_ids: [newGroup],
|
||||||
|
});
|
||||||
|
fireEvent(this, "reload-users");
|
||||||
|
} catch (err) {
|
||||||
|
alert(`Group update failed: ${err.message}`);
|
||||||
|
selectEl.value = this.user!.group_ids[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _deleteUser(ev): Promise<void> {
|
||||||
|
if (!confirm(`Are you sure you want to delete ${this._name}`)) {
|
||||||
|
ev.target.blur();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await deleteUser(this.hass!, this.user!.id);
|
||||||
|
} catch (err) {
|
||||||
|
alert(err.code);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fireEvent(this, "reload-users");
|
||||||
|
navigate(this, "/config/users");
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultArray {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
ha-card {
|
||||||
|
display: block;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto 16px;
|
||||||
|
}
|
||||||
|
ha-card:first-child {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
ha-card:last-child {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.card-content {
|
||||||
|
padding: 0 16px 16px;
|
||||||
|
}
|
||||||
|
.card-actions {
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
hass-subpage ha-card:first-of-type {
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
.user-experiment {
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-user-editor": HaUserEditor;
|
||||||
|
}
|
||||||
|
}
|
@ -355,6 +355,11 @@
|
|||||||
"not_home": "[%key:state::person::not_home%]"
|
"not_home": "[%key:state::person::not_home%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"groups": {
|
||||||
|
"system-admin": "Administrators",
|
||||||
|
"system-users": "Users",
|
||||||
|
"system-read-only": "Read-Only Users"
|
||||||
|
},
|
||||||
"ui": {
|
"ui": {
|
||||||
"auth_store": {
|
"auth_store": {
|
||||||
"ask": "Do you want to save this login?",
|
"ask": "Do you want to save this login?",
|
||||||
|
@ -57,6 +57,7 @@ export interface MFAModule {
|
|||||||
export interface CurrentUser {
|
export interface CurrentUser {
|
||||||
id: string;
|
id: string;
|
||||||
is_owner: boolean;
|
is_owner: boolean;
|
||||||
|
is_admin: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
credentials: Credential[];
|
credentials: Credential[];
|
||||||
mfa_modules: MFAModule[];
|
mfa_modules: MFAModule[];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user