Skip to content

Commit 49957b6

Browse files
authored
Merge pull request #6629 from FlowFuse/6599_add-the-drawer-and-expert-to-the-immersive-remote-instance
Add the drawer sub routes and the FF Expert to the immersive remote instance
2 parents 42a766e + 9801072 commit 49957b6

4 files changed

Lines changed: 166 additions & 9 deletions

File tree

frontend/src/pages/device/Editor/index.vue

Lines changed: 127 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22
<div ref="resizeTarget" class="ff--immersive-editor-wrapper" :class="{resizing: isEditorResizing}">
33
<EditorWrapper
44
:url="device?.editor?.url"
5+
:disable-events="isEditorResizing"
56
:device="device"
67
/>
78

9+
<DrawerTrigger :is-hidden="drawer.open" @toggle="toggleDrawer" />
10+
811
<section
912
class="tabs-wrapper drawer"
1013
:class="{'open': drawer.open, resizing: isEditorResizing}"
@@ -16,25 +19,65 @@
1619
<resize-bar
1720
@mousedown="startEditorResize"
1821
/>
22+
23+
<div class="header">
24+
<div class="logo">
25+
<router-link title="Back to remote instance overview" :to="{ name: 'device-overview', params: {id: device.id} }">
26+
<ArrowLeftIcon class="ff-btn--icon" />
27+
</router-link>
28+
</div>
29+
<ff-tabs :tabs="navigation" class="tabs" />
30+
<div class="side-actions">
31+
<button
32+
title="Close drawer"
33+
type="button"
34+
class="close-drawer-button"
35+
aria-label="Close drawer"
36+
@click="toggleDrawer"
37+
>
38+
<XIcon class="ff-btn--icon" />
39+
</button>
40+
</div>
41+
</div>
42+
43+
<ff-page :no-padding="isExpertRoute">
44+
<router-view
45+
:device="device"
46+
:instance="device.instance"
47+
/>
48+
</ff-page>
1949
</section>
2050
</div>
2151
</template>
2252

