feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
/* stylelint-disable declaration-no-important */
|
||||
.table-container {
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: var(--coz-fg-dim, rgba(6, 7, 9, 30%));
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: block !important;
|
||||
width: 4px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar:hover {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-corner {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useRef } from 'react';
|
||||
|
||||
import cls from 'classnames';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { type ColumnProps } from '@coze-arch/coze-design';
|
||||
import { UIEmpty } from '@coze-arch/bot-semi';
|
||||
import { type PersonalAccessToken } from '@coze-arch/bot-api/pat_permission_api';
|
||||
|
||||
import { useTableHeight } from '@/hooks/use-table-height';
|
||||
import { AuthTable } from '@/components/auth-table';
|
||||
|
||||
import { getTableColumnConf } from './table-column';
|
||||
|
||||
import styles from './index.module.less';
|
||||
export type GetCustomDataConfig = (options: {
|
||||
onEdit: (v: PersonalAccessToken) => void;
|
||||
onDelete: (id: string) => void;
|
||||
}) => ColumnProps<PersonalAccessToken>[];
|
||||
|
||||
interface DataTableProps {
|
||||
loading: boolean;
|
||||
size?: 'small' | 'default';
|
||||
type?: 'primary' | 'default';
|
||||
dataSource: PersonalAccessToken[];
|
||||
onEdit: (v: PersonalAccessToken) => void;
|
||||
onDelete: (id: string) => void;
|
||||
onAddClick: () => void;
|
||||
renderDataEmptySlot?: () => React.ReactElement | null;
|
||||
getCustomDataConfig?: GetCustomDataConfig;
|
||||
}
|
||||
|
||||
export const DataTable = ({
|
||||
loading,
|
||||
dataSource,
|
||||
onEdit,
|
||||
onDelete,
|
||||
onAddClick,
|
||||
renderDataEmptySlot,
|
||||
getCustomDataConfig = getTableColumnConf,
|
||||
size,
|
||||
type,
|
||||
}: DataTableProps) => {
|
||||
const tableRef = useRef<HTMLDivElement>(null);
|
||||
const tableHeight = useTableHeight(tableRef);
|
||||
|
||||
const columns: ColumnProps<PersonalAccessToken>[] = getCustomDataConfig?.({
|
||||
onEdit,
|
||||
onDelete,
|
||||
}).filter(item => !item.hidden);
|
||||
|
||||
return (
|
||||
<div className={cls('flex-1', styles['table-container'])} ref={tableRef}>
|
||||
<AuthTable
|
||||
useHoverStyle={false}
|
||||
size={size}
|
||||
type={type}
|
||||
tableProps={{
|
||||
rowKey: 'id',
|
||||
loading,
|
||||
dataSource,
|
||||
columns,
|
||||
scroll: { y: tableHeight },
|
||||
}}
|
||||
empty={
|
||||
renderDataEmptySlot?.() || (
|
||||
<UIEmpty
|
||||
empty={{
|
||||
title: I18n.t('no_api_token_1'),
|
||||
description: I18n.t('add_api_token_1'),
|
||||
btnText: I18n.t('add_new_token_button_1'),
|
||||
btnOnClick: onAddClick,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { type ColumnProps } from '@coze-arch/coze-design';
|
||||
import { type PersonalAccessToken } from '@coze-arch/bot-api/pat_permission_api';
|
||||
|
||||
import { getDetailTime } from '@/utils/time';
|
||||
|
||||
export const columnCreateAtConf: () => ColumnProps<PersonalAccessToken> =
|
||||
() => ({
|
||||
title: I18n.t('coze_api_list3'),
|
||||
dataIndex: 'created_at',
|
||||
render: (createTime: number) => getDetailTime(createTime),
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { type ColumnProps } from '@coze-arch/coze-design';
|
||||
import { type PersonalAccessToken } from '@coze-arch/bot-api/pat_permission_api';
|
||||
|
||||
import { getExpirationTime } from '@/utils/time';
|
||||
|
||||
export const columnExpireAtConf: () => ColumnProps<PersonalAccessToken> =
|
||||
() => ({
|
||||
title: I18n.t('expire_time_1'), // 状态
|
||||
dataIndex: 'expire_at',
|
||||
render: (expireTime: number) => getExpirationTime(expireTime),
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { type ColumnProps } from '@coze-arch/coze-design';
|
||||
import { type PersonalAccessToken } from '@coze-arch/bot-api/pat_permission_api';
|
||||
|
||||
import { getDetailTime } from '@/utils/time';
|
||||
|
||||
export const columnLastUseAtConf: () => ColumnProps<PersonalAccessToken> =
|
||||
() => ({
|
||||
title: I18n.t('coze_api_list4'),
|
||||
dataIndex: 'last_used_at',
|
||||
render: (lastUseTime: number) => getDetailTime(lastUseTime),
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { type ColumnProps } from '@coze-arch/coze-design';
|
||||
import { type PersonalAccessToken } from '@coze-arch/bot-api/pat_permission_api';
|
||||
export const columnNameConf: () => ColumnProps<PersonalAccessToken> = () => ({
|
||||
title: I18n.t('coze_api_list1'),
|
||||
dataIndex: 'name',
|
||||
width: 120,
|
||||
render: (name: string) => <p>{name}</p>,
|
||||
});
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type FC } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozMinusCircle, IconCozEdit } from '@coze-arch/coze-design/icons';
|
||||
import { type ColumnProps, Tooltip, Space } from '@coze-arch/coze-design';
|
||||
import { UIButton, Popconfirm } from '@coze-arch/bot-semi';
|
||||
import { type PersonalAccessToken } from '@coze-arch/bot-api/pat_permission_api';
|
||||
|
||||
import { getStatus } from '@/utils/time';
|
||||
|
||||
import styles from './index.module.less';
|
||||
export const ColumnOpBody: FC<{
|
||||
record: PersonalAccessToken;
|
||||
isCurrentUser?: boolean;
|
||||
onEdit: (v: PersonalAccessToken) => void;
|
||||
onDelete: (id: string) => void;
|
||||
afterConfirmDelete?: () => void;
|
||||
afterCancelDelete?: () => void;
|
||||
}> = ({
|
||||
record,
|
||||
isCurrentUser,
|
||||
onEdit,
|
||||
onDelete,
|
||||
afterConfirmDelete,
|
||||
afterCancelDelete,
|
||||
}) => {
|
||||
const isActive = getStatus(record?.expire_at as number);
|
||||
|
||||
return (
|
||||
<Space align="center" spacing={17}>
|
||||
<Tooltip
|
||||
content={
|
||||
isCurrentUser
|
||||
? I18n.t(isActive ? 'Edit' : 'not_support_edit_1')
|
||||
: I18n.t('org_api_pat_edit_reminder')
|
||||
}
|
||||
>
|
||||
<UIButton
|
||||
onClick={() => onEdit(record)}
|
||||
className={classNames(styles['btn-frame'], {
|
||||
[styles['btn-frame-disabled']]: !isActive,
|
||||
})}
|
||||
theme="borderless"
|
||||
icon={<IconCozEdit className={styles.icon} />}
|
||||
disabled={!isActive || !isCurrentUser}
|
||||
></UIButton>
|
||||
</Tooltip>
|
||||
<Popconfirm
|
||||
style={{ width: 400 }}
|
||||
okType="danger"
|
||||
trigger="click"
|
||||
onConfirm={() => {
|
||||
onDelete(`${record?.id}`);
|
||||
afterConfirmDelete?.();
|
||||
}}
|
||||
onCancel={() => {
|
||||
afterCancelDelete?.();
|
||||
}}
|
||||
content={I18n.t('remove_token_1')}
|
||||
title={I18n.t('remove_token_reminder_1')}
|
||||
>
|
||||
<div>
|
||||
<Tooltip content={I18n.t('Remove')}>
|
||||
<UIButton
|
||||
className={styles['btn-frame']}
|
||||
theme="borderless"
|
||||
icon={<IconCozMinusCircle className={styles.icon} />}
|
||||
></UIButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
export const columnOpConf: () => ColumnProps<PersonalAccessToken> = () => ({
|
||||
title: I18n.t('coze_api_list5'),
|
||||
width: 120,
|
||||
render: (_: string, _record: unknown) => null,
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Tag } from '@coze-arch/coze-design';
|
||||
import { type ColumnProps } from '@coze-arch/coze-design';
|
||||
import { type PersonalAccessToken } from '@coze-arch/bot-api/pat_permission_api';
|
||||
|
||||
import { getStatus } from '@/utils/time';
|
||||
|
||||
export const columnStatusConf: () => ColumnProps<PersonalAccessToken> = () => ({
|
||||
title: I18n.t('api_status_1'),
|
||||
dataIndex: 'id',
|
||||
width: 80,
|
||||
render: (_: string, record: PersonalAccessToken) => {
|
||||
const isActive = getStatus(record?.expire_at as number);
|
||||
return (
|
||||
<Tag size="small" color={isActive ? 'primary' : 'grey'}>
|
||||
{I18n.t(isActive ? 'api_status_active_1' : 'api_status_expired_1')}
|
||||
</Tag>
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
.btn-frame {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
|
||||
.icon {
|
||||
color: #6b6b75;
|
||||
}
|
||||
|
||||
&.btn-frame-disabled {
|
||||
.icon {
|
||||
color: rgba(29, 28, 35, 20%);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type ColumnProps } from '@coze-arch/coze-design';
|
||||
import { type PersonalAccessToken } from '@coze-arch/bot-api/pat_permission_api';
|
||||
|
||||
import { columnStatusConf } from './column-status';
|
||||
import { ColumnOpBody, columnOpConf } from './column-op';
|
||||
import { columnNameConf } from './column-name';
|
||||
import { columnLastUseAtConf } from './column-last-use-at';
|
||||
import { columnExpireAtConf } from './column-expire-at';
|
||||
import { columnCreateAtConf } from './column-create-at';
|
||||
export const getTableColumnConf = ({
|
||||
onEdit,
|
||||
onDelete,
|
||||
}: {
|
||||
onEdit: (v: PersonalAccessToken) => void;
|
||||
onDelete: (id: string) => void;
|
||||
}): ColumnProps<PersonalAccessToken>[] => [
|
||||
columnNameConf(),
|
||||
columnCreateAtConf(),
|
||||
columnLastUseAtConf(),
|
||||
columnExpireAtConf(),
|
||||
columnStatusConf(),
|
||||
{
|
||||
...columnOpConf(),
|
||||
render: (_, record) => (
|
||||
<ColumnOpBody {...{ record, isCurrentUser: true, onEdit, onDelete }} />
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
export const patColumn = {
|
||||
columnNameConf,
|
||||
columnCreateAtConf,
|
||||
columnLastUseAtConf,
|
||||
columnExpireAtConf,
|
||||
columnStatusConf,
|
||||
ColumnOpBody,
|
||||
columnOpConf,
|
||||
};
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { type FetchCustomPatList } from '@/hooks/pat/use-token';
|
||||
import { usePatOperation } from '@/hooks/pat/action/use-pat-operation';
|
||||
|
||||
import { TopBody } from './top-body';
|
||||
import { ResultModal } from './result-modal';
|
||||
import { PermissionModal, type PermissionModalProps } from './permission-modal';
|
||||
import { DataTable, type GetCustomDataConfig } from './data-table';
|
||||
export interface PATProps {
|
||||
size?: 'small' | 'default';
|
||||
type?: 'primary' | 'default';
|
||||
renderTopBodySlot?: (options: {
|
||||
openAddModal: () => void;
|
||||
}) => React.ReactNode;
|
||||
renderDataEmptySlot?: () => React.ReactElement | null;
|
||||
getCustomDataConfig?: GetCustomDataConfig;
|
||||
fetchCustomPatList?: FetchCustomPatList;
|
||||
renderPermissionModal?: (options: PermissionModalProps) => void;
|
||||
afterCancelPermissionModal?: (isCreate: boolean) => void;
|
||||
}
|
||||
export const PatBody: React.FC<PATProps> = ({
|
||||
size,
|
||||
type,
|
||||
renderTopBodySlot,
|
||||
renderDataEmptySlot,
|
||||
getCustomDataConfig,
|
||||
fetchCustomPatList,
|
||||
renderPermissionModal,
|
||||
afterCancelPermissionModal,
|
||||
}) => {
|
||||
const {
|
||||
onAddClick,
|
||||
loading,
|
||||
dataSource,
|
||||
editHandle,
|
||||
runDelete,
|
||||
refreshHandle,
|
||||
showDataForm,
|
||||
isCreate,
|
||||
createSuccessHandle,
|
||||
onCancel,
|
||||
successData,
|
||||
showResult,
|
||||
setShowResult,
|
||||
editInfo,
|
||||
fetchData,
|
||||
} = usePatOperation({ fetchCustomPatList, afterCancelPermissionModal });
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
const permissionModalOptions = {
|
||||
isCreate,
|
||||
onRefresh: refreshHandle,
|
||||
editInfo,
|
||||
onCreateSuccess: createSuccessHandle,
|
||||
onCancel,
|
||||
};
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col">
|
||||
{renderTopBodySlot?.({ openAddModal: onAddClick }) || (
|
||||
<TopBody openAddModal={onAddClick} />
|
||||
)}
|
||||
<DataTable
|
||||
size={size}
|
||||
type={type}
|
||||
loading={loading}
|
||||
dataSource={dataSource}
|
||||
onEdit={editHandle}
|
||||
onDelete={runDelete}
|
||||
onAddClick={onAddClick}
|
||||
renderDataEmptySlot={renderDataEmptySlot}
|
||||
getCustomDataConfig={getCustomDataConfig}
|
||||
/>
|
||||
{showDataForm
|
||||
? renderPermissionModal?.(permissionModalOptions) || (
|
||||
<PermissionModal {...permissionModalOptions} />
|
||||
)
|
||||
: null}
|
||||
<ResultModal
|
||||
data={successData}
|
||||
visible={showResult}
|
||||
onOk={() => setShowResult(false)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
.expiration-select {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
:global {
|
||||
.semi-form-field {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type FC, useState } from 'react';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Form, Input } from '@coze-arch/coze-design';
|
||||
import { type GetPersonalAccessTokenAndPermissionResponseData } from '@coze-arch/bot-api/pat_permission_api';
|
||||
|
||||
import {
|
||||
ExpirationDate,
|
||||
disabledDate,
|
||||
getExpirationOptions,
|
||||
getExpirationTime,
|
||||
} from '@/utils/time';
|
||||
import { Tips } from '@/components/instructions-wrap';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
export const CommonFormParams: FC<{
|
||||
isCreate?: boolean;
|
||||
patPermission?: GetPersonalAccessTokenAndPermissionResponseData;
|
||||
}> = ({ isCreate, patPermission }) => {
|
||||
const [durationDay, setDurationDay] = useState<ExpirationDate>();
|
||||
const dataOptionsList = getExpirationOptions();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Input
|
||||
trigger={['blur', 'change']}
|
||||
field="name"
|
||||
label={{
|
||||
text: I18n.t('coze_api_list1'),
|
||||
required: true,
|
||||
}}
|
||||
placeholder={''}
|
||||
maxLength={20}
|
||||
rules={[{ required: true, message: '' }]}
|
||||
/>
|
||||
<Form.Slot
|
||||
label={{
|
||||
text: I18n.t('expire_time_1'),
|
||||
required: true,
|
||||
extra: <Tips tips={I18n.t('expired_time_forbidden_1')} />,
|
||||
}}
|
||||
>
|
||||
{isCreate ? (
|
||||
<>
|
||||
<div className={styles['expiration-select']}>
|
||||
<Form.Select
|
||||
noLabel={true}
|
||||
field="duration_day"
|
||||
style={{ width: '100%' }}
|
||||
disabled={!isCreate}
|
||||
optionList={dataOptionsList}
|
||||
onChange={v => setDurationDay(v as ExpirationDate)}
|
||||
rules={[{ required: true, message: '' }]}
|
||||
placeholder={I18n.t('select_expired_time_1')}
|
||||
/>
|
||||
|
||||
{durationDay === ExpirationDate.CUSTOMIZE && (
|
||||
<Form.DatePicker
|
||||
noLabel={true}
|
||||
field="expire_at"
|
||||
style={{ width: '100%' }}
|
||||
disabled={!isCreate}
|
||||
disabledDate={disabledDate}
|
||||
position="bottomRight"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<Input
|
||||
disabled
|
||||
value={
|
||||
patPermission?.personal_access_token?.expire_at
|
||||
? getExpirationTime(
|
||||
patPermission?.personal_access_token?.expire_at as number,
|
||||
)
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Form.Slot>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
.permission-form-content {
|
||||
overflow-y: auto;
|
||||
|
||||
width: 417px;
|
||||
max-height: 522px;
|
||||
margin-top: 16px;
|
||||
margin-right: -11px;
|
||||
padding-right: 8px;
|
||||
.scroll-wrap;
|
||||
|
||||
:global {
|
||||
/* stylelint-disable-next-line no-descending-specificity */
|
||||
.semi-form-field {
|
||||
padding: 0;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.semi-form-field-label {
|
||||
margin-bottom: 6px;
|
||||
padding: 0 8px;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
line-height: 16px;
|
||||
color: var(--coz-fg-primary);
|
||||
}
|
||||
|
||||
.semi-form-field-error-message {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.semi-form-field-main {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.semi-form-field-label-required .semi-form-field-label-text::after,
|
||||
.semi-form-field-label-with-extra .semi-form-field-label-extra {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-wrap {
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: var(--coz-fg-dim, rgba(6, 7, 9, 30%));
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
// stylelint-disable-next-line declaration-no-important
|
||||
display: block !important;
|
||||
width: 4px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar:hover {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-corner {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
forwardRef,
|
||||
type PropsWithChildren,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
} from 'react';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import {
|
||||
type PersonalAccessToken,
|
||||
type CreatePersonalAccessTokenAndPermissionResponseData,
|
||||
type GetPersonalAccessTokenAndPermissionResponseData,
|
||||
} from '@coze-arch/bot-api/pat_permission_api';
|
||||
import { Form, type FormApi, Modal, Spin, Toast } from '@coze-arch/coze-design';
|
||||
|
||||
import { usePatForm, type FormApiInfo } from '@/hooks/pat/action/use-pat-form';
|
||||
|
||||
import { CommonFormParams } from './common-form-params';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
export interface PermissionModalProps {
|
||||
editInfo?: PersonalAccessToken;
|
||||
isCreate: boolean;
|
||||
isReady?: boolean;
|
||||
onRefresh: () => void;
|
||||
onCreateSuccess: (
|
||||
v: CreatePersonalAccessTokenAndPermissionResponseData,
|
||||
) => void;
|
||||
onCancel: () => void;
|
||||
onPatPermissionChange?: (
|
||||
data?: GetPersonalAccessTokenAndPermissionResponseData,
|
||||
) => void;
|
||||
onCustomFormValueChange?: (values: unknown, changedValue: unknown) => void;
|
||||
validateCustomParams?: () => boolean;
|
||||
getCustomParams?: () => Record<string, unknown>;
|
||||
afterSubmit?: (params: Record<string, unknown>) => void;
|
||||
isShowAuthMigrateNotice?: boolean;
|
||||
}
|
||||
|
||||
export interface PermissionModalRef {
|
||||
setFormValue: (key: string, value: unknown) => void;
|
||||
validateParams: () => void;
|
||||
getFormValues: () => Record<string, unknown>;
|
||||
}
|
||||
|
||||
export const PermissionModal = forwardRef(function PermissionModal(
|
||||
{
|
||||
editInfo,
|
||||
isCreate,
|
||||
onRefresh,
|
||||
onCreateSuccess,
|
||||
onCancel,
|
||||
children,
|
||||
onPatPermissionChange,
|
||||
onCustomFormValueChange,
|
||||
validateCustomParams,
|
||||
getCustomParams,
|
||||
afterSubmit,
|
||||
isReady = true,
|
||||
isShowAuthMigrateNotice = false,
|
||||
}: PropsWithChildren<PermissionModalProps>,
|
||||
ref,
|
||||
) {
|
||||
const formApi = useRef<FormApi<FormApiInfo>>();
|
||||
const {
|
||||
isFailToValid,
|
||||
ready,
|
||||
loading,
|
||||
onSubmit,
|
||||
onFormValueChange,
|
||||
patPermission,
|
||||
successData,
|
||||
updateSuccessData,
|
||||
validateParams,
|
||||
} = usePatForm({
|
||||
editInfo,
|
||||
isCreate,
|
||||
formApi,
|
||||
validateCustomParams,
|
||||
getCustomParams,
|
||||
afterSubmit,
|
||||
isShowAuthMigrateNotice,
|
||||
});
|
||||
const modalReady = isReady && ready;
|
||||
|
||||
useEffect(() => {
|
||||
if (successData) {
|
||||
Toast.success({ content: I18n.t('Create_success'), showClose: false });
|
||||
onCreateSuccess(successData);
|
||||
onRefresh();
|
||||
}
|
||||
}, [successData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (updateSuccessData) {
|
||||
Toast.success({ content: I18n.t('Edit_success'), showClose: false });
|
||||
onRefresh();
|
||||
}
|
||||
}, [updateSuccessData]);
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => ({
|
||||
setFormValue: (key: string, value: unknown) => {
|
||||
formApi.current?.setValue(key as keyof FormApiInfo, value);
|
||||
},
|
||||
getFormValues: () => formApi.current?.getValues(),
|
||||
validateParams,
|
||||
}),
|
||||
[validateParams],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
onPatPermissionChange?.(patPermission);
|
||||
}, [patPermission]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={isCreate ? I18n.t('add_new_pat_1') : I18n.t('edit_pat_1')}
|
||||
visible={true}
|
||||
width={480}
|
||||
centered
|
||||
maskClosable={false}
|
||||
onCancel={onCancel}
|
||||
onOk={onSubmit}
|
||||
okButtonProps={{
|
||||
disabled: isFailToValid || !modalReady,
|
||||
loading,
|
||||
}}
|
||||
cancelText={I18n.t('cancel')}
|
||||
okText={I18n.t('confirm')}
|
||||
>
|
||||
<Spin spinning={!modalReady}>
|
||||
<div className={styles['permission-form-content']}>
|
||||
<Form<FormApiInfo>
|
||||
showValidateIcon={false}
|
||||
getFormApi={api => (formApi.current = api)}
|
||||
onValueChange={(values, changedValue) => {
|
||||
if (onCustomFormValueChange) {
|
||||
onCustomFormValueChange(values, changedValue);
|
||||
} else {
|
||||
onFormValueChange(values, changedValue as FormApiInfo);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<CommonFormParams
|
||||
isCreate={isCreate}
|
||||
patPermission={patPermission}
|
||||
/>
|
||||
{children}
|
||||
</Form>
|
||||
</div>
|
||||
</Spin>
|
||||
</Modal>
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
.result-frame {
|
||||
:global {
|
||||
.semi-modal-body {
|
||||
// stylelint-disable-next-line declaration-no-important
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.warn-text {
|
||||
margin-bottom: 32px;
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
color: var(--Light-usage-text---color-text-0, #1d1c23);
|
||||
}
|
||||
|
||||
.title-text {
|
||||
margin-bottom: 8px;
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 22px;
|
||||
color: var(--Light-usage-text---color-text-0, #1d1c23);
|
||||
}
|
||||
|
||||
.para {
|
||||
overflow: hidden;
|
||||
|
||||
max-width: 90%;
|
||||
margin-bottom: 36px;
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
color: var(--Light-usage-text---color-text-0, #1d1c23);
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.key-text {
|
||||
overflow: hidden;
|
||||
|
||||
max-width: 487px;
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
color: var(--Light-usage-text---color-text-0, #1d1c23);
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.sp {
|
||||
width: 100%;
|
||||
margin-bottom: 44px;
|
||||
|
||||
.icon {
|
||||
cursor: pointer;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
|
||||
svg {
|
||||
color: rgba(77, 83, 232, 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import copy from 'copy-to-clipboard';
|
||||
import { useMemoizedFn } from 'ahooks';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozCopy } from '@coze-arch/coze-design/icons';
|
||||
import {
|
||||
UIModal,
|
||||
Typography,
|
||||
Toast,
|
||||
Space,
|
||||
Tooltip,
|
||||
} from '@coze-arch/bot-semi';
|
||||
import { type CreatePersonalAccessTokenAndPermissionResponseData } from '@coze-arch/bot-api/pat_permission_api';
|
||||
|
||||
import { getExpirationTime } from '@/utils/time';
|
||||
|
||||
import s from './index.module.less';
|
||||
|
||||
interface ResultProps {
|
||||
data?: CreatePersonalAccessTokenAndPermissionResponseData;
|
||||
visible: boolean;
|
||||
onOk: () => void;
|
||||
}
|
||||
|
||||
// 新建编辑 PAT
|
||||
export const ResultModal = ({ visible, onOk, data }: ResultProps) => {
|
||||
const doCopyAsync = useMemoizedFn(() => {
|
||||
const targetKey = data?.token;
|
||||
if (targetKey) {
|
||||
doCopy(targetKey);
|
||||
}
|
||||
});
|
||||
|
||||
const doCopy = useMemoizedFn(targetText => {
|
||||
const res = copy(targetText);
|
||||
if (!res) {
|
||||
throw new Error('custom error');
|
||||
}
|
||||
Toast.success({
|
||||
content: I18n.t('token_copied_1'),
|
||||
showClose: false,
|
||||
});
|
||||
});
|
||||
return (
|
||||
<UIModal
|
||||
className={s['result-frame']}
|
||||
title={I18n.t('new_pat_1')}
|
||||
visible={visible}
|
||||
width={560}
|
||||
centered
|
||||
onOk={onOk}
|
||||
onCancel={onOk}
|
||||
okText={I18n.t('confirm')}
|
||||
footer={null}
|
||||
>
|
||||
<p className={s['warn-text']}>{I18n.t('new_pat_reminder_1')}</p>
|
||||
<p className={s['title-text']}>{I18n.t('coze_api_list1')}</p>
|
||||
<Typography.Paragraph className={s.para} ellipsis={{ rows: 1 }}>
|
||||
{data?.personal_access_token?.name ?? '-'}
|
||||
</Typography.Paragraph>
|
||||
<p className={s['title-text']}>{I18n.t('expire_time_1')}</p>
|
||||
<Typography.Paragraph className={s.para} ellipsis={{ rows: 1 }}>
|
||||
{getExpirationTime(data?.personal_access_token.expire_at as number)}
|
||||
</Typography.Paragraph>
|
||||
<p className={s['title-text']}>{I18n.t('token_key_1')}</p>
|
||||
<Space spacing={4} className={s.sp}>
|
||||
<Typography.Paragraph className={s['key-text']} ellipsis={{ rows: 1 }}>
|
||||
{data?.token}
|
||||
</Typography.Paragraph>
|
||||
<Tooltip content={I18n.t('Copy')}>
|
||||
<IconCozCopy className={s.icon} onClick={doCopyAsync} />
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</UIModal>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type FC } from 'react';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Button, Space } from '@coze-arch/coze-design';
|
||||
|
||||
import { PATInstructionWrap } from '@/components/instructions-wrap';
|
||||
|
||||
export const TopBody: FC<{
|
||||
openAddModal: () => void;
|
||||
}> = ({ openAddModal }) => (
|
||||
<Space vertical spacing={20}>
|
||||
<Space className="w-full">
|
||||
<h3 className="flex-1 m-0">{I18n.t('auth_tab_pat')}</h3>
|
||||
<Button onClick={openAddModal} theme="solid" type="primary">
|
||||
{I18n.t('add_new_token_button_1')}
|
||||
</Button>
|
||||
</Space>
|
||||
<div className="w-full">
|
||||
<PATInstructionWrap
|
||||
onClick={() => {
|
||||
window.open(
|
||||
IS_OVERSEA
|
||||
? // cp-disable-next-line
|
||||
'https://www.coze.com/open/docs/developer_guides/coze_api_overview'
|
||||
: // cp-disable-next-line
|
||||
'https://www.coze.cn/open/docs/developer_guides/coze_api_overview',
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Space>
|
||||
);
|
||||
Reference in New Issue
Block a user