Display layout page as overview (#235)

Move the UI tiles back to the right panel, and display
a placeholder in the home page's overview tab until a
layout page with an ID of "overview" has been created.
This is (normally?) temporary until a specially-designed
overview page has been implemented - discussion on
https://github.com/openhab/openhab-webui/issues/155).

Various styling fixes & clean up.

Move the onboarding cards to separate component
(not used for now).

Don't display the Back button in a page view if it's shown
on the sidebar.

Layout pages: switch the "add widget" action sheet from
grid to simple list with groups, headers and cancel button.

Signed-off-by: Yannick Schaus <github@schaus.net>
pull/243/head
Yannick Schaus 2020-04-27 09:16:55 +02:00 committed by GitHub
parent 8a41670ca4
commit 6d12fe94fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 263 additions and 210 deletions

View File

@ -1,4 +1,7 @@
{
"overview.title": "Welcome to openHAB!",
"overview.text": "Once your system is configured, create a layout page with the ID <code>\"overview\"</code> to display it here.",
"inbox.title": "The Inbox is empty",
"inbox.text": "Discovery results from your bindings that can be added as things will appear here.<br><br>You can also start a scan for a certain binding or add things manually with the button below.",

View File

@ -10,7 +10,6 @@
<f7-list-item link="/" class="logo" panel-close v-else>
<img src="../res/img/openhab-logo.png" width="100%">
</f7-list-item>
<!-- <f7-block-title>Sitemaps</f7-block-title> -->
<f7-list>
<!-- <f7-list-item v-for="sitemap in sitemaps" :animate="false" :key="sitemap.name"
:class="{ currentsection: currentUrl.indexOf('/sitemap/' + sitemap.name) >= 0 }"
@ -79,24 +78,18 @@
:class="{ currentsection: currentUrl.indexOf('/about') >= 0 }">
<f7-icon slot="media" ios="f7:question_circle_fill" aurora="f7:question_circle_fill" md="material:help" color="gray"></f7-icon>
</f7-list-item>
<!-- <f7-list-item v-if="loggedIn" link="/" title="Logout" @click="logout()" panel-close>
<f7-icon slot="media" ios="f7:square_arrow_right" md="material:exit_to_app" color="gray"></f7-icon>
</f7-list-item> -->
<!-- <f7-list-item title="Master-Details" view=".view-main" panel-close>
<f7-icon slot="media" ios="f7:exit" md="material:exit_to_app"></f7-icon>
</f7-list-item> -->
</f7-list>
<div class="account" v-if="ready">
<div class="display-flex justify-content-center">
<f7-button v-if="!loggedIn" large color="gray" icon-size="36" tooltip="Unlock Administration" icon-f7="lock_shield_fill" @click="authorize()" />
</div>
<f7-list v-if="$store.getters.user" class="admin-links" media-list>
<f7-list v-if="$store.getters.user" media-list>
<f7-list-item :title="$store.getters.user.name" :footer="serverDisplayUrl" io="f7:person_alt_circle_fill" link="/profile/" no-chevron panel-close view=".view-main"
:class="{ currentsection: currentUrl.indexOf('/profile') >= 0 }">
<f7-icon slot="media" size="36" ios="f7:person_alt_circle_fill" aurora="f7:person_alt_circle_fill" md="f7:person_alt_circle_fill" color="gray"></f7-icon>
</f7-list-item>
</f7-list>
<!-- <f7-link v-if="user" color="gray" icon-size="30" :text="accountLabel" tooltip="Sign out" icon-f7="person_alt_circle_fill" @click="logout()" /> -->
</div>
</f7-page>
</f7-panel>
@ -108,7 +101,7 @@
</f7-panel>
<!-- Your main view, should have "view-main" class -->
<f7-view main class="safe-areas" url="/" :master-detail-breakpoint="960" @routeChanged="console.log('hello')"></f7-view>
<f7-view main v-show="ready" class="safe-areas" url="/" :master-detail-breakpoint="960" @routeChanged="console.log('hello')"></f7-view>
<f7-login-screen id="my-login-screen" :opened="loginScreenOpened">
<f7-view name="login" v-if="$device.cordova">
@ -155,13 +148,12 @@
height 0
.panel-left
overflow-y scroll
scrollbar-width none /* Firefox */
-ms-overflow-style none /* IE 10+ */
.page
background #f5f5f5 !important
padding-bottom 4rem
padding-bottom calc(var(--f7-tabbar-labels-height) + var(--f7-safe-area-bottom))
.logo
margin-top var(--f7-safe-area-top)
list-style none
@ -178,11 +170,18 @@
color var(--f7-color-white) !important
.account
z-index 300
height 4rem
height calc(var(--f7-tabbar-labels-height) + var(--f7-safe-area-bottom))
background #f5f5f5 !important
position fixed
bottom calc(var(--f7-safe-area-bottom))
width 100%
.list
position absolute
bottom 0
left 0
width 100%
margin-bottom 0
height calc(var(--f7-tabbar-labels-height) + var(--f7-safe-area-bottom))
.theme-dark
.panel-left

View File

@ -0,0 +1,64 @@
<template>
<div>
<f7-card
v-show="showSetup"
title="Welcome to openHAB!"
content="Congratulations, your server is up and running! However, it is not configured yet. Follow the setup wizard and let it guide you through the initial configuration. (Note: the wizard could also be started automatically on launch if no package is detected - services/org.openhab.addons > package)."
>
<f7-card-footer>
<f7-link color="blue" @click="skipSetupWizard()">No thanks</f7-link>
<!-- <f7-button color="blue" fill raised login-screen-open="#login-screen">Start Setup Wizard</f7-button> -->
<f7-button color="blue" fill raised href="/setup-wizard/">Start Setup Wizard</f7-button>
</f7-card-footer>
</f7-card>
<f7-card title="Suggested Tasks" v-show="showTasks">
<f7-card-content :padding="false">
<ol>
<li>
<f7-link no-link-class color="blue" href="#">Install Bindings &amp; other add-ons</f7-link>
</li>
<li>
<f7-link no-link-class color="blue" href="#">Discover &amp; configure Things</f7-link>
</li>
<li>
<f7-link
no-link-class
color="blue"
href="#"
>Design your home's conceptually with the semantic model builder and link the Things to Items</f7-link>
</li>
<li>
<f7-link
no-link-class
color="blue"
href="#"
>Connect to openHAB Cloud for remote access and integration with voice assistants</f7-link>
</li>
</ol>
</f7-card-content>
<f7-card-footer>
<f7-link color="blue" @click="dismissTasks">Dismiss</f7-link>
</f7-card-footer>
</f7-card>
</div>
</template>
<script>
export default {
props: ['showSetup', 'showTasks'],
methods: {
skipSetupWizard () {
this.$f7.dialog.confirm(
'Are you sure? You currently only have a minimal set of features available and you will need to install all essential add-ons by hand!',
'Skip Setup Wizard',
() => {
// TODO
}
)
},
dismissTasks () {
// TODO
}
}
}
</script>

View File

@ -12,6 +12,7 @@
display inline-block
opacity 0.5
height calc(2*3rem + 50px)
z-index 100
.button
width calc(100% - var(--f7-typography-margin))
padding 3rem 0

View File

@ -1,6 +1,6 @@
<template>
<f7-page stacked name="HomePage" class="page-home" @page:init="onPageInit">
<f7-navbar :large="$f7.data.themeOptions.homeNavbar !== 'simple'" :large-transparent="true">
<f7-page stacked name="HomePage" class="page-home" @page:init="onPageInit" @page:afterin="onPageAfterIn" @page:beforeout="onPageBeforeOut">
<f7-navbar :large="$f7.data.themeOptions.homeNavbar !== 'simple'" :large-transparent="true" class="home-nav">
<f7-nav-left>
<f7-link icon-ios="f7:menu" icon-aurora="f7:menu" icon-md="material:menu" panel-open="left"></f7-link>
</f7-nav-left>
@ -11,9 +11,9 @@
<f7-nav-title>
{{title}}
</f7-nav-title>
<!-- <f7-nav-right>
<f7-nav-right>
<f7-link icon-ios="f7:sidebar_right" icon-aurora="f7:sidebar_right" icon-md="material:exit_to_app" panel-open="right"></f7-link>
</f7-nav-right> -->
</f7-nav-right>
</f7-navbar>
<f7-toolbar tabbar labels bottom>
@ -25,31 +25,31 @@
<f7-tabs :class="{ 'after-big-title': $f7.data.themeOptions.homeNavbar !== 'simple' }" v-if="items">
<f7-tab id="tab-overview" :tab-active="currentTab === 'overview'" @tab:show="() => this.currentTab = 'overview'">
<overview-tab v-if="currentTab === 'overview'" :items="items" />
<overview-tab v-if="currentTab === 'overview'" :context="context" :items="items" />
</f7-tab>
<f7-tab id="tab-locations" :tab-active="currentTab === 'locations'" @tab:show="() => this.currentTab = 'locations'">
<locations-tab v-if="currentTab === 'locations'" :semantic-items="semanticItems" />
<locations-tab v-if="currentTab === 'locations'" :context="context" :semantic-items="semanticItems" />
</f7-tab>
<f7-tab id="tab-equipments" :tab-active="currentTab === 'equipments'" @tab:show="() => this.currentTab = 'equipments'">
<equipments-tab v-if="currentTab === 'equipments'" :semantic-items="semanticItems" />
<equipments-tab v-if="currentTab === 'equipments'" :context="context" :semantic-items="semanticItems" />
</f7-tab>
<f7-tab id="tab-properties" :tab-active="currentTab === 'properties'" @tab:show="() => this.currentTab = 'properties'">
<properties-tab v-if="currentTab === 'properties'" :semantic-items="semanticItems" />
<properties-tab v-if="currentTab === 'properties'" :context="context" :semantic-items="semanticItems" />
</f7-tab>
</f7-tabs>
</f7-page>
</template>
<style lang="stylus">
.theme-filled .home-title-large .title-large-text
.theme-filled .home-nav .home-title-large .title-large-text
color var(--f7-text-color)
.theme-filled .navbar-large:not(.navbar-large-collapsed) .link.icon-only
.theme-filled .home-nav.navbar-large:not(.navbar-large-collapsed) .link.icon-only
color var(--f7-theme-color)
transition color 0.3s
.theme-filled .navbar-large.navbar-large-collapsed .link.icon-only
.theme-filled .home-nav.navbar-large.navbar-large-collapsed .link.icon-only
color var(--f7-navbar-link-color)
transition color 0.3s
.home-title-large .title-large-text
.home-nav .home-title-large .title-large-text
line-height 0.95
.today
position absolute
@ -84,99 +84,12 @@ export default {
semanticItems: {}
}
},
created () {
this.$oh.api.get('/rest/items?metadata=semantics').then((data) => {
this.items = data
// get the location items
this.semanticItems.locations = data.filter((item, index, items) => {
return item.metadata && item.metadata.semantics &&
item.metadata.semantics.value.indexOf('Location_') === 0
}).sort((a, b) => {
const titleA = a.label || a.name
const titleB = b.label || b.name
return titleA.localeCompare(titleB)
}).map((l) => {
return {
item: l,
properties: data.filter((item, index, items) => {
return item.metadata && item.metadata.semantics &&
item.metadata.semantics && item.metadata.semantics.config &&
item.metadata.semantics.config.relatesTo &&
item.metadata.semantics.config.hasLocation === l.name
}),
equipments: data.filter((item, index, items) => {
return item.metadata && item.metadata.semantics &&
item.metadata.semantics && item.metadata.semantics.config &&
item.metadata.semantics.value.indexOf('Equipment_') === 0 &&
item.metadata.semantics.config.hasLocation === l.name
}).map((item) => {
return {
item: item,
points: data.filter((item2, index, items) => {
return item2.metadata && item2.metadata.semantics &&
item2.metadata.semantics && item2.metadata.semantics.config &&
item2.metadata.semantics.config.isPointOf === item.name
})
}
})
}
})
// get the equipment items
this.semanticItems.equipments = data.filter((item, index, items) => {
return item.metadata && item.metadata.semantics &&
item.metadata.semantics &&
item.metadata.semantics.value.indexOf('Equipment_') === 0
}).reduce((prev, item, i, properties) => {
const equipmentType = item.metadata.semantics.value.split('_')[1]
if (!prev[equipmentType]) prev[equipmentType] = []
const equipmentWithPoints = {
item: item,
points: data.filter((item2, index, items) => {
return item2.metadata && item2.metadata.semantics &&
item2.metadata.semantics && item2.metadata.semantics.config &&
item2.metadata.semantics.config.isPointOf === item.name
})
}
prev[equipmentType].push(equipmentWithPoints)
return prev
}, {})
// get the property items
this.semanticItems.properties = data.filter((item, index, items) => {
return item.metadata && item.metadata.semantics &&
item.metadata.semantics && item.metadata.semantics.config &&
item.metadata.semantics.config.relatesTo
}).reduce((prev, item, i, properties) => {
const property = item.metadata.semantics.config.relatesTo.split('_')[1]
if (!prev[property]) prev[property] = []
prev[property].push(item)
return prev
}, {})
})
},
methods: {
onPageInit () {
this.$f7.panel.get('left').enableVisibleBreakpoint()
},
skipSetupWizard () {
const vm = this
this.$f7.dialog.confirm(
'Are you sure? You currently only have a minimal set of features available and you will need to install all essential add-ons by hand!',
'Skip Setup Wizard',
() => {
vm.showSetup = false
}
)
},
dismissTasks () {
this.showTasks = false
},
displayCards () {
setTimeout(() => { this.showCards = true }, 3000)
}
},
computed: {
context () {
return {
store: this.$store.getters.trackedItems
}
},
title () {
switch (this.currentTab) {
case 'overview':
@ -191,6 +104,89 @@ export default {
return 'Home'
}
}
},
methods: {
onPageAfterIn () {
this.$store.dispatch('startTrackingStates')
this.load()
},
onPageBeforeOut () {
this.$store.dispatch('stopTrackingStates')
},
onPageInit () {
this.$f7.panel.get('left').enableVisibleBreakpoint()
},
load () {
this.$oh.api.get('/rest/items?metadata=semantics').then((data) => {
this.items = data
// get the location items
this.semanticItems.locations = data.filter((item, index, items) => {
return item.metadata && item.metadata.semantics &&
item.metadata.semantics.value.indexOf('Location_') === 0
}).sort((a, b) => {
const titleA = a.label || a.name
const titleB = b.label || b.name
return titleA.localeCompare(titleB)
}).map((l) => {
return {
item: l,
properties: data.filter((item, index, items) => {
return item.metadata && item.metadata.semantics &&
item.metadata.semantics && item.metadata.semantics.config &&
item.metadata.semantics.config.relatesTo &&
item.metadata.semantics.config.hasLocation === l.name
}),
equipments: data.filter((item, index, items) => {
return item.metadata && item.metadata.semantics &&
item.metadata.semantics && item.metadata.semantics.config &&
item.metadata.semantics.value.indexOf('Equipment_') === 0 &&
item.metadata.semantics.config.hasLocation === l.name
}).map((item) => {
return {
item: item,
points: data.filter((item2, index, items) => {
return item2.metadata && item2.metadata.semantics &&
item2.metadata.semantics && item2.metadata.semantics.config &&
item2.metadata.semantics.config.isPointOf === item.name
})
}
})
}
})
// get the equipment items
this.semanticItems.equipments = data.filter((item, index, items) => {
return item.metadata && item.metadata.semantics &&
item.metadata.semantics &&
item.metadata.semantics.value.indexOf('Equipment_') === 0
}).reduce((prev, item, i, properties) => {
const equipmentType = item.metadata.semantics.value.split('_')[1]
if (!prev[equipmentType]) prev[equipmentType] = []
const equipmentWithPoints = {
item: item,
points: data.filter((item2, index, items) => {
return item2.metadata && item2.metadata.semantics &&
item2.metadata.semantics && item2.metadata.semantics.config &&
item2.metadata.semantics.config.isPointOf === item.name
})
}
prev[equipmentType].push(equipmentWithPoints)
return prev
}, {})
// get the property items
this.semanticItems.properties = data.filter((item, index, items) => {
return item.metadata && item.metadata.semantics &&
item.metadata.semantics && item.metadata.semantics.config &&
item.metadata.semantics.config.relatesTo
}).reduce((prev, item, i, properties) => {
const property = item.metadata.semantics.config.relatesTo.split('_')[1]
if (!prev[property]) prev[property] = []
prev[property].push(item)
return prev
}, {})
})
}
}
}
</script>

View File

@ -1,63 +1,40 @@
<template>
<div>
<div class="hint-apps" v-if="!overviewPage && !$store.getters.user">
<f7-icon class="float-right margin-left" f7="arrow_turn_right_up" size="40"></f7-icon>
<p><em class="">Open the apps panel to launch other interfaces</em></p>
</div>
<f7-block class="block-narrow">
<habot v-if="showHABot" />
<other-apps v-if="showApps" />
<f7-col>
<f7-card
v-show="showSetup"
title="Welcome to openHAB!"
content="Congratulations, your server is up and running! However, it is not configured yet. Follow the setup wizard and let it guide you through the initial configuration. (Note: the wizard could also be started automatically on launch if no package is detected - services/org.openhab.addons > package)."
>
<f7-card-footer>
<f7-link color="blue" @click="skipSetupWizard()">No thanks</f7-link>
<!-- <f7-button color="blue" fill raised login-screen-open="#login-screen">Start Setup Wizard</f7-button> -->
<f7-button color="blue" fill raised href="/setup-wizard/">Start Setup Wizard</f7-button>
</f7-card-footer>
</f7-card>
<f7-card title="Suggested Tasks" v-show="showTasks">
<f7-card-content :padding="false">
<ol>
<li>
<f7-link no-link-class color="blue" href="#">Install Bindings &amp; other add-ons</f7-link>
</li>
<li>
<f7-link no-link-class color="blue" href="#">Discover &amp; configure Things</f7-link>
</li>
<li>
<f7-link
no-link-class
color="blue"
href="#"
>Design your home's conceptually with the semantic model builder and link the Things to Items</f7-link>
</li>
<li>
<f7-link
no-link-class
color="blue"
href="#"
>Connect to openHAB Cloud for remote access and integration with voice assistants</f7-link>
</li>
</ol>
</f7-card-content>
<f7-card-footer>
<f7-link color="blue" @click="dismissTasks">Dismiss</f7-link>
</f7-card-footer>
</f7-card>
</f7-col>
</f7-block>
<f7-block v-if="showCards && !ready" class="text-align-center">
<f7-block v-if="!$store" class="text-align-center">
<f7-preloader></f7-preloader>
<div>Loading...</div>
</f7-block>
<div class="demo-expandable-cards" v-if="showCards && ready">
<h2 class="home-header">
<!-- <f7-icon aurora="f7:star_fill" ios="f7:star_fill" md="material:star" size="25" style="vertical-align: sub" /> -->
Now
</h2>
<component :is="overviewPage.component" v-if="overviewPage" :context="overviewPageContext" :class="{notready: !ready}" @command="onCommand" />
<div class="empty" v-else>
<empty-state-placeholder icon="rocket" title="overview.title" text="overview.text" />
<f7-row class="display-flex justify-content-center">
<f7-button large fill color="blue" external href="https://next.openhab.org/docs/" target="_blank">Documentation</f7-button>
<span style="width: 8px"></span>
<f7-button large color="blue" external href="https://next.openhab.org/docs/tutorial/" target="_blank">Tutorial</f7-button>
</f7-row>
<div class="hint-signin" v-if="!$store.getters.user">
<p class="padding-left"><em>Click on the shield to sign in as an administrator</em></p>
<f7-icon f7="arrow_down_left" size="40"></f7-icon>
</div>
</div>
<!-- <h2 class="home-header">
Now
</h2> -->
<!-- <div class="demo-expandable-cards" v-if="showCards && ready">
<expandable-card color="teal" header="gauge" />
<h2 class="home-header">Favorites</h2>
<h3 class="home-header">Scenes</h3>
@ -67,18 +44,13 @@
<expandable-card color="blue" header="temperature" title="Thermostat Downstairs" />
<expandable-card color="green" header="gauge" />
<expandable-card color="deeppurple" />
<!-- <expandable-card color="gray" /> -->
<expandable-card color="black" header="player" title="SONOS Multiroom" />
<expandable-card color="blue" header="image" title="Webcam Front Door" />
<!-- <expandable-card color="orange" />
<expandable-card color="deeporange" />
<expandable-card color="pink" />
<expandable-card color="lightblue" /> -->
</div>
</div> -->
<f7-block v-if="showCards && ready">
<!-- <f7-block v-if="showCards && ready">
<f7-button small @click="showSetup = true; showTasks = true; showCards = false; showHABot = false">Simulate first-time run</f7-button>
</f7-block>
</f7-block> -->
</div>
</template>
@ -88,59 +60,63 @@
display block
width calc(100% - 30px)
margin-left calc(var(--f7-block-padding-horizontal) + var(--f7-safe-area-left))
.hint-apps
position absolute
top calc(var(--f7-page-navbar-offset, 0px) + var(--f7-page-content-extra-padding-top, 0px))
right 1rem
width 60%
p
text-align right
.hint-signin
position absolute
bottom calc(var(--f7-tabbar-labels-height) + var(--f7-safe-area-bottom))
width 50%
height 10rem
left 1rem
p
margin-left 40px
</style>
<script>
import ExpandableCard from '../../components/expandable-card.vue'
// import ExpandableCard from '../../components/expandable-card.vue'
// import OtherApps from '../../components/home/other-apps.vue'
import OhLayoutPage from '@/components/widgets/layout/oh-layout-page.vue'
import Habot from '../../components/home/habot.vue'
import OtherApps from '../../components/home/other-apps.vue'
export default {
props: ['items'],
props: ['context', 'items'],
components: {
ExpandableCard,
OtherApps,
OhLayoutPage,
Habot
},
data () {
return {
showSetup: false,
showTasks: false,
showApps: true,
showApps: false,
showCards: false,
showHABot: false,
ready: true
}
},
created () {
// if (Object.keys(this.items).length === 0) {
// this.showSetup = true
// } else {
// this.showCards = true
// this.showHABot = true
// setTimeout(() => { this.ready = true }, 1000)
// }
computed: {
overviewPage () {
const page = this.$store.getters.page('overview')
if (!page) return null
if (page.component !== 'oh-layout-page') return null
return page
},
overviewPageContext () {
return {
component: this.overviewPage,
store: this.context.store
}
}
},
methods: {
skipSetupWizard () {
const vm = this
this.$f7.dialog.confirm(
'Are you sure? You currently only have a minimal set of features available and you will need to install all essential add-ons by hand!',
'Skip Setup Wizard',
() => {
vm.showSetup = false
vm.showTasks = true
}
)
},
dismissTasks () {
this.showTasks = false
this.showHABot = true
this.showCards = true
setTimeout(() => { this.ready = true }, 1000)
},
displayCards () {
setTimeout(() => { this.showCards = true }, 3000)
onCommand (itemName, command) {
this.$store.dispatch('sendCommand', { itemName, command })
}
}
}

View File

@ -1,7 +1,7 @@
<template>
<f7-page @page:afterin="onPageAfterIn" @page:beforeout="onPageBeforeOut" hide-bars-on-scroll>
<f7-navbar :back-link="(deep) ? 'Back' : undefined">
<f7-nav-left v-if="!deep">
<f7-navbar :back-link="(showBackButton) ? 'Back' : undefined">
<f7-nav-left v-if="!showBackButton">
<f7-link icon-ios="f7:menu" icon-aurora="f7:menu" icon-md="material:menu" panel-open="left"></f7-link>
</f7-nav-left>
<f7-nav-title>{{(ready) ? page.config.label : ''}}</f7-nav-title>
@ -82,6 +82,9 @@ export default {
},
isAdmin () {
return this.ready && this.$store.getters.isAdmin
},
showBackButton () {
return this.deep && (!this.page || !this.page.config.sidebar)
}
},
methods: {

View File

@ -322,20 +322,31 @@ export default {
const standardWidgetOptions = Object.keys(StandardWidgets).map((k) => {
return {
text: StandardWidgets[k].widget.label,
color: 'blue',
onClick: () => doAddWidget(StandardWidgets[k].widget.name)
}
})
const customWidgetOptions = this.$store.state.components.widgets.map((w) => {
return {
text: w.uid,
color: 'blue',
onClick: () => doAddWidget('widget:' + w.uid)
}
})
actions = this.$f7.actions.create({
grid: true,
// grid: true,
buttons: [
standardWidgetOptions,
customWidgetOptions
[
{ label: true, text: 'Standard Library' },
...standardWidgetOptions
],
[
{ label: true, text: 'Personal Widgets' },
...customWidgetOptions
],
[
{ color: 'red', 'text': 'Cancel', close: true }
]
]
}).open()
}