Skip to content
132 changes: 127 additions & 5 deletions frontend/src/pages/device/Editor/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
<div ref="resizeTarget" class="ff--immersive-editor-wrapper" :class="{resizing: isEditorResizing}">
<EditorWrapper
:url="device?.editor?.url"
:disable-events="isEditorResizing"
:device="device"
/>

<DrawerTrigger :is-hidden="drawer.open" @toggle="toggleDrawer" />

<section
class="tabs-wrapper drawer"
:class="{'open': drawer.open, resizing: isEditorResizing}"
Expand All @@ -16,25 +19,65 @@
<resize-bar
@mousedown="startEditorResize"
/>

<div class="header">
<div class="logo">
<router-link title="Back to remote instance overview" :to="{ name: 'device-overview', params: {id: device.id} }">
<ArrowLeftIcon class="ff-btn--icon" />
</router-link>
</div>
<ff-tabs :tabs="navigation" class="tabs" />
<div class="side-actions">
<button
title="Close drawer"
type="button"
class="close-drawer-button"
aria-label="Close drawer"
@click="toggleDrawer"
>
<XIcon class="ff-btn--icon" />
</button>
</div>
</div>

<ff-page :no-padding="isExpertRoute">
<router-view
:device="device"
:instance="device.instance"
/>
</ff-page>
</section>
</div>
</template>

<script>

import { ArrowLeftIcon, XIcon } from '@heroicons/vue/solid/index.js'
import semver from 'semver'
import { mapActions } from 'vuex'
import { mapActions, mapGetters, mapState } from 'vuex'

import deviceApi from '../../../api/devices.js'
import ResizeBar from '../../../components/ResizeBar.vue'
import ExpertTabIcon from '../../../components/icons/ff-minimal-grey.js'
import DrawerTrigger from '../../../components/immersive-editor/DrawerTrigger.vue'
import EditorWrapper from '../../../components/immersive-editor/RemoteInstanceEditorWrapper.vue'
import { useDrawerHelper } from '../../../composables/DrawerHelper.js'
import { useResizingHelper } from '../../../composables/ResizingHelper.js'
import FfPage from '../../../layouts/Page.vue'
import Alerts from '../../../services/alerts.js'

const DRAWER_DEFAULT_WIDTH = 550 // Default drawer width in pixels
const DRAWER_MAX_VIEWPORT_MARGIN = 200 // Space to preserve when drawer is at max width
const DRAWER_MAX_WIDTH_RATIO = 0.9 // Maximum drawer width as percentage of viewport (desktop)
const DRAWER_MIN_WIDTH = 310 // Minimum drawer width in pixels

