diff --git a/pkg/sbombastic-image-vulnerability-scanner/config/table-headers.ts b/pkg/sbombastic-image-vulnerability-scanner/config/table-headers.ts index 61e2e95..88720e4 100644 --- a/pkg/sbombastic-image-vulnerability-scanner/config/table-headers.ts +++ b/pkg/sbombastic-image-vulnerability-scanner/config/table-headers.ts @@ -131,8 +131,10 @@ export const VEX_MANAGEMENT_TABLE = [ { name: "createdBy", labelKey: "imageScanner.vexManagement.table.headers.createdBy", - value: (row: any) => - row?.metadata?.generation === 1 ? "Rancher" : "Manual entry", + value: (row: any) => { + const gen = Number(row?.metadata?.generation); + return (gen === 1) ? 'Rancher' : 'Manual entry'; + }, sort: "metadata.generation", }, { diff --git a/pkg/sbombastic-image-vulnerability-scanner/models/sbombastic.rancher.io.vexhub.js b/pkg/sbombastic-image-vulnerability-scanner/models/sbombastic.rancher.io.vexhub.js new file mode 100644 index 0000000..6266b5a --- /dev/null +++ b/pkg/sbombastic-image-vulnerability-scanner/models/sbombastic.rancher.io.vexhub.js @@ -0,0 +1,76 @@ +import SteveModel from '@shell/plugins/steve/steve-class'; +import { insertAt } from '@shell/utils/array'; + +export default class SbombasticRancherIoVexhub extends SteveModel { + get _availableActions() { + let out = super._availableActions || []; + + // Remove download actions and View in API, keep edit YAML and clone + const remove = new Set([ + 'download', + 'downloadYaml', + 'downloadyaml', + 'viewYaml', + 'goToViewYaml', + 'viewInApi' + ]); + out = out.filter((a) => !a?.action || !remove.has(a.action)); + + const isEnabled = !!this.spec?.enabled; + + const toggle = isEnabled + ? { + action: 'disable', + label: this.t('imageScanner.vexManagement.buttons.disable') || 'Disable', + icon: 'icon-pause', + enabled: true, + bulkable: true, + invoke: async() => { + this.spec = { ...(this.spec || {}), enabled: false }; + await this.save(); + } + } + : { + action: 'enable', + label: this.t('imageScanner.vexManagement.buttons.enable') || 'Enable', + icon: 'icon-play', + enabled: true, + bulkable: true, + invoke: async() => { + this.spec = { ...(this.spec || {}), enabled: true }; + await this.save(); + } + }; + + if (isEnabled) { + // For enabled items: Disable, then other actions + const reordered = [toggle]; // Only the disable action + + // Add other actions except delete (which goes last) + const otherActions = out.filter(a => a && a.action !== 'promptRemove'); + reordered.push(...otherActions); + + // Add delete at the end + const deleteAction = out.find((a) => a?.action === 'promptRemove'); + if (deleteAction) { + reordered.push(deleteAction); + } + + // Ensure all actions are enabled + return reordered.map(action => { + if (action && action.enabled === false) { + return { ...action, enabled: true }; + } + return action; + }); + } + + // When disabled: Enable, then Delete + const deleteAction = out.find((a) => a?.action === 'promptRemove'); + return [toggle, ...(deleteAction ? [deleteAction] : [])]; + } + + get canDelete() { + return !this.spec?.enabled; + } +} \ No newline at end of file diff --git a/pkg/sbombastic-image-vulnerability-scanner/pages/c/_cluster/sbombastic-image-vulnerability-scanner/VexManagement.vue b/pkg/sbombastic-image-vulnerability-scanner/pages/c/_cluster/sbombastic-image-vulnerability-scanner/VexManagement.vue index 41e2849..183c6ca 100644 --- a/pkg/sbombastic-image-vulnerability-scanner/pages/c/_cluster/sbombastic-image-vulnerability-scanner/VexManagement.vue +++ b/pkg/sbombastic-image-vulnerability-scanner/pages/c/_cluster/sbombastic-image-vulnerability-scanner/VexManagement.vue @@ -64,48 +64,15 @@ export default { }, async fetch() { await this.$store.dispatch('cluster/findAll', { type: RESOURCE.VEX_HUB }); - let vexhubsCRD = this.$store.getters['cluster/all']?.(RESOURCE.VEX_HUB); - this.rows = (vexhubsCRD || []).map((r) => this.rowWithActions(r)); + const vexhubsCRD = this.$store.getters['cluster/all']?.(RESOURCE.VEX_HUB); + this.rows = vexhubsCRD || []; }, methods: { - rowWithActions(r) { - const isEnabled = !!r?.spec?.enabled; - const toggleAction = isEnabled - ? { action: 'disable', label: this.t('imageScanner.vexManagement.buttons.disable') || 'Disable', icon: 'icon-pause', enabled: true, bulkable: true, invoke: ({}, resources) => this.switchStatus(false, resources) } - : { action: 'enable', label: this.t('imageScanner.vexManagement.buttons.enable') || 'Enable', icon: 'icon-play', enabled: true, bulkable: true, invoke: ({}, resources) => this.switchStatus(true, resources) }; - - const actions = Array.isArray(r.availableActions) ? r.availableActions.filter(a => !['download','downloadYaml'].includes(a.action)): []; - - if (isEnabled && !actions.find(a => a && (a.action === 'edit' || a.action === 'goToEdit'))) { - const editAction = { - action: 'goToEdit', - label: this.t('imageScanner.vexManagement.actions.editConfig') || 'Edit configuration', - icon: 'icon-edit', - enabled: true, - bulkable: false, - invoke: ({}, res = []) => { - const target = (res && res.length ? res[0] : r); - const model = target._model || target; - if (model && typeof model.goToEdit === 'function') { - model.goToEdit(); - } - } - }; - actions.unshift(editAction); - } - const deleteAction = actions.find(a => a.action === 'promptRemove'); - - const availableActions = isEnabled - ? [toggleAction, ...actions] - : [toggleAction, deleteAction]; // when disabled show only Enable + Delete - - return { ...r, _model: r, availableActions }; - }, + rowWithActions(r) { return r; }, async switchStatus(desired, selected) { const resources = selected && selected.length ? selected : (this.selectedRows || []); if (!resources.length) return; - const models = resources.map((r) => r._model || r).filter(Boolean); - await Promise.all(models.map(async (m) => { + await Promise.all(resources.map(async (m) => { m.spec = { ...(m.spec || {}), enabled: desired }; await m.save(); }));