From 0f0b0d7d3630204a088e6c61d780ecfe6a0cd287 Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Tue, 23 Jul 2024 15:47:12 -0400 Subject: [PATCH 1/5] Add columns on SledsTab for Policy and State --- app/components/StatusBadge.tsx | 22 +++++++++++++++++++++- app/pages/system/inventory/DisksTab.tsx | 13 +++---------- app/pages/system/inventory/SledsTab.tsx | 7 +++++++ 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/app/components/StatusBadge.tsx b/app/components/StatusBadge.tsx index 5815d44e2f..7b355ba499 100644 --- a/app/components/StatusBadge.tsx +++ b/app/components/StatusBadge.tsx @@ -5,7 +5,13 @@ * * Copyright Oxide Computer Company */ -import type { DiskState, InstanceState, SnapshotState } from '@oxide/api' +import type { + DiskState, + InstanceState, + PhysicalDiskState, + SledState, + SnapshotState, +} from '@oxide/api' import { Badge, type BadgeColor, type BadgeProps } from '~/ui/lib/Badge' @@ -69,3 +75,17 @@ export const SnapshotStatusBadge = (props: { {props.status} ) + +export const PolicyKindBadge = ({ policy }: { policy: 'in_service' | 'expunged' }) => { + const color = policy === 'in_service' ? 'default' : 'neutral' + return {policy.replace(/_/g, ' ')} +} + +const STATE_BADGE_COLORS: Record = { + active: 'default', + decommissioned: 'neutral', +} + +export const StateBadge = ({ state }: { state: PhysicalDiskState | SledState }) => ( + {state} +) diff --git a/app/pages/system/inventory/DisksTab.tsx b/app/pages/system/inventory/DisksTab.tsx index 69707275f8..e7852398cb 100644 --- a/app/pages/system/inventory/DisksTab.tsx +++ b/app/pages/system/inventory/DisksTab.tsx @@ -10,6 +10,7 @@ import { createColumnHelper } from '@tanstack/react-table' import { apiQueryClient, type PhysicalDisk } from '@oxide/api' import { Servers24Icon } from '@oxide/design-system/icons/react' +import { PolicyKindBadge, StateBadge } from '~/components/StatusBadge' import { PAGE_SIZE, useQueryTable } from '~/table/QueryTable' import { Badge } from '~/ui/lib/Badge' import { EmptyMessage } from '~/ui/lib/EmptyMessage' @@ -37,18 +38,10 @@ const staticCols = [ colHelper.accessor('model', { header: 'model number' }), colHelper.accessor('serial', { header: 'serial number' }), colHelper.accessor('policy', { - cell: (info) => { - const policy = info.getValue().kind - const color = policy === 'in_service' ? 'default' : 'neutral' - return {policy.replace(/_/g, ' ')} - }, + cell: (info) => , }), colHelper.accessor('state', { - cell: (info) => { - const state = info.getValue() - const color = state === 'active' ? 'default' : 'neutral' - return {state} - }, + cell: (info) => , }), ] diff --git a/app/pages/system/inventory/SledsTab.tsx b/app/pages/system/inventory/SledsTab.tsx index 6f681b576b..0386e13ac1 100644 --- a/app/pages/system/inventory/SledsTab.tsx +++ b/app/pages/system/inventory/SledsTab.tsx @@ -10,6 +10,7 @@ import { createColumnHelper } from '@tanstack/react-table' import { apiQueryClient, type Sled } from '@oxide/api' import { Servers24Icon } from '@oxide/design-system/icons/react' +import { PolicyKindBadge, StateBadge } from '~/components/StatusBadge' import { makeLinkCell } from '~/table/cells/LinkCell' import { PAGE_SIZE, useQueryTable } from '~/table/QueryTable' import { EmptyMessage } from '~/ui/lib/EmptyMessage' @@ -41,6 +42,12 @@ const staticCols = [ colHelper.accessor('baseboard.part', { header: 'part number' }), colHelper.accessor('baseboard.serial', { header: 'serial number' }), colHelper.accessor('baseboard.revision', { header: 'revision' }), + colHelper.accessor('policy', { + cell: (info) => , + }), + colHelper.accessor('state', { + cell: (info) => , + }), ] export function SledsTab() { From c2b0d2f4d66c77f2a3412df36f31f0ac28843de0 Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Tue, 23 Jul 2024 16:40:30 -0400 Subject: [PATCH 2/5] set enum for policy colors --- .eslintrc.cjs | 1 + app/components/StatusBadge.tsx | 14 +++++++++++--- app/pages/system/inventory/DisksTab.tsx | 4 ++-- app/pages/system/inventory/SledsTab.tsx | 4 ++-- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index a212a7b04a..d08459e1a6 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -45,6 +45,7 @@ module.exports = { '@typescript-eslint/no-empty-interface': 'off', '@typescript-eslint/ban-ts-comment': 'off', '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-duplicate-type-constituents': 'off', '@typescript-eslint/no-unused-vars': [ 'error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }, diff --git a/app/components/StatusBadge.tsx b/app/components/StatusBadge.tsx index 7b355ba499..2369d6d9d3 100644 --- a/app/components/StatusBadge.tsx +++ b/app/components/StatusBadge.tsx @@ -8,7 +8,9 @@ import type { DiskState, InstanceState, + PhysicalDiskPolicy, PhysicalDiskState, + SledPolicy, SledState, SnapshotState, } from '@oxide/api' @@ -76,11 +78,17 @@ export const SnapshotStatusBadge = (props: { ) -export const PolicyKindBadge = ({ policy }: { policy: 'in_service' | 'expunged' }) => { - const color = policy === 'in_service' ? 'default' : 'neutral' - return {policy.replace(/_/g, ' ')} +type PolicyKind = PhysicalDiskPolicy['kind'] | SledPolicy['kind'] + +const POLICY_KIND_BADGE_COLORS: Record = { + in_service: 'default', + expunged: 'neutral', } +export const PolicyKindBadge = ({ kind }: { kind: PolicyKind }) => ( + {kind.replace(/_/g, ' ')} +) + const STATE_BADGE_COLORS: Record = { active: 'default', decommissioned: 'neutral', diff --git a/app/pages/system/inventory/DisksTab.tsx b/app/pages/system/inventory/DisksTab.tsx index e7852398cb..c5849c56c9 100644 --- a/app/pages/system/inventory/DisksTab.tsx +++ b/app/pages/system/inventory/DisksTab.tsx @@ -37,8 +37,8 @@ const staticCols = [ }), colHelper.accessor('model', { header: 'model number' }), colHelper.accessor('serial', { header: 'serial number' }), - colHelper.accessor('policy', { - cell: (info) => , + colHelper.accessor('policy.kind', { + cell: (info) => , }), colHelper.accessor('state', { cell: (info) => , diff --git a/app/pages/system/inventory/SledsTab.tsx b/app/pages/system/inventory/SledsTab.tsx index 0386e13ac1..6c4e9914e8 100644 --- a/app/pages/system/inventory/SledsTab.tsx +++ b/app/pages/system/inventory/SledsTab.tsx @@ -42,8 +42,8 @@ const staticCols = [ colHelper.accessor('baseboard.part', { header: 'part number' }), colHelper.accessor('baseboard.serial', { header: 'serial number' }), colHelper.accessor('baseboard.revision', { header: 'revision' }), - colHelper.accessor('policy', { - cell: (info) => , + colHelper.accessor('policy.kind', { + cell: (info) => , }), colHelper.accessor('state', { cell: (info) => , From 9fe7f4be5d0e08ded34dce1fbd35dc42a536702a Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Tue, 23 Jul 2024 16:52:48 -0400 Subject: [PATCH 3/5] don't be so DRY with the component --- app/components/StatusBadge.tsx | 30 +------------------------ app/pages/system/inventory/DisksTab.tsx | 28 ++++++++++++++++++----- app/pages/system/inventory/SledsTab.tsx | 22 ++++++++++++++---- 3 files changed, 42 insertions(+), 38 deletions(-) diff --git a/app/components/StatusBadge.tsx b/app/components/StatusBadge.tsx index 2369d6d9d3..5815d44e2f 100644 --- a/app/components/StatusBadge.tsx +++ b/app/components/StatusBadge.tsx @@ -5,15 +5,7 @@ * * Copyright Oxide Computer Company */ -import type { - DiskState, - InstanceState, - PhysicalDiskPolicy, - PhysicalDiskState, - SledPolicy, - SledState, - SnapshotState, -} from '@oxide/api' +import type { DiskState, InstanceState, SnapshotState } from '@oxide/api' import { Badge, type BadgeColor, type BadgeProps } from '~/ui/lib/Badge' @@ -77,23 +69,3 @@ export const SnapshotStatusBadge = (props: { {props.status} ) - -type PolicyKind = PhysicalDiskPolicy['kind'] | SledPolicy['kind'] - -const POLICY_KIND_BADGE_COLORS: Record = { - in_service: 'default', - expunged: 'neutral', -} - -export const PolicyKindBadge = ({ kind }: { kind: PolicyKind }) => ( - {kind.replace(/_/g, ' ')} -) - -const STATE_BADGE_COLORS: Record = { - active: 'default', - decommissioned: 'neutral', -} - -export const StateBadge = ({ state }: { state: PhysicalDiskState | SledState }) => ( - {state} -) diff --git a/app/pages/system/inventory/DisksTab.tsx b/app/pages/system/inventory/DisksTab.tsx index c5849c56c9..dea25f6f38 100644 --- a/app/pages/system/inventory/DisksTab.tsx +++ b/app/pages/system/inventory/DisksTab.tsx @@ -7,14 +7,28 @@ */ import { createColumnHelper } from '@tanstack/react-table' -import { apiQueryClient, type PhysicalDisk } from '@oxide/api' +import { + apiQueryClient, + type PhysicalDisk, + type PhysicalDiskPolicy, + type PhysicalDiskState, +} from '@oxide/api' import { Servers24Icon } from '@oxide/design-system/icons/react' -import { PolicyKindBadge, StateBadge } from '~/components/StatusBadge' import { PAGE_SIZE, useQueryTable } from '~/table/QueryTable' -import { Badge } from '~/ui/lib/Badge' +import { Badge, type BadgeColor } from '~/ui/lib/Badge' import { EmptyMessage } from '~/ui/lib/EmptyMessage' +const POLICY_KIND_BADGE_COLORS: Record = { + in_service: 'default', + expunged: 'neutral', +} + +const STATE_BADGE_COLORS: Record = { + active: 'default', + decommissioned: 'neutral', +} + const EmptyState = () => ( } @@ -38,10 +52,14 @@ const staticCols = [ colHelper.accessor('model', { header: 'model number' }), colHelper.accessor('serial', { header: 'serial number' }), colHelper.accessor('policy.kind', { - cell: (info) => , + cell: (info) => ( + {info.getValue()} + ), }), colHelper.accessor('state', { - cell: (info) => , + cell: (info) => ( + {info.getValue()} + ), }), ] diff --git a/app/pages/system/inventory/SledsTab.tsx b/app/pages/system/inventory/SledsTab.tsx index 6c4e9914e8..1cccf0d0f2 100644 --- a/app/pages/system/inventory/SledsTab.tsx +++ b/app/pages/system/inventory/SledsTab.tsx @@ -7,15 +7,25 @@ */ import { createColumnHelper } from '@tanstack/react-table' -import { apiQueryClient, type Sled } from '@oxide/api' +import { apiQueryClient, type Sled, type SledPolicy, type SledState } from '@oxide/api' import { Servers24Icon } from '@oxide/design-system/icons/react' -import { PolicyKindBadge, StateBadge } from '~/components/StatusBadge' import { makeLinkCell } from '~/table/cells/LinkCell' import { PAGE_SIZE, useQueryTable } from '~/table/QueryTable' +import { Badge, type BadgeColor } from '~/ui/lib/Badge' import { EmptyMessage } from '~/ui/lib/EmptyMessage' import { pb } from '~/util/path-builder' +const POLICY_KIND_BADGE_COLORS: Record = { + in_service: 'default', + expunged: 'neutral', +} + +const STATE_BADGE_COLORS: Record = { + active: 'default', + decommissioned: 'neutral', +} + const EmptyState = () => { return ( , + cell: (info) => ( + {info.getValue()} + ), }), colHelper.accessor('state', { - cell: (info) => , + cell: (info) => ( + {info.getValue()} + ), }), ] From 25899506c077e079aa4b4981f8d37cddf29f6200 Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Tue, 23 Jul 2024 17:07:35 -0400 Subject: [PATCH 4/5] refactoring a few UI bits; adding extra examples to mock api --- app/pages/system/inventory/DisksTab.tsx | 5 +- app/pages/system/inventory/SledsTab.tsx | 5 +- mock-api/sled.ts | 88 ++++++++++++++++++++----- 3 files changed, 78 insertions(+), 20 deletions(-) diff --git a/app/pages/system/inventory/DisksTab.tsx b/app/pages/system/inventory/DisksTab.tsx index dea25f6f38..c11fb3bffc 100644 --- a/app/pages/system/inventory/DisksTab.tsx +++ b/app/pages/system/inventory/DisksTab.tsx @@ -52,8 +52,11 @@ const staticCols = [ colHelper.accessor('model', { header: 'model number' }), colHelper.accessor('serial', { header: 'serial number' }), colHelper.accessor('policy.kind', { + header: 'policy', cell: (info) => ( - {info.getValue()} + + {info.getValue().replace(/_/g, ' ')} + ), }), colHelper.accessor('state', { diff --git a/app/pages/system/inventory/SledsTab.tsx b/app/pages/system/inventory/SledsTab.tsx index 1cccf0d0f2..1dcc3e187f 100644 --- a/app/pages/system/inventory/SledsTab.tsx +++ b/app/pages/system/inventory/SledsTab.tsx @@ -53,8 +53,11 @@ const staticCols = [ colHelper.accessor('baseboard.serial', { header: 'serial number' }), colHelper.accessor('baseboard.revision', { header: 'revision' }), colHelper.accessor('policy.kind', { + header: 'policy', cell: (info) => ( - {info.getValue()} + + {info.getValue().replace(/_/g, ' ')} + ), }), colHelper.accessor('state', { diff --git a/mock-api/sled.ts b/mock-api/sled.ts index 23bf8f9713..e4e3a85a21 100644 --- a/mock-api/sled.ts +++ b/mock-api/sled.ts @@ -9,23 +9,75 @@ import type { Sled } from '@oxide/api' import type { Json } from './json-type' -export const sled: Json = { - id: 'c2519937-44a4-493b-9b38-5c337c597d08', - time_created: new Date(2021, 0, 1).toISOString(), - time_modified: new Date(2021, 0, 2).toISOString(), - rack_id: '6fbafcc7-1626-4785-be65-e212f8ad66d0', - policy: { - kind: 'in_service', - provision_policy: 'provisionable', +export const sleds: Json = [ + { + id: 'c2519937-44a4-493b-9b38-5c337c597d08', + time_created: new Date(2023, 0, 1).toISOString(), + time_modified: new Date(2023, 0, 2).toISOString(), + rack_id: '6fbafcc7-1626-4785-be65-e212f8ad66d0', + policy: { + kind: 'in_service', + provision_policy: 'provisionable', + }, + state: 'active', + baseboard: { + part: '913-0000108', + serial: 'BRM02222869', + revision: 0, + }, + usable_hardware_threads: 128, + usable_physical_ram: 1_099_511_627_776, }, - state: 'active', - baseboard: { - part: '913-0000008', - serial: 'BRM02222867', - revision: 0, + { + id: '1ec7df9d-a6de-423c-8bf8-01557e8e5aea', + time_created: new Date(2024, 0, 1).toISOString(), + time_modified: new Date(2024, 0, 2).toISOString(), + rack_id: '759a1c80-4bff-4d0b-97ce-b482ca936724', + policy: { + kind: 'in_service', + provision_policy: 'provisionable', + }, + state: 'active', + baseboard: { + part: '913-0001008', + serial: 'BRM02222870', + revision: 0, + }, + usable_hardware_threads: 128, + usable_physical_ram: 1_099_511_627_776, }, - usable_hardware_threads: 128, - usable_physical_ram: 1_099_511_627_776, -} - -export const sleds: Json = [sled] + { + id: 'fca81647-868a-4aa5-b8c3-84364d4b4dc9', + time_created: new Date(2022, 0, 1).toISOString(), + time_modified: new Date(2022, 0, 2).toISOString(), + rack_id: 'ebe9a4c0-248b-491c-9448-04ddb10ef648', + policy: { + kind: 'expunged', + }, + state: 'active', + baseboard: { + part: '913-0000018', + serial: 'BRM02222868', + revision: 0, + }, + usable_hardware_threads: 128, + usable_physical_ram: 1_099_511_627_776, + }, + { + id: 'a4ed0c62-cb3a-48bc-a7a8-ee544a8a8869', + time_created: new Date(2021, 0, 1).toISOString(), + time_modified: new Date(2021, 0, 2).toISOString(), + rack_id: '64f712fe-4320-407d-835a-d63b0455d89c', + policy: { + kind: 'expunged', + }, + state: 'decommissioned', + baseboard: { + part: '913-0000008', + serial: 'BRM02222867', + revision: 0, + }, + usable_hardware_threads: 128, + usable_physical_ram: 1_099_511_627_776, + }, +] From 31dd851f6e2437a4ab81b63390b2da87bc46b95d Mon Sep 17 00:00:00 2001 From: Charlie Park Date: Tue, 23 Jul 2024 17:19:51 -0400 Subject: [PATCH 5/5] enhance tests --- test/e2e/inventory.e2e.ts | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/test/e2e/inventory.e2e.ts b/test/e2e/inventory.e2e.ts index a5cb76ea03..5ec3a0a34e 100644 --- a/test/e2e/inventory.e2e.ts +++ b/test/e2e/inventory.e2e.ts @@ -5,7 +5,8 @@ * * Copyright Oxide Computer Company */ -import { physicalDisks } from '@oxide/api-mocks' + +import { physicalDisks, sleds } from '@oxide/api-mocks' import { expect, expectRowVisible, expectVisible, test } from './utils' @@ -19,6 +20,25 @@ test('Sled inventory page', async ({ page }) => { await expect(sledsTab).toHaveClass(/is-selected/) const sledsTable = page.getByRole('table') + await expectRowVisible(sledsTable, { + id: sleds[1].id, + 'serial number': sleds[1].baseboard.serial, + policy: 'in service', + state: 'active', + }) + await expectRowVisible(sledsTable, { + id: sleds[2].id, + 'serial number': sleds[2].baseboard.serial, + policy: 'expunged', + state: 'active', + }) + await expectRowVisible(sledsTable, { + id: sleds[3].id, + 'serial number': sleds[3].baseboard.serial, + policy: 'expunged', + state: 'decommissioned', + }) + // Visit the sled detail page of the first sled await sledsTable.getByRole('link').first().click() @@ -54,11 +74,13 @@ test('Disk inventory page', async ({ page }) => { }) await expectRowVisible(table, { id: physicalDisks[4].id, + 'Form factor': 'M.2', policy: 'expunged', state: 'active', }) await expectRowVisible(table, { id: physicalDisks[5].id, + 'Form factor': 'M.2', policy: 'expunged', state: 'decommissioned', })