Skip to content

Commit

Permalink
feat: keypair info/setting modal in credential page
Browse files Browse the repository at this point in the history
  • Loading branch information
ironAiken2 committed Dec 11, 2024
1 parent b19dfdb commit c2f4135
Show file tree
Hide file tree
Showing 3 changed files with 260 additions and 10 deletions.
55 changes: 47 additions & 8 deletions react/src/components/UserCredentialList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@ import {
} from '../helper';
import { useUpdatableState } from '../hooks';
import { useBAIPaginationOptionState } from '../hooks/reactPaginationQueryOptions';
import UserKeypairSettingModal from '../pages/UserKeypairSettingModal';
import { UserKeypairSettingModalFragment$key } from '../pages/__generated__/UserKeypairSettingModalFragment.graphql';
import BAIPropertyFilter from './BAIPropertyFilter';
import BAITable from './BAITable';
import Flex from './Flex';
import { UserCredentialListDeleteMutation } from './__generated__/UserCredentialListDeleteMutation.graphql';
import { UserCredentialListModifyMutation } from './__generated__/UserCredentialListModifyMutation.graphql';
import { UserCredentialListQuery } from './__generated__/UserCredentialListQuery.graphql';
import {
UserCredentialListQuery,
UserCredentialListQuery$data,
} from './__generated__/UserCredentialListQuery.graphql';
import {
DeleteOutlined,
InfoCircleOutlined,
Expand All @@ -36,6 +41,10 @@ import { useState, useTransition } from 'react';
import { useTranslation } from 'react-i18next';
import { useLazyLoadQuery, useMutation } from 'react-relay';

type Keypair = NonNullable<
NonNullable<UserCredentialListQuery$data['keypair_list']>['items'][number]
>;

const UserCredentialList: React.FC = () => {
const { t } = useTranslation();
const { token } = theme.useToken();
Expand All @@ -45,6 +54,11 @@ const UserCredentialList: React.FC = () => {
const [activeType, setActiveType] = useState<'active' | 'inactive'>('active');
const [order, setOrder] = useState<string>();
const [filterString, setFilterString] = useState<string>();
const [currentKeypairInfo, setCurrentKeypairInfo] =
useState<UserKeypairSettingModalFragment$key | null>();
const [openUserKeypairSettingModal, setOpenUserKeypairSettingModal] =
useState(false);

const [isPendingRefresh, startRefreshTransition] = useTransition();
const [isActiveTypePending, startActiveTypeTransition] = useTransition();
const [isPendingPageChange, startPageChangeTransition] = useTransition();
Expand Down Expand Up @@ -90,6 +104,8 @@ const UserCredentialList: React.FC = () => {
num_queries
concurrency_used @since(version: "24.09.0")
concurrency_limit @since(version: "24.09.0")
...UserKeypairSettingModalFragment
}
total_count
}
Expand Down Expand Up @@ -217,10 +233,17 @@ const UserCredentialList: React.FC = () => {
icon={<ReloadOutlined />}
/>
</Tooltip>
<Button type="primary">{t('credential.AddCredential')}</Button>
<Button
type="primary"
onClick={() => {
setOpenUserKeypairSettingModal(true);
}}
>
{t('credential.AddCredential')}
</Button>
</Flex>
</Flex>
<BAITable
<BAITable<Keypair>
// resizable
rowKey={'id'}
scroll={{ x: 'max-content' }}
Expand Down Expand Up @@ -343,7 +366,7 @@ const UserCredentialList: React.FC = () => {
key: 'control',
title: t('general.Control'),
fixed: 'right',
render: (record) => {
render: (value, record) => {
return (
<Flex gap={token.marginXS}>
<Button
Expand All @@ -356,7 +379,10 @@ const UserCredentialList: React.FC = () => {
icon={
<SettingOutlined style={{ color: token.colorInfo }} />
}
onClick={() => {}}
onClick={() => {
console.log('#', record);
setCurrentKeypairInfo(record);
}}
/>
{activeType === 'inactive' && (
<Button
Expand All @@ -365,7 +391,7 @@ const UserCredentialList: React.FC = () => {
onClick={() => {
commitModifyKeypair({
variables: {
access_key: record.access_key,
access_key: record.access_key ?? '',
props: {
is_active: true,
},
Expand Down Expand Up @@ -405,7 +431,7 @@ const UserCredentialList: React.FC = () => {
activeType === 'active'
? commitModifyKeypair({
variables: {
access_key: record.access_key,
access_key: record.access_key ?? '',
props: {
is_active: false,
},
Expand All @@ -429,7 +455,7 @@ const UserCredentialList: React.FC = () => {
})
: commitDeleteKeypair({
variables: {
access_key: record.access_key,
access_key: record.access_key ?? '',
},
onCompleted: (res, errors) => {
if (!res?.delete_keypair?.ok || errors) {
Expand Down Expand Up @@ -482,6 +508,19 @@ const UserCredentialList: React.FC = () => {
});
}}
/>
<UserKeypairSettingModal
userKeypairSettingModalFrgmt={currentKeypairInfo}
open={!!currentKeypairInfo || openUserKeypairSettingModal}
onRequestClose={(success) => {
setCurrentKeypairInfo(null);
setOpenUserKeypairSettingModal(false);
if (success) {
startRefreshTransition(() => {
updateFetchKey();
});
}
}}
/>
</Flex>
);
};
Expand Down
6 changes: 4 additions & 2 deletions react/src/pages/UserCredentialsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@ const UserCredentialsPage: React.FC = () => {
</Flex>
)}
{curTabKey === 'credentials' && (
<Flex direction="column" align="stretch">
<>
<UserCredentialList />
</Flex>
{/* @ts-expect-error */}
<backend-ai-credential-view active />
</>
)}
</Suspense>
</BAICard>
Expand Down
209 changes: 209 additions & 0 deletions react/src/pages/UserKeypairSettingModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import BAIModal from '../components/BAIModal';
import HiddenFormItem from '../components/HiddenFormItem';
import ResourceGroupSelect from '../components/ResourceGroupSelect';
import { useCurrentProjectValue } from '../hooks/useCurrentProject';
import { UserKeypairSettingModalCreateMutation } from './__generated__/UserKeypairSettingModalCreateMutation.graphql';
import { UserKeypairSettingModalFragment$key } from './__generated__/UserKeypairSettingModalFragment.graphql';
import { UserKeypairSettingModalModifyMutation } from './__generated__/UserKeypairSettingModalModifyMutation.graphql';
import {
App,
Col,
Form,
Input,
InputNumber,
ModalProps,
Row,
theme,
} from 'antd';
import { FormInstance } from 'antd/lib';
import graphql from 'babel-plugin-relay/macro';
import { useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { useFragment, useMutation } from 'react-relay';

type KeypairSettingModalFormInput = {
user_id?: string;
rate_limit: number;
resource_policy: string;
};

interface UserKeypairSettingModalProps extends ModalProps {
userKeypairSettingModalFrgmt?: UserKeypairSettingModalFragment$key | null;
onRequestClose: (success: boolean) => void;
}

const UserKeypairSettingModal: React.FC<UserKeypairSettingModalProps> = ({
userKeypairSettingModalFrgmt = null,
onRequestClose,
...modalProps
}) => {
const { t } = useTranslation();
const { message } = App.useApp();
const formRef = useRef<FormInstance<KeypairSettingModalFormInput>>(null);

const currentProject = useCurrentProjectValue();

const keypair = useFragment(
graphql`
fragment UserKeypairSettingModalFragment on KeyPair {
rate_limit
access_key
}
`,
userKeypairSettingModalFrgmt,
);

const [commitCreateKeypair, isInFlightCommitCreateKeypair] =
useMutation<UserKeypairSettingModalCreateMutation>(graphql`
mutation UserKeypairSettingModalCreateMutation(
$user_id: String!
$props: KeyPairInput!
) {
create_keypair(user_id: $user_id, props: $props) {
ok
msg
}
}
`);

const [commitModifyKeypair, isInFlightCommitModifyKeypair] =
useMutation<UserKeypairSettingModalModifyMutation>(graphql`
mutation UserKeypairSettingModalModifyMutation(
$access_key: String!
$props: ModifyKeyPairInput!
) {
modify_keypair(access_key: $access_key, props: $props) {
ok
msg
}
}
`);

return (
<BAIModal
title={
keypair
? t('credential.ModifyKeypairResourcePolicy')
: t('credential.AddCredential')
}
width={500}
destroyOnClose
onOk={() => {
formRef.current
?.validateFields()
.then((values) => {
keypair
? commitModifyKeypair({
variables: {
access_key: keypair.access_key ?? '',
props: {
rate_limit: values.rate_limit,
resource_policy: values.resource_policy,
},
},
onCompleted: (res, errors) => {
if (!res.modify_keypair?.ok || errors) {
message.error(res?.modify_keypair?.msg);
onRequestClose(false);
}
message.success(t('notification.SuccessfullyUpdated'));
onRequestClose(true);
},
onError: (error) => {
message.error(error.message);
onRequestClose(false);
},
})
: commitCreateKeypair({
variables: {
user_id: values.user_id ?? '',
props: {
rate_limit: values.rate_limit,
resource_policy: values.resource_policy,
},
},
onCompleted: (res, errors) => {
if (!res.create_keypair?.ok || errors) {
message.error(res?.create_keypair?.msg);
onRequestClose(false);
}
message.success(t('credential.KeypairCreated'));
onRequestClose(true);
},
onError: (error) => {
message.error(error.message);
onRequestClose(false);
},
});
})
.catch((error) => {});
}}
okButtonProps={{
loading: isInFlightCommitCreateKeypair || isInFlightCommitModifyKeypair,
}}
onCancel={() => onRequestClose(false)}
{...modalProps}
>
<Form
ref={formRef}
layout="vertical"
initialValues={keypair ? { ...keypair } : {}}
>
{!keypair && (
<Form.Item
name="user_id"
label={t('credential.UserIDAsEmail')}
rules={[
{
required: true,
},
{
type: 'email',
},
]}
>
<Input />
</Form.Item>
)}
<Row gutter={16}>
<Col span={12}>
<Form.Item
name="resource_policy"
label={t('credential.ResourcePolicy')}
rules={[
{
required: true,
},
]}
>
<ResourceGroupSelect
projectName={currentProject.name}
autoSelectDefault
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="rate_limit"
label={t('credential.RateLimitFor15min')}
rules={[
{
required: true,
},
{
type: 'number',
min: 0,
max: 50000,
},
]}
>
<InputNumber min={0} max={50000} style={{ width: '100%' }} />
</Form.Item>
</Col>
</Row>
</Form>
</BAIModal>
);
};

export default UserKeypairSettingModal;

0 comments on commit c2f4135

Please sign in to comment.