Skip to content
Open
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
1 change: 1 addition & 0 deletions packages/backend.ai-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@
"@storybook/react-vite": "^9.1.1",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0",
"@testing-library/user-event": "^14.6.1",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/big.js": "^6.2.2",
"@types/lodash": "^4.17.20",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { convertToBinaryUnit, convertToDecimalUnit, SizeUnit } from '../helper';
import BAIFlex from './BAIFlex';
import { Typography } from 'antd';
import { BAIFlex } from 'backend.ai-ui';

interface NumberWithUnitProps {
interface BAINumberWithUnitProps {
numberUnit: string;
targetUnit: SizeUnit;
unitType: 'binary' | 'decimal';
postfix?: string;
}

const NumberWithUnit = ({
const BAINumberWithUnit = ({
numberUnit,
targetUnit,
unitType,
postfix,
}: NumberWithUnitProps) => {
}: BAINumberWithUnitProps) => {
const convertedByTargetUnit =
unitType === 'binary'
? convertToBinaryUnit(numberUnit, targetUnit, 2, true)
Expand All @@ -41,4 +41,4 @@ const NumberWithUnit = ({
);
};

export default NumberWithUnit;
export default BAINumberWithUnit;
168 changes: 168 additions & 0 deletions packages/backend.ai-ui/src/components/BAIResourceNumberWithIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import { convertToBinaryUnit } from '../helper';
import { BAINvidiaIcon } from '../icons';
import BAIFuriosaIcon from '../icons/BAIFuriosaIcon';
import BAIGaudiIcon from '../icons/BAIGaudiIcon';
import BAIIpuIcon from '../icons/BAIIpuIcon';
import BAIRebelIcon from '../icons/BAIRebelIcon';
import BAIRocmIcon from '../icons/BAIRocmIcon';
import BAITpuIcon from '../icons/BAITpuIcon';
import BAIFlex from './BAIFlex';
import NumberWithUnit from './BAINumberWithUnit';
import BAIText from './BAIText';
import { ResourceSlotName, useBAIDeviceMetaData } from './provider';
import { theme, Tooltip } from 'antd';
import _ from 'lodash';
import { CpuIcon, MemoryStickIcon, MicrochipIcon } from 'lucide-react';
import { ReactNode } from 'react';

export type ResourceOpts = {
shmem?: number;
};

export interface BAIResourceNumberWithIconProps {
type: string;
extra?: ReactNode;
opts?: ResourceOpts;
value: string;
hideTooltip?: boolean;
max?: string;
}

const BAIResourceNumberWithIcon = ({
type,
extra,
opts,
value: amount,
max,
hideTooltip = false,
}: BAIResourceNumberWithIconProps) => {
'use memo';

const deviceMetaData = useBAIDeviceMetaData();
const { token } = theme.useToken();

const formatAmount = (amount: string) => {
return deviceMetaData?.[type]?.number_format.binary
? Number(
convertToBinaryUnit(amount, 'g', 2, true)?.numberFixed,
).toString()
: (deviceMetaData?.[type]?.number_format.round_length || 0) > 0
? parseFloat(amount).toFixed(2)
: amount;
};

return (
<BAIFlex direction="row" gap="xxs">
{deviceMetaData?.[type] ? (
<ResourceTypeIcon type={type} showTooltip={!hideTooltip} />
) : (
type
)}
{deviceMetaData?.[type]?.number_format.binary ? (
<NumberWithUnit
numberUnit={amount}
targetUnit="g"
unitType="binary"
postfix={
_.isUndefined(max)
? ''
: max === 'Infinity'
? '~∞'
: `~${formatAmount(max)}`
}
/>
) : (
<>
<BAIText>
{formatAmount(amount)}
{_.isUndefined(max)
? null
: max === 'Infinity'
? '~∞'
: `~${formatAmount(max)}`}
</BAIText>
<BAIText type="secondary" style={{ whiteSpace: 'nowrap' }}>
{deviceMetaData?.[type]?.display_unit || ''}
</BAIText>
</>
)}

{type === 'mem' && opts?.shmem && opts?.shmem > 0 ? (
<BAIText type="secondary" style={{ fontSize: token.fontSizeSM }}>
(SHM: {convertToBinaryUnit(opts.shmem, 'g', 2, true)?.numberFixed}
GiB)
</BAIText>
) : null}
{extra}
</BAIFlex>
);
};

const knownDeviceIcons = {
gaudi: <BAIGaudiIcon />,
furiosa: <BAIFuriosaIcon />,
tpu: <BAITpuIcon />,
ipu: <BAIIpuIcon />,
nvidia: <BAINvidiaIcon />,
rocm: <BAIRocmIcon />,
rebel: <BAIRebelIcon />,
} as const;

interface ResourceTypeIconProps {
type: ResourceSlotName | string;
showTooltip?: boolean;
size?: number;
}

const ResourceTypeIcon = ({
type,
showTooltip = true,
size = 16,
}: ResourceTypeIconProps) => {
'use memo';

const deviceMetaData = useBAIDeviceMetaData();

const getIconContent = () => {
if (type === 'cpu') {
return (
<BAIFlex style={{ width: size, height: size }}>
<CpuIcon />
</BAIFlex>
);
}
if (type === 'mem') {
return (
<BAIFlex style={{ width: size, height: size }}>
<MemoryStickIcon />
</BAIFlex>
);
}

const displayIcon = deviceMetaData[type]?.display_icon;

if (displayIcon && _.keys(knownDeviceIcons).includes(displayIcon)) {
return (
knownDeviceIcons[displayIcon as keyof typeof knownDeviceIcons] ?? null
);
}

return (
<BAIFlex style={{ width: size, height: size }}>
<MicrochipIcon />
</BAIFlex>
);
};

const content = getIconContent();

return showTooltip ? (
<Tooltip title={deviceMetaData[type]?.description || type}>
{content}
</Tooltip>
) : (
<BAIFlex style={{ pointerEvents: 'none' }}>{content}</BAIFlex>
);
};

export default BAIResourceNumberWithIcon;
10 changes: 9 additions & 1 deletion packages/backend.ai-ui/src/components/Table/BAITable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,18 @@ export interface BAIColumnType<RecordType = any>
required?: boolean;
}

export interface BAIColumnGroupType<RecordType = AnyObject>
extends Omit<BAIColumnType<RecordType>, 'dataIndex'> {
children: ColumnsType<RecordType>;
}

/**
* Array type for BAI table columns
*/
export type BAIColumnsType<RecordType = any> = BAIColumnType<RecordType>[];
export type BAIColumnsType<RecordType = any> = (
| BAIColumnGroupType<RecordType>
| BAIColumnType<RecordType>
)[];

/**
* Utility function to determine if a column should be visible
Expand Down
Original file line number Diff line number Diff line change
@@ -1,41 +1,67 @@
import { AllowedVfolderHostsWithPermissionFragment$key } from '../__generated__/AllowedVfolderHostsWithPermissionFragment.graphql';
import { AllowedVfolderHostsWithPermissionQuery } from '../__generated__/AllowedVfolderHostsWithPermissionQuery.graphql';
import { BAIAllowedVfolderHostsWithPermissionFromGroupFragment$key } from '../../__generated__/BAIAllowedVfolderHostsWithPermissionFromGroupFragment.graphql';
import { BAIAllowedVfolderHostsWithPermissionFromKeyPairResourcePolicyFragment$key } from '../../__generated__/BAIAllowedVfolderHostsWithPermissionFromKeyPairResourcePolicyFragment.graphql';
import { BAIAllowedVfolderHostsWithPermissionQuery } from '../../__generated__/BAIAllowedVfolderHostsWithPermissionQuery.graphql';
import BAIFlex from '../BAIFlex';
import BAILink from '../BAILink';
import BAIModal from '../BAIModal';
import { BAITable } from '../Table';
import { CheckCircleFilled, StopFilled } from '@ant-design/icons';
import { Badge, theme } from 'antd';
import { BAITable, BAIFlex, BAILink, BAIModal } from 'backend.ai-ui';
import _ from 'lodash';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { graphql, useFragment, useLazyLoadQuery } from 'react-relay';

interface AllowedVfolderHostsWithPermissionProps {
allowedVfolderHostsWithPermissionFrgmt: AllowedVfolderHostsWithPermissionFragment$key;
}
export type BAIAllowedVfolderHostsWithPermissionProps =
| {
allowedHostPermissionFrgmtFromKeyPair: BAIAllowedVfolderHostsWithPermissionFromKeyPairResourcePolicyFragment$key;
allowedHostPermissionFrgmtFromGroup?: never;
}
| {
allowedHostPermissionFrgmtFromKeyPair?: never;
allowedHostPermissionFrgmtFromGroup: BAIAllowedVfolderHostsWithPermissionFromGroupFragment$key;
};

const AllowedVfolderHostsWithPermission: React.FC<
AllowedVfolderHostsWithPermissionProps
> = ({ allowedVfolderHostsWithPermissionFrgmt }) => {
const BAIAllowedVfolderHostsWithPermission: React.FC<
BAIAllowedVfolderHostsWithPermissionProps
> = ({
allowedHostPermissionFrgmtFromKeyPair,
allowedHostPermissionFrgmtFromGroup,
}) => {
const { t } = useTranslation();
const { token } = theme.useToken();
const [storageHost, setStorageHost] = React.useState<string | null>();

const keypairResourcePolicy = useFragment(
graphql`
fragment AllowedVfolderHostsWithPermissionFragment on KeyPairResourcePolicy {
allowed_vfolder_hosts
}
`,
allowedVfolderHostsWithPermissionFrgmt,
);
const keypairResourcePolicy =
useFragment<BAIAllowedVfolderHostsWithPermissionFromKeyPairResourcePolicyFragment$key>(
graphql`
fragment BAIAllowedVfolderHostsWithPermissionFromKeyPairResourcePolicyFragment on KeyPairResourcePolicy {
allowed_vfolder_hosts
}
`,
allowedHostPermissionFrgmtFromKeyPair,
);

const groupNode =
useFragment<BAIAllowedVfolderHostsWithPermissionFromGroupFragment$key>(
graphql`
fragment BAIAllowedVfolderHostsWithPermissionFromGroupFragment on GroupNode {
allowed_vfolder_hosts
}
`,
allowedHostPermissionFrgmtFromGroup,
);

const allowedVfolderHosts = JSON.parse(
keypairResourcePolicy?.allowed_vfolder_hosts || '{}',
keypairResourcePolicy?.allowed_vfolder_hosts ||
groupNode?.allowed_vfolder_hosts ||
'{}',
);

const { vfolder_host_permissions } =
useLazyLoadQuery<AllowedVfolderHostsWithPermissionQuery>(
useLazyLoadQuery<BAIAllowedVfolderHostsWithPermissionQuery>(
graphql`
query AllowedVfolderHostsWithPermissionQuery {
query BAIAllowedVfolderHostsWithPermissionQuery {
vfolder_host_permissions {
vfolder_host_permission_list
}
Expand Down Expand Up @@ -77,7 +103,7 @@ const AllowedVfolderHostsWithPermission: React.FC<
</BAIFlex>
<BAIModal
centered
title={`${storageHost} ${t('data.explorer.Permission')}`}
title={`${storageHost} ${t('comp:AllowedVfolderHostsWithPermission.Permission')}`}
open={!_.isEmpty(storageHost)}
onCancel={() => setStorageHost(null)}
footer={null}
Expand Down Expand Up @@ -116,12 +142,12 @@ const AllowedVfolderHostsWithPermission: React.FC<
)}
columns={[
{
title: t('data.explorer.Permission'),
title: t('comp:AllowedVfolderHostsWithPermission.Permission'),
dataIndex: 'permission',
key: 'permission',
},
{
title: t('data.explorer.Allowed'),
title: t('comp:AllowedVfolderHostsWithPermission.Allowed'),
dataIndex: 'isAllowed',
key: 'isAllowed',
},
Expand All @@ -132,4 +158,4 @@ const AllowedVfolderHostsWithPermission: React.FC<
);
};

export default AllowedVfolderHostsWithPermission;
export default BAIAllowedVfolderHostsWithPermission;
Loading
Loading