Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,21 @@
"rules": {
// plugin:vue
"vue/html-indent": ["warn", 4],
// plugin:vue - style rules

// plugin:vue - style rules
"vue/max-attributes-per-line": "off",
"vue/attribute-hyphenation": "off",
"vue/singleline-html-element-content-newline": "off",
"vue/component-definition-name-casing": "off",

// plugin:promise
"promise/always-return": "off" // common Vue.js pattern
"promise/always-return": "off", // common Vue.js pattern

// Frontend runs through webpack, not Node — Node-based resolution rules
// do not understand webpack aliases (@/) or .vue file resolution
"import/no-unresolved": "off",
"n/no-missing-import": "off",
"n/no-missing-require": "off"
}
},

Expand Down
4 changes: 3 additions & 1 deletion frontend/src/components/DevicesBrowser.vue
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,8 @@ import DevicesStatusBar from './charts/DeviceStatusBar.vue'
import AddDeviceToGroupDialog from './dialogs/device-group-management/AddDeviceToGroupDialog.vue'
import RemoveDeviceFromGroupDialog from './dialogs/device-group-management/RemoveDeviceFromGroupDialog.vue'

import { useUxDialogStore } from '@/stores/ux-dialog.js'

const POLL_TIME = 10000

export default {
Expand Down Expand Up @@ -459,8 +461,8 @@ export default {
computed: {
...mapState('account', ['team', 'teamMembership']),
...mapState('ux/tours', ['tours']),
...mapState('ux/dialog', ['dialog']),
...mapGetters('account', ['featuresCheck']),
dialog () { return useUxDialogStore().dialog },
columns () {
const columns = [
{ label: 'Remote Instance', key: 'name', sortable: !this.moreThanOnePage, component: { is: markRaw(DeviceLink) } },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,18 @@

<script>
import { ChipIcon } from '@heroicons/vue/outline'
import { mapActions, mapState } from 'vuex'
import ApplicationAPI from '../../../api/application.js'
import { pluralize } from '../../../composables/String.js'
import FfLoading from '../../Loading.vue'
import NoticeBanner from '../../notices/NoticeBanner.vue'
import DeployNotice from '../../notices/device-groups/DeployNotice.vue'
import DeviceList from './components/device-list.vue'
import { useUxDialogStore } from '@/stores/ux-dialog.js'
export default {
name: 'AddDeviceToGroupDialog',
components: {
Expand Down Expand Up @@ -119,7 +121,7 @@ export default {
}
},
computed: {
...mapState('ux/dialog', ['dialog']),
dialog () { return useUxDialogStore().dialog },
application () {
if (!this.devicesBelongToSameApplication) return null
Expand Down Expand Up @@ -199,7 +201,7 @@ export default {
},
methods: {
pluralize,
...mapActions('ux/dialog', ['setDisablePrimary']),
setDisablePrimary (value) { useUxDialogStore().setDisablePrimary(value) },
async getDeviceGroups (application) {
this.loading = true
return ApplicationAPI.getDeviceGroups(application.id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,13 @@
</template>

<script>
import { mapState } from 'vuex'

import { pluralize } from '../../../../composables/String.js'

import FfDataTable from '../../../../ui-components/components/data-table/DataTable.vue'
import Accordion from '../../../Accordion.vue'

import { useUxDialogStore } from '@/stores/ux-dialog.js'

export default {
name: 'device-list',
components: {
Expand All @@ -50,7 +51,7 @@ export default {
},
emits: ['selection-removed'],
computed: {
...mapState('ux/dialog', ['dialog']),
dialog () { return useUxDialogStore().dialog },
columns () {
return [
{ label: 'Name', key: 'name', class: ['flex-grow'], sortable: true },
Expand Down
17 changes: 7 additions & 10 deletions frontend/src/mixins/Dialog.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
import { mapActions, mapState } from 'vuex'

import dialog from '../services/dialog.js'

import { useUxDialogStore } from '@/stores/ux-dialog.js'

export default {
computed: {
...mapState('ux/dialog', ['dialog'])
dialog () { return useUxDialogStore().dialog }
},
methods: {
...mapActions('ux/dialog', ['clearDialog']),
...mapActions('ux/dialog', {
handleDialog: 'showDialogHandlers'
}),
clearDialog (cancelled = false) {
useUxDialogStore().clearDialog(cancelled)
},
showDialogHandler (msg, onConfirm, onCancel) {
return this.handleDialog({
payload: msg, onConfirm, onCancel
})
return useUxDialogStore().showDialogHandlers({ payload: msg, onConfirm, onCancel })
}
},
mounted () {
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/store/modules/account/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { getTeamProperty } from '../../../composables/TeamProperties.js'
import router from '../../../routes.js'
import product from '../../../services/product.js'

import { useUxDialogStore } from '@/stores/ux-dialog.js'

// initial state
const initialState = () => ({
// Runtime settings
Expand Down Expand Up @@ -512,7 +514,7 @@ const actions = {
// Reset migrated Pinia stores — uncomment each line as its store is migrated
const pinia = getActivePinia()
if (pinia) {
// Task 1: useUxDialogStore().$reset()
useUxDialogStore().$reset()
// Task 2: useUxToursStore().$reset()
// Task 3: useUxNavigationStore().$reset()
// Task 4: useUxDrawersStore().$reset()
Expand Down
101 changes: 0 additions & 101 deletions frontend/src/store/modules/ux/dialog/index.js

This file was deleted.

3 changes: 1 addition & 2 deletions frontend/src/store/modules/ux/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import QueueIcon from '../../../components/icons/Queue.js'
import { hasALowerOrEqualTeamRoleThan, hasAMinimumTeamRoleOf, hasPermission } from '../../../composables/Permissions.js'
import { Roles } from '../../../utils/roles.js'

import dialog from './dialog/index.js'
import drawers from './drawers/index.js'
import tours from './tours/index.js'

Expand Down Expand Up @@ -514,7 +513,7 @@ const actions = {

export default {
namespaced: true,
modules: { dialog, drawers, tours },
modules: { drawers, tours },
state,
initialState: initialState(),
getters,
Expand Down
1 change: 1 addition & 0 deletions frontend/src/stores/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
// Barrel export — add store exports here as each task is merged
export { useUxDialogStore } from './ux-dialog.js'
61 changes: 61 additions & 0 deletions frontend/src/stores/ux-dialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { defineStore } from 'pinia'

const initialDialogState = () => ({
boxClass: null,
cancelLabel: null,
canBeCanceled: true,
confirmLabel: null,
contentClass: '',
disablePrimary: false,
header: null,
html: null,
is: null,
kind: null,
onCancel: null,
onConfirm: null,
subHeader: null,
text: null,
textLines: null,
notices: []
})

export const useUxDialogStore = defineStore('ux-dialog', {
state: () => ({
dialog: initialDialogState()
}),
actions: {
clearDialog (cancelled = false) {
if (cancelled) {
this.dialog.onCancel?.()
}
this.dialog = initialDialogState()
},
showDialogHandlers ({ payload, onConfirm, onCancel }) {
this.clearDialog(false)

if (typeof payload === 'string') {
this.dialog.content = payload
} else {
this.dialog.header = payload.header
this.dialog.text = payload.text
this.dialog.textLines = payload.text?.split('\n')
this.dialog.html = payload.html
this.dialog.is = payload.is ?? undefined
this.dialog.confirmLabel = payload.confirmLabel
this.dialog.cancelLabel = payload.cancelLabel
this.dialog.kind = payload.kind
this.dialog.disablePrimary = payload.disablePrimary
this.dialog.notices = payload.notices
if (Object.prototype.hasOwnProperty.call(payload, 'canBeCanceled')) {
this.dialog.canBeCanceled = payload.canBeCanceled
}
this.dialog.boxClass = payload.boxClass
}
this.dialog.onConfirm = onConfirm
this.dialog.onCancel = onCancel
},
setDisablePrimary (value) {
this.dialog.disablePrimary = value
}
}
})
57 changes: 57 additions & 0 deletions frontend/src/stores/ux-dialog.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// frontend/src/tests/stores/ux-dialog.spec.js
import { createPinia, setActivePinia } from 'pinia'
import { beforeEach, describe, expect, it, vi } from 'vitest'

import { useUxDialogStore } from '@/stores/ux-dialog.js'

describe('ux-dialog store', () => {
beforeEach(() => {
setActivePinia(createPinia())
})

it('initializes with empty dialog state', () => {
const store = useUxDialogStore()
expect(store.dialog.header).toBeNull()
expect(store.dialog.text).toBeNull()
})

it('showDialogHandlers sets dialog properties from object payload', () => {
const store = useUxDialogStore()
const onConfirm = vi.fn()
store.showDialogHandlers({
payload: { header: 'Delete?', text: 'This cannot be undone.', kind: 'danger' },
onConfirm
})
expect(store.dialog.header).toBe('Delete?')
expect(store.dialog.kind).toBe('danger')
expect(store.dialog.onConfirm).toBe(onConfirm)
})

it('showDialogHandlers splits text into textLines', () => {
const store = useUxDialogStore()
store.showDialogHandlers({ payload: { text: 'Line 1\nLine 2' }, onConfirm: vi.fn() })
expect(store.dialog.textLines).toEqual(['Line 1', 'Line 2'])
})

it('clearDialog resets all dialog state', () => {
const store = useUxDialogStore()
store.dialog.header = 'Test'
store.clearDialog()
expect(store.dialog.header).toBeNull()
})

it('clearDialog(true) calls onCancel before resetting', () => {
const store = useUxDialogStore()
const onCancel = vi.fn()
store.dialog.onCancel = onCancel
store.clearDialog(true)
expect(onCancel).toHaveBeenCalledOnce()
expect(store.dialog.header).toBeNull()
})

it('setDisablePrimary updates the flag', () => {
const store = useUxDialogStore()
store.setDisablePrimary(true)
expect(store.dialog.disablePrimary).toBe(true)
})
})
Loading
Loading