export default {
name: 'DeviceEditor',
components: {
XIcon,
ArrowLeftIcon,
FfPage,
DrawerTrigger,
ResizeBar,
EditorWrapper
},
Expand All @@ -54,11 +97,13 @@ export default {
startResize: startEditorResize,
widthStyle: editorWidthStyle,
bindResizer: bindDrawerResizer,
isResizing: isEditorResizing
isResizing: isEditorResizing,
setEditorWidth: setDeviceEditorWidth
} = useResizingHelper()

return {
startEditorResize,
setDeviceEditorWidth,
bindDrawerResizer,
editorWidthStyle,
drawer,
Expand All @@ -82,7 +127,64 @@ export default {
}
},
computed: {

...mapState('account', ['features']),
...mapGetters('account', ['featuresCheck']),
isExpertRoute () {
return this.$route.name === 'device-editor-expert'
},
isDevModeAvailable: function () {
return !!this.features.deviceEditor
},
navigation () {
return [
{
label: 'Expert',
to: { name: 'device-editor-expert', params: { id: this.device.id } },
tag: 'device-expert',
icon: ExpertTabIcon,
hidden: !this.featuresCheck.isExpertAssistantFeatureEnabled
},
{
label: 'Overview',
to: { name: 'device-editor-overview' },
tag: 'device-overview'
},
{
label: 'Version History',
to: {
name: 'device-editor-version-history',
params: { id: this.$route.params.id }
},
tag: 'version-history'
},
{
label: 'Audit Log',
to: { name: 'device-editor-audit-log' },
tag: 'device-audit-log'
},
{
label: 'Node-RED Logs',
to: { name: 'device-editor-logs' },
tag: 'device-logs'
},
{
label: 'Performance',
to: { name: 'device-editor-performance' },
tag: 'device-performance'
},
{
label: 'Settings',
to: { name: 'device-editor-settings' },
tag: 'device-settings'
},
{
label: 'Developer Mode',
to: { name: 'device-editor-developer-mode' },
tag: 'device-devmode',
hidden: !(this.isDevModeAvailable && this.device.mode === 'developer')
}
]
}
},
watch: {
device (device) {
Expand All @@ -96,7 +198,28 @@ export default {
}
},
mounted () {
this.loadDevice().catch(err => err)
this.loadDevice()
.then(() => {
this.bindDrawer({
containerEl: this.$el,
getInstance: () => this.device,
setEditorWidth: this.setDeviceEditorWidth,
defaultWidth: DRAWER_DEFAULT_WIDTH
})
})
.then(() => {
this.bindDrawerResizer({
component: this.$refs.resizeTarget,
initialWidth: DRAWER_DEFAULT_WIDTH,
minWidth: DRAWER_MIN_WIDTH,
maxViewportMarginX: DRAWER_MAX_VIEWPORT_MARGIN,
maxWidthRatio: DRAWER_MAX_WIDTH_RATIO
})
})
.catch(err => err)
.finally(() => {
this.runInitialTease()
})
},
methods: {
...mapActions('context', { setContextDevice: 'setDevice' }),
Expand All @@ -105,7 +228,6 @@ export default {
this.device = await deviceApi.getDevice(this.$route.params.id)
} catch (err) {
if (err.status === 403) {
clearTimeout(this.openTunnelTimeout)
return this.$router.push({ name: 'Home' })
}
} finally {
Expand Down
30 changes: 29 additions & 1 deletion frontend/src/pages/device/Editor/routes.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import store from '../../../store/index.js'
import { children } from '../routes.js'

import DeviceEditor from './index.vue'

const renameRoute = (child) => {
return {
...child,
name: child.name.replace('device-', 'device-editor-'),
...(child.children ? { children: child.children.map(renameRoute) } : {})
}
}

export default [
{
path: '/device/:id/editor',
Expand All @@ -8,6 +19,23 @@ export default [
meta: {
title: 'Device - Editor',
layout: 'plain'
}
},
redirect: to => {
const name = store.getters['account/featuresCheck'].isExpertAssistantFeatureEnabled
? 'device-editor-expert'
: 'device-editor-overview'
return { name, params: { id: to.params.id } }
},
children: [
...children.map(child => renameRoute(child)),
{
path: 'expert',
name: 'device-editor-expert',
component: () => import('../../../components/expert/Expert.vue'),
meta: {
title: 'Remote Instance - Expert'
}
}
]
}
]
11 changes: 9 additions & 2 deletions frontend/src/pages/device/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ const children = [
meta: {
title: 'Device - Settings'
},
redirect: { name: 'device-settings-general' },
redirect: to => {
return to.name.startsWith('device-editor-') ? { name: 'device-editor-settings-general' } : { name: 'device-settings-general' }
},
children: [
{
name: 'device-settings-general',
Expand Down Expand Up @@ -92,7 +94,12 @@ const children = [
},
redirect: to => {
const features = store.getters['account/featuresCheck']
const name = features.isTimelineFeatureEnabled ? 'device-version-history-timeline' : 'device-snapshots'
let name = features.isTimelineFeatureEnabled ? 'device-version-history-timeline' : 'device-snapshots'

if (to.name.startsWith('device-editor-')) { // redirect to immersive mode when needed
name = name.replace('device-', 'device-editor-')
}

return {
name,
params: to.params
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/instance/Editor/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export default [
name: 'instance-editor-expert',
component: () => import('../../../components/expert/Expert.vue'),
meta: {
title: 'Instance - Expert'
title: 'Hosted Instance - Expert'
}
}
]
Expand Down
Loading