2353
<script>
2454
55+
import { ArrowLeftIcon, XIcon } from '@heroicons/vue/solid/index.js'
2556
import semver from 'semver'
26-
import { mapActions } from 'vuex'
57+
import { mapActions, mapGetters, mapState } from 'vuex'
2758
2859
import deviceApi from '../../../api/devices.js'
2960
import ResizeBar from '../../../components/ResizeBar.vue'
61+
import ExpertTabIcon from '../../../components/icons/ff-minimal-grey.js'
62+
import DrawerTrigger from '../../../components/immersive-editor/DrawerTrigger.vue'
3063
import EditorWrapper from '../../../components/immersive-editor/RemoteInstanceEditorWrapper.vue'
3164
import { useDrawerHelper } from '../../../composables/DrawerHelper.js'
3265
import { useResizingHelper } from '../../../composables/ResizingHelper.js'
66+
import FfPage from '../../../layouts/Page.vue'
3367
import Alerts from '../../../services/alerts.js'
3468
69+
const DRAWER_DEFAULT_WIDTH = 550 // Default drawer width in pixels
70+
const DRAWER_MAX_VIEWPORT_MARGIN = 200 // Space to preserve when drawer is at max width
71+
const DRAWER_MAX_WIDTH_RATIO = 0.9 // Maximum drawer width as percentage of viewport (desktop)
72+
const DRAWER_MIN_WIDTH = 310 // Minimum drawer width in pixels
73+
3574
export default {
3675
name: 'DeviceEditor',
3776
components: {
77+
XIcon,
78+
ArrowLeftIcon,
79+
FfPage,
80+
DrawerTrigger,
3881
ResizeBar,
3982
EditorWrapper
4083
},
@@ -54,11 +97,13 @@ export default {
5497
startResize: startEditorResize,
5598
widthStyle: editorWidthStyle,
5699
bindResizer: bindDrawerResizer,
57-
isResizing: isEditorResizing
100+
isResizing: isEditorResizing,
101+
setEditorWidth: setDeviceEditorWidth
58102
} = useResizingHelper()
59103
60104
return {
61105
startEditorResize,
106+
setDeviceEditorWidth,
62107
bindDrawerResizer,
63108
editorWidthStyle,
64109
drawer,
@@ -82,7 +127,64 @@ export default {
82127
}
83128
},
84129
computed: {
85-
130+
...mapState('account', ['features']),
131+
...mapGetters('account', ['featuresCheck']),
132+
isExpertRoute () {
133+
return this.$route.name === 'device-editor-expert'
134+
},
135+
isDevModeAvailable: function () {
136+
return !!this.features.deviceEditor
137+
},
138+
navigation () {
139+
return [
140+
{
141+
label: 'Expert',
142+
to: { name: 'device-editor-expert', params: { id: this.device.id } },
143+
tag: 'device-expert',
144+
icon: ExpertTabIcon,
145+
hidden: !this.featuresCheck.isExpertAssistantFeatureEnabled
146+
},
147+
{
148+
label: 'Overview',
149+
to: { name: 'device-editor-overview' },
150+
tag: 'device-overview'
151+
},
152+
{
153+
label: 'Version History',
154+
to: {
155+
name: 'device-editor-version-history',
156+
params: { id: this.$route.params.id }
157+
},
158+
tag: 'version-history'
159+
},
160+
{
161+
label: 'Audit Log',
162+
to: { name: 'device-editor-audit-log' },
163+
tag: 'device-audit-log'
164+
},
165+
{
166+
label: 'Node-RED Logs',
167+
to: { name: 'device-editor-logs' },
168+
tag: 'device-logs'
169+
},
170+
{
171+
label: 'Performance',
172+
to: { name: 'device-editor-performance' },
173+
tag: 'device-performance'
174+
},
175+
{
176+
label: 'Settings',
177+
to: { name: 'device-editor-settings' },
178+
tag: 'device-settings'
179+
},
180+
{
181+
label: 'Developer Mode',
182+
to: { name: 'device-editor-developer-mode' },
183+
tag: 'device-devmode',
184+
hidden: !(this.isDevModeAvailable && this.device.mode === 'developer')
185+
}
186+
]
187+
}
86188
},
87189
watch: {
88190
device (device) {
@@ -96,7 +198,28 @@ export default {
96198
}
97199
},
98200
mounted () {
99-
this.loadDevice().catch(err => err)
201+
this.loadDevice()
202+
.then(() => {
203+
this.bindDrawer({
204+
containerEl: this.$el,
205+
getInstance: () => this.device,
206+
setEditorWidth: this.setDeviceEditorWidth,
207+
defaultWidth: DRAWER_DEFAULT_WIDTH
208+
})
209+
})
210+
.then(() => {
211+
this.bindDrawerResizer({
212+
component: this.$refs.resizeTarget,
213+
initialWidth: DRAWER_DEFAULT_WIDTH,
214+
minWidth: DRAWER_MIN_WIDTH,
215+
maxViewportMarginX: DRAWER_MAX_VIEWPORT_MARGIN,
216+
maxWidthRatio: DRAWER_MAX_WIDTH_RATIO
217+
})
218+
})
219+
.catch(err => err)
220+
.finally(() => {
221+
this.runInitialTease()
222+
})
100223
},
101224
methods: {
102225
...mapActions('context', { setContextDevice: 'setDevice' }),
@@ -105,7 +228,6 @@ export default {
105228
this.device = await deviceApi.getDevice(this.$route.params.id)
106229
} catch (err) {
107230
if (err.status === 403) {
108-
clearTimeout(this.openTunnelTimeout)
109231
return this.$router.push({ name: 'Home' })
110232
}
111233
} finally {
Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
1+
import store from '../../../store/index.js'
2+
import { children } from '../routes.js'
3+
14
import DeviceEditor from './index.vue'
25

6+
const renameRoute = (child) => {
7+
return {
8+
...child,
9+
name: child.name.replace('device-', 'device-editor-'),
10+
...(child.children ? { children: child.children.map(renameRoute) } : {})
11+
}
12+
}
13+
314
export default [
415
{
516
path: '/device/:id/editor',
@@ -8,6 +19,23 @@ export default [
819
meta: {
920
title: 'Device - Editor',
1021
layout: 'plain'
11-
}
22+
},
23+
redirect: to => {
24+
const name = store.getters['account/featuresCheck'].isExpertAssistantFeatureEnabled
25+
? 'device-editor-expert'
26+
: 'device-editor-overview'
27+
return { name, params: { id: to.params.id } }
28+
},
29+
children: [
30+
...children.map(child => renameRoute(child)),
31+
{
32+
path: 'expert',
33+
name: 'device-editor-expert',
34+
component: () => import('../../../components/expert/Expert.vue'),
35+
meta: {
36+
title: 'Remote Instance - Expert'
37+
}
38+
}
39+
]
1240
}
1341
]

frontend/src/pages/device/routes.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ const children = [
3030
meta: {
3131
title: 'Device - Settings'
3232
},
33-
redirect: { name: 'device-settings-general' },
33+
redirect: to => {
34+
return to.name.startsWith('device-editor-') ? { name: 'device-editor-settings-general' } : { name: 'device-settings-general' }
35+
},
3436
children: [
3537
{
3638
name: 'device-settings-general',
@@ -92,7 +94,12 @@ const children = [
9294
},
9395
redirect: to => {
9496
const features = store.getters['account/featuresCheck']
95-
const name = features.isTimelineFeatureEnabled ? 'device-version-history-timeline' : 'device-snapshots'
97+
let name = features.isTimelineFeatureEnabled ? 'device-version-history-timeline' : 'device-snapshots'
98+
99+
if (to.name.startsWith('device-editor-')) { // redirect to immersive mode when needed
100+
name = name.replace('device-', 'device-editor-')
101+
}
102+
96103
return {
97104
name,
98105
params: to.params

frontend/src/pages/instance/Editor/routes.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export default [
7272
name: 'instance-editor-expert',
7373
component: () => import('../../../components/expert/Expert.vue'),
7474
meta: {
75-
title: 'Instance - Expert'
75+
title: 'Hosted Instance - Expert'
7676
}
7777
}
7878
]

0 commit comments

Comments
 (0)