diff --git a/src/common/dom/is-navigation-click.ts b/src/common/dom/is-navigation-click.ts index 05edb52e14..9cc92a5832 100644 --- a/src/common/dom/is-navigation-click.ts +++ b/src/common/dom/is-navigation-click.ts @@ -12,7 +12,7 @@ export const isNavigationClick = (e: MouseEvent) => { const anchor = e .composedPath() - .filter((n) => (n as HTMLElement).tagName === "A")[0] as + .find((n) => (n as HTMLElement).tagName === "A") as | HTMLAnchorElement | undefined; if ( diff --git a/src/components/ha-analytics.ts b/src/components/ha-analytics.ts index d81cbf379c..33756e4c28 100644 --- a/src/components/ha-analytics.ts +++ b/src/components/ha-analytics.ts @@ -2,12 +2,12 @@ import "@polymer/paper-tooltip/paper-tooltip"; import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; import { fireEvent } from "../common/dom/fire_event"; -import { Analytics, AnalyticsPreferences } from "../data/analytics"; +import type { Analytics, AnalyticsPreferences } from "../data/analytics"; import { haStyle } from "../resources/styles"; -import { HomeAssistant } from "../types"; -import "./ha-checkbox"; -import type { HaCheckbox } from "./ha-checkbox"; +import type { HomeAssistant } from "../types"; import "./ha-settings-row"; +import "./ha-switch"; +import type { HaSwitch } from "./ha-switch"; const ADDITIONAL_PREFERENCES = [ { @@ -40,62 +40,62 @@ export class HaAnalytics extends LitElement { return html` - - - - Basic analytics This includes information about your system. + + ${ADDITIONAL_PREFERENCES.map( (preference) => - html` - - - - ${!baseEnabled - ? html` - You need to enable basic analytics for this option to be - available - ` - : ""} - - - ${preference.title} - - - ${preference.description} - - ` + html` + + + ${preference.title} + + + ${preference.description} + + + + + ${!baseEnabled + ? html` + + You need to enable basic analytics for this option to be + available + + ` + : ""} + + + ` )} - - - - Diagnostics Share crash reports when unexpected errors occur. + + `; } @@ -120,23 +120,23 @@ export class HaAnalytics extends LitElement { }); } - private _handleRowCheckboxClick(ev: Event) { - const checkbox = ev.currentTarget as HaCheckbox; - const preference = (checkbox as any).preference; + private _handleRowClick(ev: Event) { + const target = ev.currentTarget as HaSwitch; + const preference = (target as any).preference; const preferences = this.analytics ? { ...this.analytics.preferences } : {}; - if (preferences[preference] === checkbox.checked) { + if (preferences[preference] === target.checked) { return; } - preferences[preference] = checkbox.checked; + preferences[preference] = target.checked; if ( ADDITIONAL_PREFERENCES.some((entry) => entry.key === preference) && - checkbox.checked + target.checked ) { preferences.base = true; - } else if (preference === "base" && !checkbox.checked) { + } else if (preference === "base" && !target.checked) { preferences.usage = false; preferences.statistics = false; } diff --git a/src/components/ha-clickable-list-item.ts b/src/components/ha-clickable-list-item.ts new file mode 100644 index 0000000000..895c67954a --- /dev/null +++ b/src/components/ha-clickable-list-item.ts @@ -0,0 +1,69 @@ +import { ListItemBase } from "@material/mwc-list/mwc-list-item-base"; +import { styles } from "@material/mwc-list/mwc-list-item.css"; +import { css, CSSResult, html } from "lit"; +import { customElement, property, query } from "lit/decorators"; + +@customElement("ha-clickable-list-item") +export class HaClickableListItem extends ListItemBase { + @property() public href?: string; + + @property({ type: Boolean }) public disableHref = false; + + // property used only in css + @property({ type: Boolean, reflect: true }) public rtl = false; + + @query("a") private _anchor!: HTMLAnchorElement; + + public render() { + const r = super.render(); + const href = this.href || ""; + + return html`${this.disableHref + ? html`${r}` + : html`${r}`}`; + } + + firstUpdated() { + super.firstUpdated(); + this.addEventListener("keydown", (ev) => { + if (ev.key === "Enter" || ev.key === " ") { + this._anchor.click(); + } + }); + } + + static get styles(): CSSResult[] { + return [ + styles, + css` + :host { + padding-left: 0px; + padding-right: 0px; + } + :host([rtl]) span { + margin-left: var(--mdc-list-item-graphic-margin, 20px) !important; + margin-right: 0px !important; + } + :host([graphic="avatar"]:not([twoLine])), + :host([graphic="icon"]:not([twoLine])) { + height: 48px; + } + a { + width: 100%; + height: 100%; + display: flex; + align-items: center; + padding-left: var(--mdc-list-side-padding, 20px); + padding-right: var(--mdc-list-side-padding, 20px); + font-weight: 500; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-clickable-list-item": HaClickableListItem; + } +} diff --git a/src/components/ha-navigation-list.ts b/src/components/ha-navigation-list.ts new file mode 100644 index 0000000000..734e4b8cf8 --- /dev/null +++ b/src/components/ha-navigation-list.ts @@ -0,0 +1,92 @@ +import "@material/mwc-list/mwc-list"; +import "@material/mwc-list/mwc-list-item"; +import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators"; +import type { PageNavigation } from "../layouts/hass-tabs-subpage"; +import type { HomeAssistant } from "../types"; +import "./ha-icon-next"; +import "./ha-svg-icon"; +import "./ha-clickable-list-item"; + +@customElement("ha-navigation-list") +class HaNavigationList extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ type: Boolean }) public narrow!: boolean; + + @property({ attribute: false }) public pages!: PageNavigation[]; + + @property({ type: Boolean }) public hasSecondary = false; + + public render(): TemplateResult { + return html` + + ${this.pages.map( + (page) => html` + +
+ +
+ ${page.name} + ${this.hasSecondary + ? html`${page.description}` + : ""} + ${!this.narrow + ? html`` + : ""} +
+ ` + )} +
+ `; + } + + private _entryClicked(ev) { + ev.currentTarget.blur(); + } + + static styles: CSSResultGroup = css` + a { + text-decoration: none; + color: var(--primary-text-color); + position: relative; + display: block; + outline: 0; + } + ha-svg-icon, + ha-icon-next { + color: var(--secondary-text-color); + height: 24px; + width: 24px; + } + ha-svg-icon { + padding: 8px; + } + .icon-background { + border-radius: 50%; + } + .icon-background ha-svg-icon { + color: #fff; + } + mwc-list-item { + cursor: pointer; + font-size: var(--navigation-list-item-title-font-size); + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "ha-navigation-list": HaNavigationList; + } +} diff --git a/src/components/map/ha-locations-editor.ts b/src/components/map/ha-locations-editor.ts index 2dcc446099..43f68553a7 100644 --- a/src/components/map/ha-locations-editor.ts +++ b/src/components/map/ha-locations-editor.ts @@ -19,9 +19,9 @@ import memoizeOne from "memoize-one"; import { fireEvent } from "../../common/dom/fire_event"; import type { LeafletModuleType } from "../../common/dom/setup-leaflet-map"; import type { HomeAssistant } from "../../types"; +import "../ha-input-helper-text"; import "./ha-map"; import type { HaMap } from "./ha-map"; -import "../ha-input-helper-text"; declare global { // for fire event @@ -297,7 +297,7 @@ export class HaLocationsEditor extends LitElement { return css` ha-map { display: block; - height: 300px; + height: 100%; } `; } diff --git a/src/panels/config/areas/ha-config-area-page.ts b/src/panels/config/areas/ha-config-area-page.ts index dfb038e375..a34cbc5841 100644 --- a/src/panels/config/areas/ha-config-area-page.ts +++ b/src/panels/config/areas/ha-config-area-page.ts @@ -202,7 +202,7 @@ class HaConfigAreaPage extends LitElement { ${this.narrow diff --git a/src/panels/config/areas/ha-config-areas-dashboard.ts b/src/panels/config/areas/ha-config-areas-dashboard.ts index 3b13c7f89c..a82b567eee 100644 --- a/src/panels/config/areas/ha-config-areas-dashboard.ts +++ b/src/panels/config/areas/ha-config-areas-dashboard.ts @@ -82,7 +82,7 @@ export class HaConfigAreasDashboard extends LitElement { .narrow=${this.narrow} .isWide=${this.isWide} back-path="/config" - .tabs=${configSections.devices} + .tabs=${configSections.areas} .route=${this.route} > + ${this.hass.localize("ui.panel.config.backup.caption")} +
${error ? html`
${error}
` : ""}

diff --git a/src/panels/config/core/ha-config-core.js b/src/panels/config/core/ha-config-core.js index 4e01b9d6af..b76dd9b678 100644 --- a/src/panels/config/core/ha-config-core.js +++ b/src/panels/config/core/ha-config-core.js @@ -3,11 +3,11 @@ import "@polymer/app-layout/app-toolbar/app-toolbar"; import { html } from "@polymer/polymer/lib/utils/html-tag"; /* eslint-plugin-disable lit */ import { PolymerElement } from "@polymer/polymer/polymer-element"; -import "../../../layouts/hass-tabs-subpage"; +import "../../../layouts/hass-subpage"; import LocalizeMixin from "../../../mixins/localize-mixin"; import "../../../styles/polymer-ha-style"; -import { configSections } from "../ha-panel-config"; -import "./ha-config-section-core"; +import "./ha-config-core-form"; +import "./ha-config-name-form"; /* * @appliesMixin LocalizeMixin @@ -17,36 +17,29 @@ class HaConfigCore extends LocalizeMixin(PolymerElement) { return html` - -

- +
+ +
- + `; } @@ -59,14 +52,6 @@ class HaConfigCore extends LocalizeMixin(PolymerElement) { route: Object, }; } - - _computeTabs() { - return configSections.general; - } - - computeClasses(isWide) { - return isWide ? "content" : "content narrow"; - } } customElements.define("ha-config-core", HaConfigCore); diff --git a/src/panels/config/core/ha-config-network.ts b/src/panels/config/core/ha-config-network.ts index 3fa243c0b6..42cecfe606 100644 --- a/src/panels/config/core/ha-config-network.ts +++ b/src/panels/config/core/ha-config-network.ts @@ -9,6 +9,7 @@ import { } from "lit"; import { customElement, property, state } from "lit/decorators"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; +import "../../../components/ha-alert"; import "../../../components/ha-card"; import "../../../components/ha-checkbox"; import "../../../components/ha-network"; @@ -28,7 +29,7 @@ class ConfigNetwork extends LitElement { @state() private _networkConfig?: NetworkConfig; - @state() private _error?: string; + @state() private _error?: { code: string; message: string }; protected render(): TemplateResult { if ( @@ -39,9 +40,15 @@ class ConfigNetwork extends LitElement { } return html` - +
- ${this._error ? html`
${this._error}
` : ""} + ${this._error + ? html` + ${this._error.message || this._error.code} + ` + : ""}

Configure which network adapters integrations will use. Currently this setting only affects multicast traffic. A restart is required diff --git a/src/panels/config/core/ha-config-section-analytics.ts b/src/panels/config/core/ha-config-section-analytics.ts new file mode 100644 index 0000000000..200f1879b3 --- /dev/null +++ b/src/panels/config/core/ha-config-section-analytics.ts @@ -0,0 +1,43 @@ +import { css, html, LitElement, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators"; +import "../../../layouts/hass-subpage"; +import type { HomeAssistant, Route } from "../../../types"; +import "./ha-config-analytics"; + +@customElement("ha-config-section-analytics") +class HaConfigSectionAnalytics extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public route!: Route; + + @property({ type: Boolean }) public narrow!: boolean; + + protected render(): TemplateResult { + return html` + +

+ +
+ + `; + } + + static styles = css` + .content { + padding: 28px 20px 0; + max-width: 1040px; + margin: 0 auto; + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "ha-config-section-analytics": HaConfigSectionAnalytics; + } +} diff --git a/src/panels/config/core/ha-config-section-core.js b/src/panels/config/core/ha-config-section-core.js deleted file mode 100644 index 8a807fa346..0000000000 --- a/src/panels/config/core/ha-config-section-core.js +++ /dev/null @@ -1,70 +0,0 @@ -import "@material/mwc-button"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import "../../../components/buttons/ha-call-service-button"; -import "../../../components/ha-card"; -import LocalizeMixin from "../../../mixins/localize-mixin"; -import "../../../styles/polymer-ha-style"; -import "../ha-config-section"; -import "./ha-config-analytics"; -import "./ha-config-core-form"; -import "./ha-config-name-form"; -import "./ha-config-network"; -import "./ha-config-url-form"; - -/* - * @appliesMixin LocalizeMixin - */ -class HaConfigSectionCore extends LocalizeMixin(PolymerElement) { - static get template() { - return html` - - [[localize('ui.panel.config.core.section.core.header')]] - [[localize('ui.panel.config.core.section.core.introduction')]] - - - - - - - - `; - } - - static get properties() { - return { - hass: { - type: Object, - }, - - isWide: { - type: Boolean, - value: false, - }, - - validating: { - type: Boolean, - value: false, - }, - - isValid: { - type: Boolean, - value: null, - }, - - validateLog: { - type: String, - value: "", - }, - - showAdvanced: Boolean, - }; - } -} - -customElements.define("ha-config-section-core", HaConfigSectionCore); diff --git a/src/panels/config/core/ha-config-section-network.ts b/src/panels/config/core/ha-config-section-network.ts new file mode 100644 index 0000000000..7f87ae1d6c --- /dev/null +++ b/src/panels/config/core/ha-config-section-network.ts @@ -0,0 +1,49 @@ +import { css, html, LitElement, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators"; +import "../../../layouts/hass-subpage"; +import type { HomeAssistant, Route } from "../../../types"; +import "./ha-config-network"; +import "./ha-config-url-form"; + +@customElement("ha-config-section-network") +class HaConfigSectionNetwork extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public route!: Route; + + @property({ type: Boolean }) public narrow!: boolean; + + protected render(): TemplateResult { + return html` + +
+ + +
+
+ `; + } + + static styles = css` + .content { + padding: 28px 20px 0; + max-width: 1040px; + margin: 0 auto; + } + ha-config-network { + display: block; + margin-top: 24px; + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "ha-config-section-network": HaConfigSectionNetwork; + } +} diff --git a/src/panels/config/core/ha-config-section-storage.ts b/src/panels/config/core/ha-config-section-storage.ts new file mode 100644 index 0000000000..6e3a96f140 --- /dev/null +++ b/src/panels/config/core/ha-config-section-storage.ts @@ -0,0 +1,40 @@ +import { css, html, LitElement, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators"; +import "../../../layouts/hass-subpage"; +import type { HomeAssistant, Route } from "../../../types"; +import "./ha-config-analytics"; + +@customElement("ha-config-section-storage") +class HaConfigSectionStorage extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ attribute: false }) public route!: Route; + + @property({ type: Boolean }) public narrow!: boolean; + + protected render(): TemplateResult { + return html` + +
+
+ `; + } + + static styles = css` + .content { + padding: 28px 20px 0; + max-width: 1040px; + margin: 0 auto; + } + `; +} + +declare global { + interface HTMLElementTagNameMap { + "ha-config-section-storage": HaConfigSectionStorage; + } +} diff --git a/src/panels/config/core/ha-config-system-navigation.ts b/src/panels/config/core/ha-config-system-navigation.ts new file mode 100644 index 0000000000..0db85573b0 --- /dev/null +++ b/src/panels/config/core/ha-config-system-navigation.ts @@ -0,0 +1,115 @@ +import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { customElement, property } from "lit/decorators"; +import "../../../components/ha-card"; +import "../../../components/ha-navigation-list"; +import { CloudStatus } from "../../../data/cloud"; +import "../../../layouts/hass-subpage"; +import { haStyle } from "../../../resources/styles"; +import type { HomeAssistant } from "../../../types"; +import "../ha-config-section"; +import { configSections } from "../ha-panel-config"; + +@customElement("ha-config-system-navigation") +class HaConfigSystemNavigation extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property({ type: Boolean, reflect: true }) + public narrow!: boolean; + + @property({ type: Boolean }) public isWide!: boolean; + + @property({ attribute: false }) public cloudStatus?: CloudStatus; + + @property({ type: Boolean }) public showAdvanced!: boolean; + + protected render(): TemplateResult { + const pages = configSections.general.map((page) => ({ + ...page, + name: page.translationKey + ? this.hass.localize(page.translationKey) + : page.name, + })); + + return html` + + + + ${this.narrow + ? html`
+ ${this.hass.localize( + "ui.panel.config.dashboard.system.title" + )} +
` + : ""} + +
+
+
+ `; + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + ha-card { + margin-bottom: env(safe-area-inset-bottom); + } + :host(:not([narrow])) ha-card { + margin-bottom: max(24px, env(safe-area-inset-bottom)); + } + + ha-config-section { + margin: auto; + margin-top: -32px; + max-width: 600px; + } + + ha-card { + overflow: hidden; + } + + ha-card a { + text-decoration: none; + color: var(--primary-text-color); + } + + .title { + font-size: 16px; + padding: 16px; + padding-bottom: 0; + } + + :host([narrow]) ha-card { + border-radius: 0; + box-shadow: unset; + } + + :host([narrow]) ha-config-section { + margin-top: -42px; + } + + ha-navigation-list { + --navigation-list-item-title-font-size: 16px; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-config-system-navigation": HaConfigSystemNavigation; + } +} diff --git a/src/panels/config/core/ha-config-url-form.ts b/src/panels/config/core/ha-config-url-form.ts index 80830d6c7a..f8af5f8781 100644 --- a/src/panels/config/core/ha-config-url-form.ts +++ b/src/panels/config/core/ha-config-url-form.ts @@ -9,17 +9,17 @@ import { } from "lit"; import { customElement, property, state } from "lit/decorators"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; -import "../../../components/ha-card"; -import "../../../components/ha-switch"; +import { isIPAddress } from "../../../common/string/is_ip_address"; import "../../../components/ha-alert"; +import "../../../components/ha-card"; import "../../../components/ha-formfield"; +import "../../../components/ha-switch"; import "../../../components/ha-textfield"; import type { HaTextField } from "../../../components/ha-textfield"; import { CloudStatus, fetchCloudStatus } from "../../../data/cloud"; import { saveCoreConfig } from "../../../data/core"; import type { PolymerChangedEvent } from "../../../polymer-types"; import type { HomeAssistant } from "../../../types"; -import { isIPAddress } from "../../../common/string/is_ip_address"; @customElement("ha-config-url-form") class ConfigUrlForm extends LitElement { @@ -74,7 +74,10 @@ class ConfigUrlForm extends LitElement { } return html` - +
${!canEdit ? html` @@ -335,6 +338,7 @@ class ConfigUrlForm extends LitElement { a { color: var(--primary-color); + text-decoration: none; } `; } diff --git a/src/panels/config/dashboard/ha-config-dashboard.ts b/src/panels/config/dashboard/ha-config-dashboard.ts index 3f0e3e4849..e690e37b59 100644 --- a/src/panels/config/dashboard/ha-config-dashboard.ts +++ b/src/panels/config/dashboard/ha-config-dashboard.ts @@ -34,6 +34,7 @@ import { updateCanInstall, UpdateEntity } from "../../../data/update"; import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box"; import { showQuickBar } from "../../../dialogs/quick-bar/show-dialog-quick-bar"; import "../../../layouts/ha-app-layout"; +import { PageNavigation } from "../../../layouts/hass-tabs-subpage"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant } from "../../../types"; import { documentationUrl } from "../../../util/documentation-url"; @@ -119,10 +120,26 @@ class HaConfigDashboard extends LitElement { private _notifyUpdates = false; + private _pages = memoizeOne((clouStatus, isLoaded) => { + const pages: PageNavigation[] = []; + if (clouStatus && isLoaded) { + pages.push({ + component: "cloud", + path: "/config/cloud", + name: "Home Assistant Cloud", + info: this.cloudStatus, + iconPath: mdiCloudLock, + iconColor: "#3B808E", + }); + } + return [...pages, ...configSections.dashboard]; + }); + protected render(): TemplateResult { const canInstallUpdates = this._filterUpdateEntitiesWithInstall( this.hass.states ); + return html` @@ -175,30 +192,14 @@ class HaConfigDashboard extends LitElement { ${this.hass.localize("panel.config")}
` : ""} - ${this.cloudStatus && isComponentLoaded(this.hass, "cloud") - ? html` - - ` - : ""}
diff --git a/src/panels/config/dashboard/ha-config-navigation.ts b/src/panels/config/dashboard/ha-config-navigation.ts index d457429318..9341432955 100644 --- a/src/panels/config/dashboard/ha-config-navigation.ts +++ b/src/panels/config/dashboard/ha-config-navigation.ts @@ -1,13 +1,14 @@ -import "@polymer/paper-item/paper-icon-item"; -import "@polymer/paper-item/paper-item-body"; -import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import "@material/mwc-list/mwc-list"; +import "@material/mwc-list/mwc-list-item"; +import { html, LitElement, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators"; import { canShowPage } from "../../../common/config/can_show_page"; import "../../../components/ha-card"; import "../../../components/ha-icon-next"; -import { CloudStatus, CloudStatusLoggedIn } from "../../../data/cloud"; -import { PageNavigation } from "../../../layouts/hass-tabs-subpage"; -import { HomeAssistant } from "../../../types"; +import "../../../components/ha-navigation-list"; +import type { CloudStatus, CloudStatusLoggedIn } from "../../../data/cloud"; +import type { PageNavigation } from "../../../layouts/hass-tabs-subpage"; +import type { HomeAssistant } from "../../../types"; @customElement("ha-config-navigation") class HaConfigNavigation extends LitElement { @@ -15,129 +16,71 @@ class HaConfigNavigation extends LitElement { @property({ type: Boolean }) public narrow!: boolean; - @property() public showAdvanced!: boolean; - - @property() public pages!: PageNavigation[]; + @property({ attribute: false }) public pages!: PageNavigation[]; protected render(): TemplateResult { + const pages = this.pages + .filter((page) => + page.path === "#external-app-configuration" + ? this.hass.auth.external?.config.hasSettingsScreen + : canShowPage(this.hass, page) + ) + .map((page) => ({ + ...page, + name: + page.name || + this.hass.localize( + `ui.panel.config.dashboard.${page.translationKey}.title` + ), + description: + page.component === "cloud" && (page.info as CloudStatus) + ? page.info.logged_in + ? ` + ${this.hass.localize( + "ui.panel.config.cloud.description_login", + "email", + (page.info as CloudStatusLoggedIn).email + )} + ` + : ` + ${this.hass.localize( + "ui.panel.config.cloud.description_features" + )} + ` + : ` + ${ + page.description || + this.hass.localize( + `ui.panel.config.dashboard.${page.translationKey}.description` + ) + } + `, + })); return html` - ${this.pages.map((page) => - ( - page.path === "#external-app-configuration" - ? this.hass.auth.external?.config.hasSettingsScreen - : canShowPage(this.hass, page) - ) - ? html` - - -
- -
- - ${page.name || - this.hass.localize( - `ui.panel.config.dashboard.${page.translationKey}.title` - )} - ${page.component === "cloud" && (page.info as CloudStatus) - ? page.info.logged_in - ? html` -
- ${this.hass.localize( - "ui.panel.config.cloud.description_login", - "email", - (page.info as CloudStatusLoggedIn).email - )} -
- ` - : html` -
- ${this.hass.localize( - "ui.panel.config.cloud.description_features" - )} -
- ` - : html` -
- ${page.description || - this.hass.localize( - `ui.panel.config.dashboard.${page.translationKey}.description` - )} -
- `} -
- ${!this.narrow ? html`` : ""} -
-
- ` - : "" - )} + `; } private _entryClicked(ev) { - ev.currentTarget.blur(); - if ( - ev.currentTarget.parentElement.href.endsWith( - "#external-app-configuration" - ) - ) { + const anchor = ev + .composedPath() + .find((n) => (n as HTMLElement).tagName === "A") as + | HTMLAnchorElement + | undefined; + + if (anchor?.href?.endsWith("#external-app-configuration")) { ev.preventDefault(); this.hass.auth.external!.fireMessage({ type: "config_screen/show", }); } } - - static get styles(): CSSResultGroup { - return css` - a { - text-decoration: none; - color: var(--primary-text-color); - position: relative; - display: block; - outline: 0; - } - ha-svg-icon, - ha-icon-next { - color: var(--secondary-text-color); - height: 24px; - width: 24px; - } - ha-svg-icon { - padding: 8px; - } - .iron-selected paper-item::before, - a:not(.iron-selected):focus::before { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - pointer-events: none; - content: ""; - transition: opacity 15ms linear; - will-change: opacity; - } - a:not(.iron-selected):focus::before { - background-color: currentColor; - opacity: var(--dark-divider-opacity); - } - .iron-selected paper-item:focus::before, - .iron-selected:focus paper-item::before { - opacity: 0.2; - } - .icon-background { - border-radius: 50%; - } - .icon-background ha-svg-icon { - color: #fff; - } - `; - } } declare global { diff --git a/src/panels/config/ha-panel-config.ts b/src/panels/config/ha-panel-config.ts index 7483960acd..a467dcbe6f 100644 --- a/src/panels/config/ha-panel-config.ts +++ b/src/panels/config/ha-panel-config.ts @@ -4,12 +4,15 @@ import { mdiBadgeAccountHorizontal, mdiCellphoneCog, mdiCog, + mdiCpu32Bit, mdiDevices, mdiHomeAssistant, mdiInformation, + mdiInformationOutline, mdiLightningBolt, mdiMapMarkerRadius, mdiMathLog, + mdiNetwork, mdiNfcVariant, mdiPalette, mdiPaletteSwatch, @@ -20,6 +23,7 @@ import { mdiShape, mdiSofa, mdiTools, + mdiUpdate, mdiViewDashboard, } from "@mdi/js"; import { PolymerElement } from "@polymer/polymer"; @@ -58,11 +62,11 @@ export const configSections: { [name: string]: PageNavigation[] } = { core: true, }, { - path: "/config/blueprint", - translationKey: "blueprints", - iconPath: mdiPaletteSwatch, - iconColor: "#64B5F6", - component: "blueprint", + path: "/config/areas", + translationKey: "areas", + iconPath: mdiSofa, + iconColor: "#E48629", + components: ["zone"], }, { path: "/config/backup", @@ -74,8 +78,8 @@ export const configSections: { [name: string]: PageNavigation[] } = { { path: "/hassio", translationKey: "supervisor", - iconPath: mdiHomeAssistant, - iconColor: "#4084CD", + iconPath: mdiPuzzle, + iconColor: "#F1C447", component: "hassio", }, { @@ -97,7 +101,7 @@ export const configSections: { [name: string]: PageNavigation[] } = { translationKey: "people", iconPath: mdiAccount, iconColor: "#E48629", - components: ["person", "zone", "users"], + components: ["person", "users"], }, { path: "#external-app-configuration", @@ -106,9 +110,16 @@ export const configSections: { [name: string]: PageNavigation[] } = { iconColor: "#8E24AA", }, { - path: "/config/server_control", - translationKey: "settings", + path: "/config/system", + translationKey: "system", iconPath: mdiCog, + iconColor: "#301ABE", + core: true, + }, + { + path: "/config/info", + translationKey: "about", + iconPath: mdiInformationOutline, iconColor: "#4A5963", core: true, }, @@ -148,11 +159,11 @@ export const configSections: { [name: string]: PageNavigation[] } = { core: true, }, { - component: "areas", - path: "/config/areas", - translationKey: "ui.panel.config.areas.caption", - iconPath: mdiSofa, - iconColor: "#2D338F", + component: "helpers", + path: "/config/helpers", + translationKey: "ui.panel.config.helpers.caption", + iconPath: mdiTools, + iconColor: "#4D2EA4", core: true, }, ], @@ -178,16 +189,6 @@ export const configSections: { [name: string]: PageNavigation[] } = { iconPath: mdiScriptText, iconColor: "#518C43", }, - { - component: "helpers", - path: "/config/helpers", - translationKey: "ui.panel.config.helpers.caption", - iconPath: mdiTools, - iconColor: "#4D2EA4", - core: true, - }, - ], - blueprints: [ { component: "blueprint", path: "/config/blueprint", @@ -232,13 +233,6 @@ export const configSections: { [name: string]: PageNavigation[] } = { iconPath: mdiAccount, iconColor: "#E48629", }, - { - component: "zone", - path: "/config/zone", - translationKey: "ui.panel.config.zone.caption", - iconPath: mdiMapMarkerRadius, - iconColor: "#E48629", - }, { component: "users", path: "/config/users", @@ -249,6 +243,23 @@ export const configSections: { [name: string]: PageNavigation[] } = { advancedOnly: true, }, ], + areas: [ + { + component: "areas", + path: "/config/areas", + translationKey: "ui.panel.config.areas.caption", + iconPath: mdiSofa, + iconColor: "#2D338F", + core: true, + }, + { + component: "zone", + path: "/config/location", + translationKey: "ui.panel.config.zone.caption", + iconPath: mdiMapMarkerRadius, + iconColor: "#E48629", + }, + ], general: [ { component: "core", @@ -274,6 +285,45 @@ export const configSections: { [name: string]: PageNavigation[] } = { iconColor: "#4A5963", core: true, }, + { + path: "/config/backup", + translationKey: "ui.panel.config.backup.caption", + iconPath: mdiBackupRestore, + iconColor: "#4084CD", + component: "backup", + }, + { + path: "/config/analytics", + translationKey: "ui.panel.config.analytics.caption", + iconPath: mdiShape, + iconColor: "#f1c447", + }, + { + path: "/config/hardware", + translationKey: "ui.panel.config.hardware.caption", + iconPath: mdiCpu32Bit, + iconColor: "#4A5963", + }, + { + path: "/config/network", + translationKey: "ui.panel.config.network.caption", + iconPath: mdiNetwork, + iconColor: "#B1345C", + }, + { + path: "/config/storage", + translationKey: "ui.panel.config.storage.caption", + iconPath: mdiServer, + iconColor: "#518C43", + }, + { + path: "/config/update", + translationKey: "ui.panel.config.updates.caption", + iconPath: mdiUpdate, + iconColor: "#4A5963", + }, + ], + about: [ { component: "info", path: "/config/info", @@ -296,6 +346,10 @@ class HaPanelConfig extends HassRouterPage { protected routerOptions: RouterOptions = { defaultPage: "dashboard", routes: { + analytics: { + tag: "ha-config-section-analytics", + load: () => import("./core/ha-config-section-analytics"), + }, areas: { tag: "ha-config-areas", load: () => import("./areas/ha-config-areas"), @@ -328,9 +382,9 @@ class HaPanelConfig extends HassRouterPage { tag: "ha-config-devices", load: () => import("./devices/ha-config-devices"), }, - server_control: { - tag: "ha-config-server-control", - load: () => import("./server_control/ha-config-server-control"), + system: { + tag: "ha-config-system-navigation", + load: () => import("./core/ha-config-system-navigation"), }, logs: { tag: "ha-config-logs", @@ -362,6 +416,10 @@ class HaPanelConfig extends HassRouterPage { tag: "ha-config-lovelace", load: () => import("./lovelace/ha-config-lovelace"), }, + network: { + tag: "ha-config-section-network", + load: () => import("./core/ha-config-section-network"), + }, person: { tag: "ha-config-person", load: () => import("./person/ha-config-person"), @@ -378,11 +436,15 @@ class HaPanelConfig extends HassRouterPage { tag: "ha-config-helpers", load: () => import("./helpers/ha-config-helpers"), }, + storage: { + tag: "ha-config-section-storage", + load: () => import("./core/ha-config-section-storage"), + }, users: { tag: "ha-config-users", load: () => import("./users/ha-config-users"), }, - zone: { + location: { tag: "ha-config-zone", load: () => import("./zone/ha-config-zone"), }, diff --git a/src/panels/config/helpers/ha-config-helpers.ts b/src/panels/config/helpers/ha-config-helpers.ts index 4173fd69ef..fbaa0e1c2f 100644 --- a/src/panels/config/helpers/ha-config-helpers.ts +++ b/src/panels/config/helpers/ha-config-helpers.ts @@ -6,21 +6,29 @@ import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; import { computeStateDomain } from "../../../common/entity/compute_state_domain"; import { domainIcon } from "../../../common/entity/domain_icon"; +import { navigate } from "../../../common/navigate"; import { LocalizeFunc } from "../../../common/translations/localize"; +import { extractSearchParam } from "../../../common/url/search-params"; import { DataTableColumnContainer, RowClickedEvent, } from "../../../components/data-table/ha-data-table"; import "../../../components/ha-fab"; -import "../../../components/ha-icon-overflow-menu"; import "../../../components/ha-icon"; +import "../../../components/ha-icon-overflow-menu"; import "../../../components/ha-svg-icon"; import { ConfigEntry, getConfigEntries } from "../../../data/config_entries"; +import { getConfigFlowHandlers } from "../../../data/config_flow"; import { EntityRegistryEntry, subscribeEntityRegistry, } from "../../../data/entity_registry"; import { domainToName } from "../../../data/integration"; +import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow"; +import { + showAlertDialog, + showConfirmationDialog, +} from "../../../dialogs/generic/show-dialog-box"; import "../../../layouts/hass-loading-screen"; import "../../../layouts/hass-tabs-subpage-data-table"; import { SubscribeMixin } from "../../../mixins/subscribe-mixin"; @@ -29,14 +37,6 @@ import { showEntityEditorDialog } from "../entities/show-dialog-entity-editor"; import { configSections } from "../ha-panel-config"; import { HELPER_DOMAINS } from "./const"; import { showHelperDetailDialog } from "./show-dialog-helper-detail"; -import { navigate } from "../../../common/navigate"; -import { extractSearchParam } from "../../../common/url/search-params"; -import { getConfigFlowHandlers } from "../../../data/config_flow"; -import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow"; -import { - showAlertDialog, - showConfirmationDialog, -} from "../../../dialogs/generic/show-dialog-box"; // This groups items by a key but only returns last entry per key. const groupByOne = ( @@ -196,7 +196,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { .narrow=${this.narrow} back-path="/config" .route=${this.route} - .tabs=${configSections.automations} + .tabs=${configSections.devices} .columns=${this._columns(this.narrow, this.hass.localize)} .data=${this._getItems( this._stateItems, diff --git a/src/panels/config/info/ha-config-info.ts b/src/panels/config/info/ha-config-info.ts index a2459d1565..cb61dbc46f 100644 --- a/src/panels/config/info/ha-config-info.ts +++ b/src/panels/config/info/ha-config-info.ts @@ -1,7 +1,7 @@ import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import { property } from "lit/decorators"; -import "../../../layouts/hass-tabs-subpage"; import "../../../components/ha-logo-svg"; +import "../../../layouts/hass-tabs-subpage"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant, Route } from "../../../types"; import { documentationUrl } from "../../../util/documentation-url"; @@ -34,7 +34,7 @@ class HaConfigInfo extends LitElement { .narrow=${this.narrow} back-path="/config" .route=${this.route} - .tabs=${configSections.general} + .tabs=${configSections.about} >
${search}
@@ -80,7 +79,7 @@ export class HaConfigLogs extends LitElement { .filter=${this._filter} >
- + `; } diff --git a/src/panels/config/server_control/ha-config-server-control.ts b/src/panels/config/server_control/ha-config-server-control.ts index 9c0011c049..6686590646 100644 --- a/src/panels/config/server_control/ha-config-server-control.ts +++ b/src/panels/config/server_control/ha-config-server-control.ts @@ -8,11 +8,11 @@ import "../../../components/buttons/ha-call-service-button"; import "../../../components/ha-card"; import { checkCoreConfig } from "../../../data/core"; import { domainToName } from "../../../data/integration"; +import "../../../layouts/hass-subpage"; import "../../../layouts/hass-tabs-subpage"; import { haStyle } from "../../../resources/styles"; import { HomeAssistant, Route } from "../../../types"; import "../ha-config-section"; -import { configSections } from "../ha-panel-config"; @customElement("ha-config-server-control") export class HaConfigServerControl extends LitElement { @@ -49,12 +49,10 @@ export class HaConfigServerControl extends LitElement { protected render(): TemplateResult { return html` - @@ -203,7 +201,7 @@ export class HaConfigServerControl extends LitElement { ` : ""} - + `; } diff --git a/src/panels/config/users/ha-config-users.ts b/src/panels/config/users/ha-config-users.ts index d93a01748b..ba4d4746ff 100644 --- a/src/panels/config/users/ha-config-users.ts +++ b/src/panels/config/users/ha-config-users.ts @@ -157,7 +157,7 @@ export class HaConfigUsers extends LitElement { .narrow=${this.narrow} .route=${this.route} backPath="/config" - .tabs=${configSections.persons} + .tabs=${configSections.areas} .columns=${this._columns(this.narrow, this.hass.localize)} .data=${this._users} @row-click=${this._editUser} diff --git a/src/panels/config/zone/ha-config-zone.ts b/src/panels/config/zone/ha-config-zone.ts index 5533dcff03..23db9af09f 100644 --- a/src/panels/config/zone/ha-config-zone.ts +++ b/src/panels/config/zone/ha-config-zone.ts @@ -228,7 +228,7 @@ export class HaConfigZone extends SubscribeMixin(LitElement) { .narrow=${this.narrow} .route=${this.route} back-path="/config" - .tabs=${configSections.persons} + .tabs=${configSections.areas} > ${this.narrow ? html` diff --git a/src/translations/en.json b/src/translations/en.json index f165746cfb..d6c1811a0a 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1067,23 +1067,19 @@ "dashboard": { "devices": { "title": "Devices & Services", - "description": "Integrations, devices, entities and areas" + "description": "Integrations, devices, entities and helpers" }, "automations": { "title": "Automations & Scenes", - "description": "Manage automations, scenes, scripts and helpers" + "description": "Manage automations, scenes, scripts and blueprints" }, "backup": { "title": "Backup", "description": "Generate backups of your Home Assistant configuration" }, - "blueprints": { - "title": "Blueprints", - "description": "Pre-made automations and scripts by the community" - }, "supervisor": { - "title": "Add-ons, Backups & Supervisor", - "description": "Create backups, check logs or reboot your system" + "title": "Add-ons", + "description": "Extend the function around Home Assistant" }, "dashboards": { "title": "Dashboards", @@ -1098,16 +1094,24 @@ "description": "Trigger automations when an NFC tag, QR code, etc. is scanned" }, "people": { - "title": "People & Zones", - "description": "Manage the people and zones that Home Assistant tracks" + "title": "People", + "description": "Manage the people that Home Assistant tracks" + }, + "areas": { + "title": "Areas & Zones", + "description": "Manage areas & zones that Home Assistant tracks" }, "companion": { "title": "Companion App", "description": "Location and notifications" }, - "settings": { - "title": "Settings", - "description": "Basic settings, server controls, logs and info" + "system": { + "title": "System", + "description": "Create backups, check logs or reboot your system" + }, + "about": { + "title": "About", + "description": "Version, system health and links to documentation" } }, "common": { @@ -1117,6 +1121,7 @@ "learn_more": "Learn more" }, "updates": { + "caption": "Updates", "no_update_entities": { "title": "Unable to check for updates", "description": "You do not have any integrations that provide updates." @@ -1168,7 +1173,7 @@ } }, "backup": { - "caption": "[%key:ui::panel::config::dashboard::backup::title%]", + "caption": "Backups", "create_backup": "[%key:supervisor::backup::create_backup%]", "creating_backup": "Backup is currently being created", "download_backup": "[%key:supervisor::backup::download_backup%]", @@ -1458,6 +1463,9 @@ "internal_url_https_error_title": "Invalid local network URL", "internal_url_https_error_description": "You have configured an HTTPS certificate in Home Assistant. This means that your internal URL needs to be set to a domain covered by the certficate." }, + "hardware": { + "caption": "Hardware" + }, "info": { "caption": "Info", "copy_menu": "Copy menu", @@ -1764,7 +1772,7 @@ "geo_location": { "label": "Geolocation", "source": "Source", - "zone": "Zone", + "zone": "Location", "event": "Event", "enter": "Enter", "leave": "Leave" @@ -3087,6 +3095,15 @@ "tips": { "tip": "Tip!", "join": "Join the community on our {forums}, {twitter}, {discord}, {blog} or {newsletter}" + }, + "analytics": { + "caption": "Analytics" + }, + "network": { + "caption": "Network" + }, + "storage": { + "caption": "Storage" } }, "lovelace": { @@ -3680,7 +3697,7 @@ } }, "map": { - "edit_zones": "Edit Zones" + "edit_zones": "Edit zones" }, "profile": { "current_user": "You are currently logged in as {fullName}.",