Skip to content

Commit 0ea98ff

Browse files
committed
feat: Show proper list of self-managed instances
https://harperdb.atlassian.net/browse/STUDIO-428
1 parent d9a9e11 commit 0ea98ff

File tree

5 files changed

+58
-49
lines changed

5 files changed

+58
-49
lines changed

src/components/DataTable.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export function DataTable<TData, TValue>({ columns, data }: DataTableProps<TData
2424
<TableRow key={headerGroup.id} className="border-none">
2525
{headerGroup.headers.map((header) => {
2626
return (
27-
<TableHead key={header.id} className="p-4">
27+
<TableHead key={header.id} className="p-4" style={{ width: `${header.getSize()}%` }}>
2828
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
2929
</TableHead>
3030
);
@@ -41,7 +41,7 @@ export function DataTable<TData, TValue>({ columns, data }: DataTableProps<TData
4141
className="hover:bg-muted/10 data-[state=selected]:bg-muted"
4242
>
4343
{row.getVisibleCells().map((cell) => (
44-
<TableCell key={cell.id} className="p-4">
44+
<TableCell key={cell.id} className="p-4" style={{ width: `${cell.column.getSize()}%` }}>
4545
{flexRender(cell.column.columnDef.cell, cell.getContext())}
4646
</TableCell>
4747
))}
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1+
import { cx } from 'class-variance-authority';
12
import { useMemo } from 'react';
23

34
export function TextLoadingSkeleton({ className }: { className?: string } = {}) {
4-
const computedClassName = useMemo(() => `animate-pulse ${className} h-2.5 w-48 bg-gray-200 rounded-full dark:bg-gray-700 mb-4`, [className]);
5+
const computedClassName = useMemo(() => cx(
6+
`animate-pulse h-2.5 bg-gray-200 rounded-full dark:bg-gray-700 mb-4`,
7+
!className?.includes('w-') && 'w-48 ',
8+
className,
9+
), [className]);
510
return <div className={computedClassName}></div>;
611
}

src/features/cluster/InstanceLogInCell.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { TextLoadingSkeleton } from '@/components/TextLoadingSkeleton';
21
import { Button } from '@/components/ui/button';
32
import { defaultInstanceRoute } from '@/config/constants';
43
import { useInstanceClient } from '@/config/useInstanceClient';
@@ -8,6 +7,7 @@ import { Instance } from '@/lib/api.patch';
87
import { authStore } from '@/lib/authStore';
98
import { getOperationsUrlForInstance } from '@/lib/urls/getOperationsUrlForInstance';
109
import { Link } from '@tanstack/react-router';
10+
import { LoaderCircleIcon } from 'lucide-react';
1111
import { useCallback, useMemo } from 'react';
1212

1313
export function InstanceLogInCell({ instance }: { readonly instance: Instance }) {
@@ -19,11 +19,8 @@ export function InstanceLogInCell({ instance }: { readonly instance: Instance })
1919
authStore.setUserForEntity(instance, null);
2020
}, [instance, instanceClient]);
2121

22-
if (!['CLONE_READY', 'RUNNING', 'UPDATED'].includes(instance.status)) {
23-
return <p>N/A</p>;
24-
}
25-
if (instanceAuthIsLoading) {
26-
return <TextLoadingSkeleton />;
22+
if (instanceAuthIsLoading || !['CLONE_READY', 'RUNNING', 'UPDATED'].includes(instance.status)) {
23+
return <LoaderCircleIcon className="animate-spin" color="gray" />;
2724
}
2825
if (!instanceUser) {
2926
return <Link

src/features/cluster/index.tsx

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ import { renderBadgeStatusVariant } from '@/components/ui/utils/badgeStatus';
77
import { EmptyCluster } from '@/features/cluster/EmptyCluster';
88
import { InstanceLogInCell } from '@/features/cluster/InstanceLogInCell';
99
import { getClusterInfoQueryOptions } from '@/features/cluster/queries/getClusterInfoQuery';
10+
import { calculateInstanceFQDN } from '@/features/clusters/upsert/lib/calculateInstanceFQDN';
1011
import { Instance } from '@/lib/api.patch';
12+
import { excludeFalsy } from '@/lib/arrays/excludeFalsy';
1113
import { humanFileSize } from '@/lib/humanFileSize';
12-
import { InstanceTypes, renderInstanceTypeOption } from '@/lib/InstanceType';
1314
import { capitalizeWords } from '@/lib/string/capitalizeWords';
1415
import { useQuery } from '@tanstack/react-query';
1516
import { useParams } from '@tanstack/react-router';
@@ -21,62 +22,87 @@ export function ClusterIndex() {
2122
const { data: cluster, isLoading: clusterIsLoading } = useQuery(
2223
getClusterInfoQueryOptions(clusterId, true),
2324
);
25+
const isSelfManaged = useMemo(() => !!cluster?.plans?.[0]?.planId?.startsWith('self-hosted'), [cluster]);
2426

2527
const columns: ColumnDef<Instance>[] = useMemo(
26-
() => [
28+
() => ([
2729
{
28-
accessorKey: 'name', // Accessor key for the "name" field from data object
29-
header: 'Name', // Column header
30+
id: 'instanceActions',
31+
size: 1,
32+
minSize: 1,
33+
cell: (cell) => (<div className="flex justify-end">
34+
<InstanceLogInCell instance={cell.row.original} />
35+
</div>),
3036
},
31-
{
37+
isSelfManaged && {
3238
accessorKey: 'instanceFqdn',
33-
header: 'Instance Url',
34-
cell: (cell) => <InstanceLogInCell instance={cell.row.original} />,
35-
},
36-
{
37-
accessorKey: 'instanceTypeId',
38-
header: 'Instance Type',
39+
size: 90,
40+
header: 'URL',
3941
cell: (cell) => {
40-
return renderInstanceTypeOption(cell.getValue() as InstanceTypes);
42+
return calculateInstanceFQDN({
43+
secure: cell.row.original.operationsApiSecure ? 'true' : 'false',
44+
port: cell.row.original.operationsApiPort,
45+
fqdn: cell.row.original.instanceFqdn,
46+
});
4147
},
4248
},
43-
{
49+
!isSelfManaged && {
50+
accessorKey: 'name',
51+
size: 90,
52+
header: 'Name',
53+
},
54+
!isSelfManaged && {
4455
accessorKey: 'status',
4556
header: 'Status',
57+
size: 1,
58+
minSize: 1,
4659
cell: (cell) => {
4760
const status = cell.getValue() as string;
4861
return <Badge variant={renderBadgeStatusVariant(status)}>{capitalizeWords(status)}</Badge>;
4962
},
5063
},
51-
{
64+
!isSelfManaged && {
5265
accessorKey: 'version',
66+
size: 1,
67+
minSize: 1,
5368
header: 'Version',
5469
},
55-
{
70+
!isSelfManaged && {
5671
accessorKey: 'storageGb',
72+
size: 1,
73+
minSize: 1,
5774
header: 'Storage',
5875
cell: (cell) => {
5976
const value = cell.getValue() as number;
6077
return humanFileSize(value, Math.pow(1024, 3));
6178
},
6279
},
63-
{
80+
!isSelfManaged && {
6481
accessorKey: 'cpuCores',
82+
size: 1,
83+
minSize: 1,
6584
header: 'Cores/Threads',
6685
cell: (cell) => {
6786
return <>{cell.row.original.cpuCores} / {cell.row.original.threads}</>;
6887
},
6988
},
70-
{
89+
!isSelfManaged && {
7190
accessorKey: 'memoryMb',
91+
size: 1,
92+
minSize: 1,
7293
header: 'Memory',
7394
cell: (cell) => {
7495
const value = cell.getValue() as number;
7596
return humanFileSize(value, Math.pow(1024, 2));
7697
},
7798
},
78-
],
79-
[],
99+
] satisfies Array<ColumnDef<Instance> | false>).filter(excludeFalsy),
100+
[isSelfManaged],
101+
);
102+
const instances = useMemo(
103+
() =>
104+
cluster?.instances?.filter(instance => instance.status !== 'REMOVED') ?? [],
105+
[cluster],
80106
);
81107
return (
82108
<>
@@ -86,8 +112,8 @@ export function ClusterIndex() {
86112
<CardContent className="p-0 min-h-96">
87113
{clusterIsLoading
88114
? <TextLoadingSkeleton />
89-
: cluster?.instances?.length
90-
? <DataTable data={cluster.instances} columns={columns} />
115+
: instances.length
116+
? <DataTable data={instances} columns={columns} />
91117
: <EmptyCluster />
92118
}
93119
</CardContent>

src/lib/InstanceType.ts

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)