Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instances table: remove hostname, normal row height, split CPU/RAM #2389

Merged
merged 3 commits into from
Aug 20, 2024
Merged
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
8 changes: 3 additions & 5 deletions app/components/TimeAgo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,8 @@ export const TimeAgo = ({
</div>
)
return (
<span className="mt-0.5">
<Tooltip content={content} placement={placement}>
<span className="text-sans-sm text-tertiary">{timeAgoAbbr(datetime)}</span>
</Tooltip>
</span>
<Tooltip content={content} placement={placement}>
<span className="text-sans-sm text-tertiary">{timeAgoAbbr(datetime)}</span>
</Tooltip>
)
}
31 changes: 21 additions & 10 deletions app/pages/project/instances/InstancesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* Copyright Oxide Computer Company
*/
import { createColumnHelper } from '@tanstack/react-table'
import { filesize } from 'filesize'
import { useMemo } from 'react'
import { useNavigate, type LoaderFunctionArgs } from 'react-router-dom'

Expand All @@ -20,7 +21,6 @@ import { Instances16Icon, Instances24Icon } from '@oxide/design-system/icons/rea
import { DocsPopover } from '~/components/DocsPopover'
import { RefreshButton } from '~/components/RefreshButton'
import { getProjectSelector, useProjectSelector, useQuickActions } from '~/hooks'
import { InstanceResourceCell } from '~/table/cells/InstanceResourceCell'
import { InstanceStatusCell } from '~/table/cells/InstanceStatusCell'
import { makeLinkCell } from '~/table/cells/LinkCell'
import { getActionsCol } from '~/table/columns/action-col'
Expand Down Expand Up @@ -99,21 +99,32 @@ export function InstancesPage() {
colHelper.accessor('name', {
cell: makeLinkCell((instance) => pb.instance({ project, instance })),
}),
colHelper.accessor((i) => ({ ncpus: i.ncpus, memory: i.memory }), {
header: 'CPU, RAM',
cell: (info) => <InstanceResourceCell value={info.getValue()} />,
colHelper.accessor('ncpus', {
header: 'CPU',
cell: (info) => (
<>
{info.getValue()} <span className="ml-1 text-quaternary">vCPU</span>
</>
),
}),
colHelper.accessor('memory', {
header: 'Memory',
cell: (info) => {
const memory = filesize(info.getValue(), { output: 'object', base: 2 })
return (
<>
{memory.value} <span className="ml-1 text-quaternary">{memory.unit}</span>
</>
)
},
}),
colHelper.accessor(
(i) => ({
runState: i.runState,
timeRunStateUpdated: i.timeRunStateUpdated,
}),
(i) => ({ runState: i.runState, timeRunStateUpdated: i.timeRunStateUpdated }),
{
header: 'status',
cell: (info) => <InstanceStatusCell value={info.getValue()} />,
}
),
colHelper.accessor('hostname', {}),
colHelper.accessor('timeCreated', Columns.timeCreated),
getActionsCol(makeActions),
],
Expand All @@ -137,7 +148,7 @@ export function InstancesPage() {
<RefreshButton onClick={refetchInstances} />
<CreateLink to={pb.instancesNew({ project })}>New Instance</CreateLink>
</TableActions>
<Table columns={columns} emptyState={<EmptyState />} rowHeight="large" />
<Table columns={columns} emptyState={<EmptyState />} />
</>
)
}
5 changes: 3 additions & 2 deletions app/pages/system/inventory/sled/SledInstancesTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,17 @@ const staticCols = [
)
},
}),
// we don't show run state last update time like on project instances because
// it's not in this response
colHelper.accessor('state', {
header: 'status',
cell: (info) => <InstanceStatusBadge key="run-state" status={info.getValue()} />,
cell: (info) => <InstanceStatusBadge status={info.getValue()} />,
}),
colHelper.accessor((i) => R.pick(i, ['memory', 'ncpus']), {
header: 'specs',
cell: (info) => <InstanceResourceCell value={info.getValue()} />,
}),
colHelper.accessor('timeCreated', Columns.timeCreated),
colHelper.accessor('timeModified', Columns.timeModified),
]

export function SledInstancesTab() {
Expand Down
4 changes: 2 additions & 2 deletions app/table/cells/InstanceStatusCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ type Props = { value: Pick<Instance, 'runState' | 'timeRunStateUpdated'> }

export const InstanceStatusCell = ({ value }: Props) => {
return (
<div className="flex flex-col">
<InstanceStatusBadge key="run-state" status={value.runState} />
<div className="flex items-center gap-1.5">
<InstanceStatusBadge status={value.runState} />
<TimeAgo tooltipText="Run state updated" datetime={value.timeRunStateUpdated} />
</div>
)
Expand Down
14 changes: 8 additions & 6 deletions mock-api/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@
*/
import type { Instance } from '@oxide/api'

import { GiB } from '~/util/units'

import type { Json } from './json-type'
import { project } from './project'

export const instance: Json<Instance> = {
id: '935499b3-fd96-432a-9c21-83a3dc1eece4',
name: 'db1',
ncpus: 7,
memory: 1024 * 1024 * 256,
ncpus: 2,
memory: 4 * GiB,
description: 'an instance',
hostname: 'oxide.com',
project_id: project.id,
Expand All @@ -27,8 +29,8 @@ export const instance: Json<Instance> = {
const failedInstance: Json<Instance> = {
id: 'b5946edc-5bed-4597-88ab-9a8beb9d32a4',
name: 'you-fail',
ncpus: 7,
memory: 1024 * 1024 * 256,
ncpus: 4,
memory: 6 * GiB,
description: 'a failed instance',
hostname: 'oxide.com',
project_id: project.id,
Expand All @@ -41,8 +43,8 @@ const failedInstance: Json<Instance> = {
const startingInstance: Json<Instance> = {
id: '16737f54-1f76-4c96-8b7c-9d24971c1d62',
name: 'not-there-yet',
ncpus: 7,
memory: 1024 * 1024 * 256,
ncpus: 2,
memory: 8 * GiB,
description: 'a starting instance',
hostname: 'oxide.com',
project_id: project.id,
Expand Down
26 changes: 25 additions & 1 deletion test/e2e/instance.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*
* Copyright Oxide Computer Company
*/
import { expect, refreshInstance, sleep, test } from './utils'
import { expect, expectRowVisible, refreshInstance, sleep, test } from './utils'

test('can delete a failed instance', async ({ page }) => {
await page.goto('/projects/mock-project/instances')
Expand Down Expand Up @@ -78,3 +78,27 @@ test('delete from instance detail', async ({ page }) => {
await expect(page.getByRole('cell', { name: 'db1' })).toBeVisible()
await expect(page.getByRole('cell', { name: 'you-fail' })).toBeHidden()
})

test('instance table', async ({ page }) => {
await page.goto('/projects/mock-project/instances')

const table = page.getByRole('table')
await expectRowVisible(table, {
name: 'db1',
CPU: '2 vCPU',
Memory: '4 GiB',
status: expect.stringMatching(/^running\d+s$/),
})
await expectRowVisible(table, {
name: 'you-fail',
CPU: '4 vCPU',
Memory: '6 GiB',
status: expect.stringMatching(/^failed\d+s$/),
})
await expectRowVisible(table, {
name: 'not-there-yet',
CPU: '2 vCPU',
Memory: '8 GiB',
status: expect.stringMatching(/^starting\d+s$/),
})
})
Loading