2525 @update:sort =" updateSort"
2626 >
2727 <template #actions >
28+ <ff-popover button-text =" Filters" button-kind =" secondary" >
29+ <template #panel =" { close } " >
30+ <section >
31+ <popover-item
32+ title =" Fleet Mode"
33+ @click =" onFilterClick('fleetMode', close)"
34+ >
35+ <template #icon >
36+ <ff-checkbox v-model =" deviceModeFilters.fleetMode" style =" top : -8px ;" />
37+ </template >
38+ </popover-item >
39+ <popover-item
40+ title =" Developer Mode"
41+ @click =" onFilterClick('developerMode', close)"
42+ >
43+ <template #icon >
44+ <ff-checkbox v-model =" deviceModeFilters.developerMode" style =" top : -8px ;" />
45+ </template >
46+ </popover-item >
47+ </section >
48+ </template >
49+ </ff-popover >
2850 <DropdownMenu v-if =" hasPermission (' team:device:bulk-delete' , applicationContext ) || hasPermission (' team:device:bulk-edit' , applicationContext )" :disabled =" ! checkedDevices ?.length " data-el="bulk-actions-dropdown" buttonClass="ff-btn ff-btn--secondary" :options =" bulkActionsDropdownOptions " >Actions</DropdownMenu >
2951 <ff-button
3052 v-if =" displayingInstance && hasPermission('project:snapshot:create', applicationContext)"
@@ -325,6 +347,7 @@ import DeviceLink from '../pages/application/components/cells/DeviceLink.vue'
325347import Snapshot from ' ../pages/application/components/cells/Snapshot.vue'
326348
327349import DeviceLastSeenCell from ' ../pages/device/components/DeviceLastSeenCell.vue'
350+ import DeviceModeBadge from ' ../pages/device/components/DeviceModeBadge.vue'
328351import SnapshotAssignDialog from ' ../pages/instance/VersionHistory/Snapshots/dialogs/SnapshotAssignDialog.vue'
329352import InstanceStatusBadge from ' ../pages/instance/components/InstanceStatusBadge.vue'
330353import DeviceAssignApplicationDialog from ' ../pages/team/Devices/dialogs/DeviceAssignApplicationDialog.vue'
@@ -333,6 +356,9 @@ import DeviceCredentialsDialog from '../pages/team/Devices/dialogs/DeviceCredent
333356import TeamDeviceCreateDialog from ' ../pages/team/Devices/dialogs/TeamDeviceCreateDialog.vue'
334357
335358import Alerts from ' ../services/alerts.js'
359+ import FfPopover from ' ../ui-components/components/Popover.vue'
360+ import PopoverItem from ' ../ui-components/components/PopoverItem.vue'
361+ import FfCheckbox from ' ../ui-components/components/form/Checkbox.vue'
336362
337363import { debounce } from ' ../utils/eventHandling.js'
338364import { createPollTimer } from ' ../utils/timers.js'
@@ -346,6 +372,9 @@ const POLL_TIME = 10000
346372export default {
347373 name: ' DevicesBrowser' ,
348374 components: {
375+ FfCheckbox,
376+ PopoverItem,
377+ FfPopover,
349378 ClockIcon,
350379 DeviceAssignApplicationDialog,
351380 DeviceAssignInstanceDialog,
@@ -401,7 +430,11 @@ export default {
401430 },
402431 /** @type { import('../utils/timers.js').PollTimer } */
403432 pollTimer: null ,
404- deviceEditModalOpened: false
433+ deviceEditModalOpened: false ,
434+ deviceModeFilters: {
435+ fleetMode: false ,
436+ developerMode: false
437+ }
405438 }
406439 },
407440 computed: {
@@ -413,6 +446,7 @@ export default {
413446 { label: ' Remote Instance' , key: ' name' , sortable: ! this .moreThanOnePage , component: { is: markRaw (DeviceLink) } },
414447 { label: ' Type' , key: ' type' , class: [' w-48' ], sortable: ! this .moreThanOnePage },
415448 { label: ' Last Seen' , key: ' lastSeenAt' , class: [' w-48' ], sortable: ! this .moreThanOnePage , component: { is: markRaw (DeviceLastSeenCell) } },
449+ { label: ' Mode' , key: ' mode' , class: [' w-48' ], sortable: true , component: { is: markRaw (DeviceModeBadge) } },
416450 { label: ' Last Known Status' , class: [' w-32' ], component: { is: markRaw (InstanceStatusBadge), map: { instanceId: ' id' }, extraProps: { instanceType: ' device' } } }
417451 ]
418452
@@ -548,12 +582,19 @@ export default {
548582 * - property: which filter row is being applied, e.g. status or lastseen
549583 * - bucket: which value of this property are we filtering on from the buckets in the status bar
550584 */
551- applyFilter (filter ) {
585+ applyFilter (filter , shouldClearDeviceModeFilters = true ) {
552586 this .filter = filter
553587
554588 if (this .unfilteredHasMoreThanOnePage ) {
555589 this .doFilterServerSide ()
556590 }
591+
592+ if (shouldClearDeviceModeFilters) {
593+ this .deviceModeFilters = {
594+ fleetMode: false ,
595+ developerMode: false
596+ }
597+ }
557598 },
558599
559600 updateSearch (searchTerm ) {
@@ -789,6 +830,29 @@ export default {
789830 }
790831
791832 return ' Unassigned'
833+ },
834+
835+ onFilterClick (filter , closeCallback ) {
836+ const compare = filter === ' fleetMode' ? ' autonomous' : ' developer'
837+ this .deviceModeFilters [filter] = ! this .deviceModeFilters [filter]
838+
839+ this .applyFilter (
840+ {
841+ devices: Array .from (this .devices .values ())
842+ .filter ((device ) => ! this .deviceModeFilters [filter] ? true : device .mode === compare)
843+ .map (device => device .id ),
844+ property: ' mode'
845+ },
846+ false
847+ )
848+
849+ // resetting filters because we can't have multiple filters applied at once
850+ const filters = {
851+ fleetMode: false ,
852+ developerMode: false
853+ }
854+ filters[filter] = this .deviceModeFilters [filter]
855+ this .deviceModeFilters = filters
792856 }
793857 }
794858}
0 commit comments