Skip to content

Commit 0e849d8

Browse files
authored
feat(gpu): add list gpu column (#6388) (#6398)
* feat(gpu): add list gpu column (#6388) feat: list gpu * fix(devbox): some gpu bug (#6390) * feat: list gpu * fix: gpu column show bug * fix: GPU item font style adjust * fix: lint bug
1 parent 89ec16b commit 0e849d8

File tree

5 files changed

+51
-23
lines changed

5 files changed

+51
-23
lines changed

frontend/providers/devbox/app/[lang]/(platform)/(home)/components/List.tsx

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ import {
2525
type FilterFn,
2626
getSortedRowModel,
2727
getFilteredRowModel,
28-
type SortingState
28+
type SortingState,
29+
type HeaderContext,
30+
type CellContext
2931
} from '@tanstack/react-table';
3032
import Image from 'next/image';
3133
import dynamic from 'next/dynamic';
@@ -34,6 +36,7 @@ import { useCallback, useMemo, useState, useEffect } from 'react';
3436

3537
import { useRouter } from '@/i18n';
3638
import { useDateTimeStore } from '@/stores/date';
39+
import { usePriceStore } from '@/stores/price';
3740
import { DevboxListItemTypeV2, DevboxStatusMapType } from '@/types/devbox';
3841
import { DevboxStatusEnum, devboxStatusMap } from '@/constants/devbox';
3942
import { generateMockMonitorData } from '@/constants/mock';
@@ -59,6 +62,7 @@ import { Polygon } from '@/components/Polygon';
5962
import DatePicker from '@/components/DatePicker';
6063
import { Separator } from '@sealos/shadcn-ui/separator';
6164
import SearchEmpty from './SearchEmpty';
65+
import GPUItem from '@/components/GPUItem';
6266

6367
const DeleteDevboxDialog = dynamic(() => import('@/components/dialogs/DeleteDevboxDialog'));
6468
const EditRemarkDialog = dynamic(() => import('@/components/dialogs/EditRemarkDialog'));
@@ -102,6 +106,7 @@ const DevboxList = ({
102106
useControlDevbox(refetchDevboxList);
103107

104108
const { startDateTime: dateRangeStart } = useDateTimeStore();
109+
const { sourcePrice } = usePriceStore();
105110

106111
// Check if a specific time range is selected (not "all time")
107112
const isSpecificTimeRangeSelected = useMemo(() => {
@@ -133,7 +138,7 @@ const DevboxList = ({
133138
() => [
134139
{
135140
accessorKey: 'name',
136-
header: ({ column }) => (
141+
header: ({ column }: HeaderContext<DevboxListItemTypeV2, unknown>) => (
137142
<DropdownMenu>
138143
<DropdownMenuTrigger asChild>
139144
<div className="flex cursor-pointer items-center gap-2 select-none hover:text-zinc-800">
@@ -191,7 +196,7 @@ const DevboxList = ({
191196
</DropdownMenu>
192197
),
193198
size: 250,
194-
cell: ({ row }) => {
199+
cell: ({ row }: CellContext<DevboxListItemTypeV2, unknown>) => {
195200
const item = row.original;
196201
return (
197202
<div className="flex w-full cursor-pointer items-center gap-2 pr-4">
@@ -292,7 +297,7 @@ const DevboxList = ({
292297
accessorKey: 'status',
293298
enableColumnFilter: true,
294299
filterFn: statusFilterFn,
295-
header: ({ column, table }) => {
300+
header: ({ column, table }: HeaderContext<DevboxListItemTypeV2, unknown>) => {
296301
const currentData = table.getCoreRowModel().rows.map((row) => row.original);
297302

298303
const existingStatuses = new Set(
@@ -358,7 +363,7 @@ const DevboxList = ({
358363
</DropdownMenu>
359364
);
360365
},
361-
cell: ({ row }) => {
366+
cell: ({ row }: CellContext<DevboxListItemTypeV2, unknown>) => {
362367
const item = row.original;
363368
if (!item.status || !item.status.value) return null;
364369
return (
@@ -371,9 +376,9 @@ const DevboxList = ({
371376
},
372377
{
373378
accessorKey: 'cpu',
374-
header: ({ column }) => <span className="select-none">{t('cpu')}</span>,
379+
header: ({ column }: HeaderContext<DevboxListItemTypeV2, unknown>) => <span className="select-none">{t('cpu')}</span>,
375380
size: 256,
376-
cell: ({ row }) => {
381+
cell: ({ row }: CellContext<DevboxListItemTypeV2, unknown>) => {
377382
const item = row.original;
378383
return (
379384
<MonitorChart
@@ -386,9 +391,9 @@ const DevboxList = ({
386391
},
387392
{
388393
accessorKey: 'memory',
389-
header: ({ column }) => <span className="select-none">{t('memory')}</span>,
394+
header: ({ column }: HeaderContext<DevboxListItemTypeV2, unknown>) => <span className="select-none">{t('memory')}</span>,
390395
size: 256,
391-
cell: ({ row }) => {
396+
cell: ({ row }: CellContext<DevboxListItemTypeV2, unknown>) => {
392397
const item = row.original;
393398
return (
394399
<MonitorChart
@@ -399,11 +404,20 @@ const DevboxList = ({
399404
);
400405
}
401406
},
407+
{
408+
accessorKey: 'gpu',
409+
header: ({ column }: HeaderContext<DevboxListItemTypeV2, unknown>) => <span className="select-none">GPU</span>,
410+
size: 180,
411+
cell: ({ row }: CellContext<DevboxListItemTypeV2, unknown>) => {
412+
const item = row.original;
413+
return <GPUItem gpu={item.gpu} />;
414+
}
415+
},
402416
{
403417
accessorKey: 'createTime',
404418
enableColumnFilter: true,
405419
filterFn: dateFilterFn,
406-
header: ({ column }) => (
420+
header: ({ column }: HeaderContext<DevboxListItemTypeV2, unknown>) => (
407421
<DropdownMenu>
408422
<DropdownMenuTrigger asChild>
409423
<div className="flex cursor-pointer items-center gap-2 hover:text-zinc-800">
@@ -460,7 +474,7 @@ const DevboxList = ({
460474
</DropdownMenu>
461475
),
462476
size: 150,
463-
cell: ({ row }) => {
477+
cell: ({ row }: CellContext<DevboxListItemTypeV2, unknown>) => {
464478
const item = row.original;
465479
return (
466480
<span className="text-sm text-zinc-600">
@@ -471,9 +485,9 @@ const DevboxList = ({
471485
},
472486
{
473487
id: 'actions',
474-
header: ({ column }) => <span className="select-none">{t('action')}</span>,
488+
header: ({ column }: HeaderContext<DevboxListItemTypeV2, unknown>) => <span className="select-none">{t('action')}</span>,
475489
size: 300,
476-
cell: ({ row }) => {
490+
cell: ({ row }: CellContext<DevboxListItemTypeV2, unknown>) => {
477491
const item = row.original;
478492
if (!item.status || !item.status.value) {
479493
return (
@@ -593,9 +607,14 @@ const DevboxList = ({
593607
);
594608
}
595609
}
596-
],
610+
].filter((column) => {
611+
if (column.accessorKey === 'gpu' && !sourcePrice.gpu) {
612+
return false;
613+
}
614+
return true;
615+
}),
597616
// NOTE: do not add devboxList dependency, it will cause infinite re-render
598-
[statusFilter, isSpecificTimeRangeSelected]
617+
[statusFilter, isSpecificTimeRangeSelected, sourcePrice.gpu]
599618
);
600619

601620
const { startDateTime } = useDateTimeStore();

frontend/providers/devbox/components/GPUItem.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useMemo } from 'react';
22
import { useTranslations } from 'next-intl';
3-
import { CircuitBoard } from 'lucide-react';
3+
import Image from 'next/image';
44

55
import { cn } from '@sealos/shadcn-ui';
66
import { GpuType } from '@/types/user';
@@ -16,18 +16,18 @@ const GPUItem = ({ gpu, className }: { gpu?: GpuType; className?: string }) => {
1616
}, [gpu?.type, sourcePrice?.gpu]);
1717

1818
return (
19-
<div className={cn('flex items-center whitespace-nowrap', className)}>
20-
<CircuitBoard className="mr-2 w-4 text-muted-foreground" />
19+
<div className={cn('flex items-center text-sm whitespace-nowrap text-zinc-600', className)}>
20+
<Image src="/images/nvidia.svg" alt="GPU" width={16} height={16} className="mr-2" />
2121
{gpuAlias && (
2222
<>
23-
<div className="text-xs">{gpuAlias}</div>
24-
<div className="mx-1 text-muted-foreground">/</div>
23+
<span>{gpuAlias}</span>
24+
<span className="mx-1">/</span>
2525
</>
2626
)}
27-
<div className={cn('text-xs', !!gpu?.amount ? 'text-foreground' : 'text-muted-foreground')}>
27+
<span>
2828
{!!gpuAlias ? gpu?.amount : 0}
2929
{t('Card')}
30-
</div>
30+
</span>
3131
</div>
3232
);
3333
};

frontend/providers/devbox/types/devbox.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ export interface DevboxListItemTypeV2 {
167167
createTime: string;
168168
cpu: number;
169169
memory: number;
170+
gpu?: GpuType;
170171
usedCpu: MonitorDataResult;
171172
usedMemory: MonitorDataResult;
172173
sshPort: number;

frontend/providers/devbox/types/static.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ export interface SourcePrice {
22
cpu: number;
33
memory: number;
44
nodeports: number;
5-
gpu: {
5+
gpu?: {
66
alias: string;
77
type: string;
88
price: number;

frontend/providers/devbox/utils/adapt.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ export const adaptDevboxListItemV2 = ([devbox, template]: [
3333
name: string;
3434
}
3535
]): DevboxListItemTypeV2 => {
36+
const gpuType = devbox.spec.nodeSelector?.[gpuNodeSelectorKey];
37+
const gpuAmount = devbox.spec.resource[gpuResourceKey];
38+
3639
return {
3740
id: devbox.metadata?.uid || ``,
3841
name: devbox.metadata.name || 'devbox',
@@ -43,6 +46,11 @@ export const adaptDevboxListItemV2 = ([devbox, template]: [
4346
createTime: devbox.metadata.creationTimestamp,
4447
cpu: cpuFormatToM(devbox.spec.resource.cpu),
4548
memory: memoryFormatToMi(devbox.spec.resource.memory),
49+
gpu: gpuType || gpuAmount ? {
50+
type: gpuType || '',
51+
amount: Number(gpuAmount || 0),
52+
manufacturers: 'nvidia'
53+
} : undefined,
4654
usedCpu: {
4755
name: '',
4856
xData: new Array(30).fill(0),

0 commit comments

Comments
 (0)