From efbd089ffb9deb6de00dbc57df66748e9ecaa44d Mon Sep 17 00:00:00 2001 From: Edward Sammut Alessi Date: Thu, 30 Apr 2026 13:53:38 +0200 Subject: [PATCH] feat(frontend): add qol machine updates to omni frontend Add some QoL updates for machine management to Omni frontend. 1. Add a copy machine UUID button to the cluster machine page 2. Add a toggle between hostnames and UUIDs to the machines list page (copy will copy what it sees, preference is saved) 3. Add kernel args tabs to machine and cluster machine pages, to allow editing kernel args. The "Update kernel args" button from machines list dropdown menu will now redirect to here instead of opening a modal. Signed-off-by: Edward Sammut Alessi --- .../src/components/Button/TButtonGroup.vue | 4 +- frontend/src/components/List/TList.vue | 72 +++++++------- frontend/src/components/TModal.vue | 1 - .../clusters/[cluster]/machine/[machine].vue | 4 + .../machine/[machine]/kernel-args.vue | 15 +++ .../(authenticated)/machines/[machine].vue | 4 + .../machines/[machine]/kernel-args.vue | 15 +++ frontend/src/views/Machines/MachineItem.vue | 17 +++- .../MachineKernelArgs.stories.ts} | 56 ++++++----- .../MachineKernelArgs.vue} | 93 +++++++------------ frontend/src/views/Machines/Machines.vue | 33 +++++-- frontend/src/views/Nodes/NodesBreadcrumbs.vue | 11 ++- frontend/src/views/Nodes/NodesHeader.vue | 2 +- frontend/typed-router.d.ts | 38 ++++++++ 14 files changed, 232 insertions(+), 133 deletions(-) create mode 100644 frontend/src/pages/(authenticated)/clusters/[cluster]/machine/[machine]/kernel-args.vue create mode 100644 frontend/src/pages/(authenticated)/machines/[machine]/kernel-args.vue rename frontend/src/views/{Modals/UpdateKernelArgs.stories.ts => Machines/MachineKernelArgs.stories.ts} (59%) rename frontend/src/views/{Modals/UpdateKernelArgs.vue => Machines/MachineKernelArgs.vue} (77%) diff --git a/frontend/src/components/Button/TButtonGroup.vue b/frontend/src/components/Button/TButtonGroup.vue index ff24568b..23bc2ca4 100644 --- a/frontend/src/components/Button/TButtonGroup.vue +++ b/frontend/src/components/Button/TButtonGroup.vue @@ -67,11 +67,11 @@ const modelValue = defineModel() } .checked { - @apply bg-naturals-n4; + @apply bg-naturals-n6; } .checked span { - @apply text-naturals-n12; + @apply text-primary-p3; } .popper { diff --git a/frontend/src/components/List/TList.vue b/frontend/src/components/List/TList.vue index 4c956128..70b4e3bb 100644 --- a/frontend/src/components/List/TList.vue +++ b/frontend/src/components/List/TList.vue @@ -315,44 +315,46 @@ const openPage = (page: number | string) => { -
- +
+
+ +
-
+
+ - + - - - + +
diff --git a/frontend/src/components/TModal.vue b/frontend/src/components/TModal.vue index 7dc913d2..87b2dcea 100644 --- a/frontend/src/components/TModal.vue +++ b/frontend/src/components/TModal.vue @@ -28,7 +28,6 @@ const modals: Record = { configPatchDestroy: defineAsyncComponent(() => import('@/views/Modals/ConfigPatchDestroy.vue')), userDestroy: defineAsyncComponent(() => import('@/views/Modals/UserDestroy.vue')), userCreate: defineAsyncComponent(() => import('@/views/Modals/UserCreate.vue')), - updateKernelArgs: defineAsyncComponent(() => import('@/views/Modals/UpdateKernelArgs.vue')), joinTokenCreate: defineAsyncComponent(() => import('@/views/Modals/JoinTokenCreate.vue')), joinTokenRevoke: defineAsyncComponent(() => import('@/views/Modals/JoinTokenRevoke.vue')), joinTokenDelete: defineAsyncComponent(() => import('@/views/Modals/JoinTokenDelete.vue')), diff --git a/frontend/src/pages/(authenticated)/clusters/[cluster]/machine/[machine].vue b/frontend/src/pages/(authenticated)/clusters/[cluster]/machine/[machine].vue index b2d14477..a62a3e3d 100644 --- a/frontend/src/pages/(authenticated)/clusters/[cluster]/machine/[machine].vue +++ b/frontend/src/pages/(authenticated)/clusters/[cluster]/machine/[machine].vue @@ -67,6 +67,10 @@ const routes = computed(() => { name: 'Extensions', to: { name: 'NodeExtensions', params: { machine: machine.value } }, }, + { + name: 'Kernel Args', + to: { name: 'NodeKernelArgs' }, + }, ] }) diff --git a/frontend/src/pages/(authenticated)/clusters/[cluster]/machine/[machine]/kernel-args.vue b/frontend/src/pages/(authenticated)/clusters/[cluster]/machine/[machine]/kernel-args.vue new file mode 100644 index 00000000..80158c6d --- /dev/null +++ b/frontend/src/pages/(authenticated)/clusters/[cluster]/machine/[machine]/kernel-args.vue @@ -0,0 +1,15 @@ + + + + diff --git a/frontend/src/pages/(authenticated)/machines/[machine].vue b/frontend/src/pages/(authenticated)/machines/[machine].vue index c9788077..65e11bca 100644 --- a/frontend/src/pages/(authenticated)/machines/[machine].vue +++ b/frontend/src/pages/(authenticated)/machines/[machine].vue @@ -50,6 +50,10 @@ const routes = computed(() => { name: 'Extensions', to: { name: 'MachineExtensions' }, }, + { + name: 'Kernel Args', + to: { name: 'MachineKernelArgs' }, + }, ] }) diff --git a/frontend/src/pages/(authenticated)/machines/[machine]/kernel-args.vue b/frontend/src/pages/(authenticated)/machines/[machine]/kernel-args.vue new file mode 100644 index 00000000..9efa0cd2 --- /dev/null +++ b/frontend/src/pages/(authenticated)/machines/[machine]/kernel-args.vue @@ -0,0 +1,15 @@ + + + + diff --git a/frontend/src/views/Machines/MachineItem.vue b/frontend/src/views/Machines/MachineItem.vue index db3d88d6..d4620cf4 100644 --- a/frontend/src/views/Machines/MachineItem.vue +++ b/frontend/src/views/Machines/MachineItem.vue @@ -25,9 +25,14 @@ import type { Label } from '@/methods/labels' import { addMachineLabels, removeMachineLabels } from '@/methods/machine' import ItemLabels from '@/views/ItemLabels/ItemLabels.vue' -const { machine, searchQuery = '' } = defineProps<{ +const { + machine, + showUUID, + searchQuery = '', +} = defineProps<{ machine: Resource panelOpen?: boolean + showUUID?: boolean searchQuery?: string }>() @@ -48,7 +53,9 @@ const { canManageKernelArgs, canReadConfigPatches } = useClusterPermissions( ) const machineName = computed(() => { - return machine.spec.message_status?.network?.hostname ?? machine.metadata.id + return showUUID + ? machine.metadata.id + : (machine.spec.message_status?.network?.hostname ?? machine.metadata.id) }) const clusterName = computed(() => machine.spec.message_status?.cluster) @@ -177,9 +184,9 @@ const maintenanceUpdateDescription = computed(() => { :disabled="!canManageKernelArgs" @select=" $router.push({ - query: { - modal: 'updateKernelArgs', - machine: machine.metadata.id, + name: 'MachineKernelArgs', + params: { + machine: machine.metadata.id!, }, }) " diff --git a/frontend/src/views/Modals/UpdateKernelArgs.stories.ts b/frontend/src/views/Machines/MachineKernelArgs.stories.ts similarity index 59% rename from frontend/src/views/Modals/UpdateKernelArgs.stories.ts rename to frontend/src/views/Machines/MachineKernelArgs.stories.ts index ff3a9473..07955be8 100644 --- a/frontend/src/views/Modals/UpdateKernelArgs.stories.ts +++ b/frontend/src/views/Machines/MachineKernelArgs.stories.ts @@ -5,17 +5,19 @@ import { faker } from '@faker-js/faker' import { createWatchStreamHandler } from '@msw/helpers' import type { Meta, StoryObj } from '@storybook/vue3-vite' -import { http, HttpResponse } from 'msw' -import type { Resource } from '@/api/grpc' -import type { GetRequest } from '@/api/omni/resources/resources.pb' import type { KernelArgsSpec, KernelArgsStatusSpec } from '@/api/omni/specs/omni.pb' import { DefaultNamespace, KernelArgsStatusType, KernelArgsType } from '@/api/resources' -import UpdateKernelArgs from './UpdateKernelArgs.vue' +import MachineKernelArgs from './MachineKernelArgs.vue' -const meta: Meta = { - component: UpdateKernelArgs, +const machineId = faker.string.uuid() + +const meta: Meta = { + component: MachineKernelArgs, + parameters: { + machine: machineId, + }, } export default meta @@ -29,6 +31,7 @@ export const Data: Story = { expectedOptions: { type: KernelArgsStatusType, namespace: DefaultNamespace, + id: machineId, }, initialResources: [ { @@ -41,27 +44,36 @@ export const Data: Story = { faker.helpers.slugify(faker.hacker.phrase()).toLowerCase(), ), }, - metadata: {}, + metadata: { + type: KernelArgsStatusType, + namespace: DefaultNamespace, + id: machineId, + }, }, ], }).handler, - http.post('/omni.resources.ResourceService/Get', async ({ request }) => { - const { type, namespace } = await request.clone().json() - - if (type !== KernelArgsType || namespace !== DefaultNamespace) return - - const resource: Resource = { - spec: { - args: faker.helpers.multiple(() => - faker.helpers.slugify(faker.hacker.phrase()).toLowerCase(), - ), + createWatchStreamHandler({ + expectedOptions: { + type: KernelArgsType, + namespace: DefaultNamespace, + id: machineId, + }, + initialResources: [ + { + spec: { + args: faker.helpers.multiple(() => + faker.helpers.slugify(faker.hacker.phrase()).toLowerCase(), + ), + }, + metadata: { + type: KernelArgsType, + namespace: DefaultNamespace, + id: machineId, + }, }, - metadata: {}, - } - - return HttpResponse.json({ body: JSON.stringify(resource) }) - }), + ], + }).handler, ], }, }, diff --git a/frontend/src/views/Modals/UpdateKernelArgs.vue b/frontend/src/views/Machines/MachineKernelArgs.vue similarity index 77% rename from frontend/src/views/Modals/UpdateKernelArgs.vue rename to frontend/src/views/Machines/MachineKernelArgs.vue index 1059fdae..bf114450 100644 --- a/frontend/src/views/Modals/UpdateKernelArgs.vue +++ b/frontend/src/views/Machines/MachineKernelArgs.vue @@ -6,7 +6,6 @@ included in the LICENSE file. -->
Current Kernel Cmdline
- {{ currentCmdline || 'none' }} + + {{ currentCmdline || 'none' }} + -
+ - - diff --git a/frontend/src/views/Machines/Machines.vue b/frontend/src/views/Machines/Machines.vue index 0f8a4bac..f32b753e 100644 --- a/frontend/src/views/Machines/Machines.vue +++ b/frontend/src/views/Machines/Machines.vue @@ -5,6 +5,7 @@ Use of this software is governed by the Business Source License included in the LICENSE file. --> diff --git a/frontend/src/views/Nodes/NodesHeader.vue b/frontend/src/views/Nodes/NodesHeader.vue index cf9bd4af..cd592acc 100644 --- a/frontend/src/views/Nodes/NodesHeader.vue +++ b/frontend/src/views/Nodes/NodesHeader.vue @@ -104,7 +104,7 @@ const { canRebootMachines, canRemoveMachines, canAddClusterMachines } = useClust