feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
/* stylelint-disable declaration-no-important */
|
||||
/* stylelint-disable no-descending-specificity */
|
||||
/* stylelint-disable max-nesting-depth */
|
||||
// 历史问题
|
||||
.table-wrap {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
margin-right: -11px;
|
||||
padding-right: 11px;
|
||||
}
|
||||
|
||||
.table-content {
|
||||
width: 100%;
|
||||
|
||||
:global {
|
||||
.semi-table-tbody > .semi-table-row,
|
||||
.semi-table-thead > .semi-table-row > .semi-table-row-head,
|
||||
.semi-table-tbody > .semi-table-row:hover > .semi-table-row-cell {
|
||||
background-color: transparent !important;
|
||||
background-image: none !important;
|
||||
}
|
||||
|
||||
.semi-table-tbody > .semi-table-row > .semi-table-row-cell {
|
||||
height: 60px !important;
|
||||
padding: 0 16px !important;
|
||||
font-weight: 400 !important;
|
||||
color: var(--coz-fg-primary) !important;
|
||||
}
|
||||
|
||||
.semi-table-header::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
background-color: transparent;
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
.semi-table-body {
|
||||
padding-top: 0 !important;
|
||||
|
||||
.scroll-wrap;
|
||||
}
|
||||
|
||||
.semi-table-tbody
|
||||
> .semi-table-row
|
||||
> .semi-table-row-cell:last-child::after,
|
||||
.semi-table-tbody
|
||||
> .semi-table-row
|
||||
> .semi-table-row-cell:first-child::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.semi-table-fixed-header table {
|
||||
height: 48px !important;
|
||||
}
|
||||
|
||||
.semi-table-thead > .semi-table-row > .semi-table-row-head {
|
||||
padding: 0 16px !important;
|
||||
color: var(--coz-fg-plus, rgba(6, 7, 9, 96%)) !important;
|
||||
}
|
||||
|
||||
.semi-table-tbody > .semi-table-row {
|
||||
cursor: default !important;
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
.semi-switch:not(.semi-switch-checked) {
|
||||
background-color: var(--semi-color-fill-0);
|
||||
}
|
||||
|
||||
.semi-switch:not(.semi-switch-checked):hover {
|
||||
background-color: var(--semi-color-fill-1);
|
||||
}
|
||||
}
|
||||
|
||||
&.small {
|
||||
:global {
|
||||
.semi-table-tbody > .semi-table-row > .semi-table-row-cell {
|
||||
height: 56px !important;
|
||||
padding: 0 8px !important;
|
||||
}
|
||||
|
||||
.semi-table-fixed-header table {
|
||||
height: 28px !important;
|
||||
}
|
||||
|
||||
.semi-table-thead > .semi-table-row > .semi-table-row-head {
|
||||
padding: 0 8px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.primary {
|
||||
:global {
|
||||
.semi-table-tbody > .semi-table-row > .semi-table-row-cell {
|
||||
color: var(--coz-fg-primary, rgba(6, 7, 9, 80%)) !important;
|
||||
}
|
||||
|
||||
.semi-table-fixed-header table {
|
||||
height: 28px !important;
|
||||
}
|
||||
|
||||
.semi-table-thead > .semi-table-row > .semi-table-row-head {
|
||||
padding: 0 8px !important;
|
||||
color: var(--coz-fg-secondary, rgba(32, 41, 69, 62%)) !important;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-wrap {
|
||||
&::-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,49 @@
|
||||
/*
|
||||
* 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 cls from 'classnames';
|
||||
import { type TableProps, Table } from '@coze-arch/coze-design';
|
||||
|
||||
import styles from './index.module.less';
|
||||
|
||||
export const AuthTable: FC<
|
||||
TableProps & {
|
||||
size?: 'small' | 'default';
|
||||
type?: 'primary' | 'default';
|
||||
}
|
||||
> = ({
|
||||
wrapperClassName,
|
||||
tableProps,
|
||||
size = 'default',
|
||||
type = 'default',
|
||||
...rest
|
||||
}) => (
|
||||
<Table
|
||||
{...rest}
|
||||
wrapperClassName={cls(styles['table-wrap'], wrapperClassName)}
|
||||
tableProps={{
|
||||
...tableProps,
|
||||
className: cls(
|
||||
styles['table-content'],
|
||||
tableProps?.className,
|
||||
styles[size],
|
||||
styles[type],
|
||||
),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
.message-frame {
|
||||
margin-bottom: 24px;
|
||||
padding: 12px 16px;
|
||||
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
line-height: 16px;
|
||||
color: var(--coz-fg-primary);
|
||||
|
||||
background: var(--coz-mg-hglt, rgba(186, 192, 255, 20%));
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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 ReactNode, type FC } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozInfoCircle } from '@coze-arch/coze-design/icons';
|
||||
import { Tooltip, Typography, Space } from '@coze-arch/coze-design';
|
||||
|
||||
import styles from './index.module.less';
|
||||
export const LinkDocs: FC<{ text?: string; onClick?: () => void }> = ({
|
||||
text,
|
||||
onClick,
|
||||
}) => (
|
||||
<Typography.Text
|
||||
className="text-[12px] !font-normal"
|
||||
link
|
||||
onClick={() => {
|
||||
onClick?.();
|
||||
}}
|
||||
>
|
||||
{text ? text : I18n.t('coze_api_instru')}
|
||||
</Typography.Text>
|
||||
);
|
||||
|
||||
export const PATInstructionWrap: FC<{
|
||||
onClick?: () => void;
|
||||
}> = ({ onClick }) => (
|
||||
<div className={styles['message-frame']}>
|
||||
<Space spacing={0}>
|
||||
<p>{I18n.t('pat_reminder_1')}</p>
|
||||
<LinkDocs onClick={onClick} />
|
||||
</Space>
|
||||
<p>{I18n.t('pat_reminder_2')}</p>
|
||||
{IS_OVERSEA ? <p>{I18n.t('api_token_reminder_1')}</p> : null}
|
||||
</div>
|
||||
);
|
||||
|
||||
export const Tips: FC<{ tips: string | ReactNode; className?: string }> = ({
|
||||
tips,
|
||||
className,
|
||||
}) => (
|
||||
<Tooltip theme="dark" trigger="hover" content={tips}>
|
||||
<div
|
||||
className={classNames(
|
||||
'flex items-center justify-center hover:coz-mg-secondary-hovered w-[16px] h-[16px] rounded-[4px] mr-[4px] ml-[2px] text-[12px]',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<IconCozInfoCircle className="coz-fg-secondary" />
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
@@ -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>
|
||||
);
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* 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, useState } from 'react';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { type PersonalAccessToken } from '@coze-arch/bot-api/pat_permission_api';
|
||||
import { Modal, type FormApi } from '@coze-arch/coze-design';
|
||||
|
||||
import { ExpirationDate, getExpireAt } from '@/utils/time';
|
||||
import {
|
||||
usePATPermission,
|
||||
useCreatePAT,
|
||||
useUpdatePAT,
|
||||
} from '@/hooks/pat/use-token';
|
||||
|
||||
export interface FormApiInfo {
|
||||
name: string;
|
||||
duration_day: ExpirationDate;
|
||||
|
||||
expire_at: Date;
|
||||
}
|
||||
|
||||
interface PatFormProps {
|
||||
editInfo?: PersonalAccessToken;
|
||||
isCreate: boolean;
|
||||
isShowAuthMigrateNotice?: boolean;
|
||||
formApi: React.MutableRefObject<FormApi<FormApiInfo> | undefined>;
|
||||
validateCustomParams?: () => boolean;
|
||||
getCustomParams?: () => Record<string, unknown>;
|
||||
afterSubmit?: (params: Record<string, unknown>) => void;
|
||||
}
|
||||
|
||||
const getDurationData = (durationDay: ExpirationDate, expireAt: Date) => ({
|
||||
duration_day: durationDay,
|
||||
...(durationDay === ExpirationDate.CUSTOMIZE
|
||||
? { expire_at: getExpireAt(expireAt as Date) }
|
||||
: {}),
|
||||
});
|
||||
|
||||
const validateName = (name?: string) => Boolean(name);
|
||||
const validateDuration = (durationDay?: ExpirationDate, expireAt?: Date) => {
|
||||
if (!durationDay) {
|
||||
return false;
|
||||
}
|
||||
if (durationDay === ExpirationDate.CUSTOMIZE && !expireAt) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const authMigrateNoticeLSKey = 'auth_migrate_notice_do_not_show_again';
|
||||
|
||||
const useAuthMigrateNotice = (isShowAuthMigrateNotice?: boolean) => {
|
||||
useEffect(() => {
|
||||
if (!isShowAuthMigrateNotice) {
|
||||
return;
|
||||
}
|
||||
if (!localStorage.getItem(authMigrateNoticeLSKey)) {
|
||||
Modal.info({
|
||||
title: I18n.t('api_permissionkey_notification_title'),
|
||||
content: I18n.t('api_permissionkey_notification_content'),
|
||||
okText: I18n.t('got_it'),
|
||||
onOk: () => {
|
||||
localStorage.setItem(authMigrateNoticeLSKey, 'true');
|
||||
},
|
||||
showCancelButton: false,
|
||||
closable: false,
|
||||
maskClosable: false,
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
};
|
||||
|
||||
export const usePatForm = ({
|
||||
editInfo,
|
||||
isCreate,
|
||||
formApi,
|
||||
getCustomParams,
|
||||
validateCustomParams,
|
||||
afterSubmit,
|
||||
isShowAuthMigrateNotice,
|
||||
}: PatFormProps) => {
|
||||
const { patPermission } = usePATPermission({
|
||||
patId: editInfo?.id,
|
||||
});
|
||||
|
||||
const { loading: createLoading, runCreate, successData } = useCreatePAT();
|
||||
const {
|
||||
loading: updateLoading,
|
||||
runUpdate,
|
||||
updateSuccessData,
|
||||
} = useUpdatePAT();
|
||||
|
||||
const [isFailToValid, setIsFailToValid] = useState(true);
|
||||
|
||||
const onSubmit = () => {
|
||||
const {
|
||||
name = '',
|
||||
duration_day,
|
||||
expire_at,
|
||||
} = formApi.current?.getValues() || {};
|
||||
|
||||
const params = {
|
||||
name,
|
||||
...(getCustomParams?.() || {}),
|
||||
};
|
||||
if (isCreate) {
|
||||
runCreate({
|
||||
...params,
|
||||
...getDurationData(duration_day as ExpirationDate, expire_at as Date),
|
||||
});
|
||||
} else {
|
||||
runUpdate({ ...params, id: editInfo?.id ?? '' });
|
||||
}
|
||||
afterSubmit?.({ ...params, duration_day, expire_at });
|
||||
};
|
||||
|
||||
const validateParams = () => {
|
||||
const { name, duration_day, expire_at } =
|
||||
formApi.current?.getValues() || {};
|
||||
|
||||
const nameValid = validateName(name);
|
||||
const isCustomParamsValid = validateCustomParams?.() !== false;
|
||||
const durationValid = isCreate
|
||||
? validateDuration(duration_day, expire_at)
|
||||
: true;
|
||||
setIsFailToValid(!(nameValid && isCustomParamsValid && durationValid));
|
||||
};
|
||||
|
||||
const onFormValueChange = (
|
||||
_values: FormApiInfo,
|
||||
_changedValue: FormApiInfo,
|
||||
) => {
|
||||
validateParams();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isCreate) {
|
||||
formApi.current?.setValue('name', 'Secret token');
|
||||
} else if (patPermission && patPermission?.personal_access_token?.name) {
|
||||
formApi.current?.setValue(
|
||||
'name',
|
||||
patPermission?.personal_access_token?.name,
|
||||
);
|
||||
}
|
||||
}, [patPermission]);
|
||||
|
||||
const ready = isCreate ? true : !!patPermission;
|
||||
|
||||
useAuthMigrateNotice(isShowAuthMigrateNotice);
|
||||
|
||||
return {
|
||||
isFailToValid,
|
||||
ready,
|
||||
loading: updateLoading || createLoading,
|
||||
onSubmit,
|
||||
onFormValueChange,
|
||||
patPermission,
|
||||
validateParams,
|
||||
successData,
|
||||
updateSuccessData,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 { useState } from 'react';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Toast } from '@coze-arch/coze-design';
|
||||
import {
|
||||
type CreatePersonalAccessTokenAndPermissionResponseData,
|
||||
type PersonalAccessToken,
|
||||
} from '@coze-arch/bot-api/pat_permission_api';
|
||||
|
||||
import {
|
||||
useDeletePAT,
|
||||
useGetPATList,
|
||||
type FetchCustomPatList,
|
||||
} from '../use-token';
|
||||
export const usePatOperation = ({
|
||||
fetchCustomPatList,
|
||||
afterCancelPermissionModal,
|
||||
}: {
|
||||
fetchCustomPatList?: FetchCustomPatList;
|
||||
afterCancelPermissionModal?: (isCreate: boolean) => void;
|
||||
}) => {
|
||||
const { loading, dataSource, fetchData } = useGetPATList({
|
||||
fetchCustomPatList,
|
||||
});
|
||||
const { runDelete } = useDeletePAT({
|
||||
successHandle: () => {
|
||||
Toast.success({ content: I18n.t('Delete_success'), showClose: false });
|
||||
fetchData();
|
||||
},
|
||||
});
|
||||
const [showDataForm, setShowDataForm] = useState(false);
|
||||
const [showResult, setShowResult] = useState(false);
|
||||
const [isCreate, setIsCreate] = useState(true);
|
||||
const [editInfo, setEditInfo] = useState<PersonalAccessToken>();
|
||||
const [successData, setSuccessData] =
|
||||
useState<CreatePersonalAccessTokenAndPermissionResponseData>();
|
||||
const onAddClick = () => {
|
||||
setIsCreate(true);
|
||||
setShowDataForm(true);
|
||||
};
|
||||
const editHandle = (v: PersonalAccessToken) => {
|
||||
setEditInfo(v);
|
||||
setIsCreate(false);
|
||||
setShowDataForm(true);
|
||||
};
|
||||
const onCancel = () => {
|
||||
setShowDataForm(false);
|
||||
setEditInfo(undefined);
|
||||
afterCancelPermissionModal?.(isCreate);
|
||||
};
|
||||
|
||||
const createSuccessHandle = (
|
||||
data: CreatePersonalAccessTokenAndPermissionResponseData,
|
||||
) => {
|
||||
setSuccessData(data);
|
||||
setEditInfo(undefined);
|
||||
setShowResult(true);
|
||||
};
|
||||
const refreshHandle = () => {
|
||||
fetchData();
|
||||
setShowDataForm(false);
|
||||
setEditInfo(undefined);
|
||||
};
|
||||
return {
|
||||
dataSource,
|
||||
loading,
|
||||
showDataForm,
|
||||
setShowDataForm,
|
||||
isCreate,
|
||||
editInfo,
|
||||
successData,
|
||||
onAddClick,
|
||||
createSuccessHandle,
|
||||
refreshHandle,
|
||||
editHandle,
|
||||
runDelete,
|
||||
onCancel,
|
||||
setIsCreate,
|
||||
showResult,
|
||||
setShowResult,
|
||||
fetchData,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* 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, useState } from 'react';
|
||||
|
||||
import { useMemoizedFn, useRequest } from 'ahooks';
|
||||
import { REPORT_EVENTS } from '@coze-arch/report-events';
|
||||
import { reporter } from '@coze-arch/logger';
|
||||
import {
|
||||
type PersonalAccessToken,
|
||||
type CreatePersonalAccessTokenAndPermissionRequest,
|
||||
type UpdatePersonalAccessTokenAndPermissionRequest,
|
||||
type CreatePersonalAccessTokenAndPermissionResponseData,
|
||||
type GetPersonalAccessTokenAndPermissionResponseData,
|
||||
type ListPersonalAccessTokensResponse2,
|
||||
} from '@coze-arch/bot-api/pat_permission_api';
|
||||
import { patPermissionApi } from '@coze-arch/bot-api';
|
||||
|
||||
export type FetchCustomPatList =
|
||||
() => Promise<ListPersonalAccessTokensResponse2>;
|
||||
export const useGetPATList = ({
|
||||
fetchCustomPatList,
|
||||
}: {
|
||||
fetchCustomPatList?: FetchCustomPatList;
|
||||
}) => {
|
||||
const [dataSource, setDataSource] = useState<PersonalAccessToken[]>([]);
|
||||
const fetchPatList = useMemoizedFn(() => {
|
||||
if (fetchCustomPatList) {
|
||||
return fetchCustomPatList();
|
||||
}
|
||||
return patPermissionApi.ListPersonalAccessTokens({});
|
||||
});
|
||||
const { loading, run: fetchData } = useRequest(fetchPatList, {
|
||||
manual: true,
|
||||
onSuccess: dataSourceData => {
|
||||
setDataSource(dataSourceData?.data?.personal_access_tokens);
|
||||
reporter.event({
|
||||
eventName: REPORT_EVENTS.openGetPatList,
|
||||
meta: {
|
||||
level: 'success',
|
||||
action: 'ListPersonalAccessTokens',
|
||||
},
|
||||
});
|
||||
},
|
||||
onError: error => {
|
||||
reporter.errorEvent({
|
||||
eventName: REPORT_EVENTS.openGetPatList,
|
||||
error,
|
||||
meta: {
|
||||
action: 'ListPersonalAccessTokens',
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
dataSource,
|
||||
loading,
|
||||
fetchData,
|
||||
};
|
||||
};
|
||||
|
||||
export const useCreatePAT = () => {
|
||||
const [successData, setSuccessData] =
|
||||
useState<CreatePersonalAccessTokenAndPermissionResponseData>();
|
||||
const { loading, run: runCreate } = useRequest(
|
||||
(info: CreatePersonalAccessTokenAndPermissionRequest) =>
|
||||
patPermissionApi.CreatePersonalAccessTokenAndPermission(info),
|
||||
{
|
||||
manual: true,
|
||||
onSuccess: dataSourceData => {
|
||||
setSuccessData(dataSourceData?.data);
|
||||
reporter.event({
|
||||
eventName: REPORT_EVENTS.openPatAction,
|
||||
meta: {
|
||||
level: 'success',
|
||||
action: 'CreatePersonalAccessTokenAndPermission',
|
||||
},
|
||||
});
|
||||
},
|
||||
onError: error => {
|
||||
reporter.errorEvent({
|
||||
eventName: REPORT_EVENTS.openPatAction,
|
||||
error,
|
||||
meta: {
|
||||
action: 'CreatePersonalAccessTokenAndPermission',
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
return {
|
||||
runCreate,
|
||||
loading,
|
||||
successData,
|
||||
};
|
||||
};
|
||||
|
||||
export const useUpdatePAT = (
|
||||
handle: {
|
||||
successHandle?: () => void;
|
||||
} = {},
|
||||
) => {
|
||||
const {
|
||||
loading,
|
||||
run: runUpdate,
|
||||
data: updateSuccessData,
|
||||
} = useRequest(
|
||||
(info: UpdatePersonalAccessTokenAndPermissionRequest) =>
|
||||
patPermissionApi.UpdatePersonalAccessTokenAndPermission(info),
|
||||
{
|
||||
manual: true,
|
||||
onSuccess: () => {
|
||||
handle?.successHandle?.();
|
||||
reporter.event({
|
||||
eventName: REPORT_EVENTS.openPatAction,
|
||||
meta: {
|
||||
level: 'success',
|
||||
action: 'UpdatePersonalAccessTokenAndPermission',
|
||||
},
|
||||
});
|
||||
},
|
||||
onError: error => {
|
||||
reporter.errorEvent({
|
||||
eventName: REPORT_EVENTS.openPatAction,
|
||||
error,
|
||||
meta: {
|
||||
action: 'UpdatePersonalAccessTokenAndPermission',
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
return {
|
||||
runUpdate,
|
||||
loading,
|
||||
updateSuccessData,
|
||||
};
|
||||
};
|
||||
|
||||
export const useDeletePAT = ({
|
||||
successHandle,
|
||||
}: {
|
||||
successHandle: () => void;
|
||||
}) => {
|
||||
const { loading, runAsync } = useRequest(
|
||||
(id: string) =>
|
||||
patPermissionApi.DeletePersonalAccessTokenAndPermission({ id }),
|
||||
{
|
||||
manual: true,
|
||||
},
|
||||
);
|
||||
const runDelete = async (id: string) => {
|
||||
try {
|
||||
await runAsync(id);
|
||||
successHandle();
|
||||
reporter.event({
|
||||
eventName: REPORT_EVENTS.openPatAction,
|
||||
meta: {
|
||||
level: 'success',
|
||||
action: 'DeletePersonalAccessTokenAndPermission',
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
reporter.errorEvent({
|
||||
eventName: REPORT_EVENTS.openPatAction,
|
||||
error: error as Error,
|
||||
meta: {
|
||||
action: 'DeletePersonalAccessTokenAndPermission',
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
runDelete,
|
||||
loading,
|
||||
};
|
||||
};
|
||||
export const usePATPermission = ({ patId }: { patId?: string }) => {
|
||||
const [patPermission, setPatPermission] =
|
||||
useState<GetPersonalAccessTokenAndPermissionResponseData>();
|
||||
|
||||
const { error: detailError, run } = useRequest(
|
||||
(id: string) =>
|
||||
patPermissionApi.GetPersonalAccessTokenAndPermission({ id }),
|
||||
{
|
||||
manual: true,
|
||||
onSuccess: dataSourceData => {
|
||||
setPatPermission(dataSourceData.data);
|
||||
|
||||
reporter.event({
|
||||
eventName: REPORT_EVENTS.openGetPatList,
|
||||
meta: {
|
||||
level: 'success',
|
||||
action: 'GetPersonalAccessTokenAndPermission',
|
||||
},
|
||||
});
|
||||
},
|
||||
onError: error => {
|
||||
reporter.errorEvent({
|
||||
eventName: REPORT_EVENTS.openGetPatList,
|
||||
error,
|
||||
meta: {
|
||||
action: 'GetPersonalAccessTokenAndPermission',
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
useEffect(() => {
|
||||
if (patId) {
|
||||
run(patId);
|
||||
} else {
|
||||
setPatPermission(undefined);
|
||||
}
|
||||
}, [patId]);
|
||||
|
||||
return {
|
||||
patPermission,
|
||||
detailError,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 { useState, useEffect } from 'react';
|
||||
|
||||
export const useTableHeight = (tableRef: React.RefObject<HTMLDivElement>) => {
|
||||
const [tableHeight, setTableHeight] = useState<string>('calc(100vh - 360px)');
|
||||
|
||||
useEffect(() => {
|
||||
if (!tableRef.current) {
|
||||
return;
|
||||
}
|
||||
const calculateHeight = () => {
|
||||
if (tableRef.current) {
|
||||
const topPosition = tableRef.current.getBoundingClientRect().top;
|
||||
setTableHeight(`calc(100vh - ${topPosition + 80}px)`);
|
||||
}
|
||||
};
|
||||
|
||||
calculateHeight();
|
||||
window.addEventListener('resize', calculateHeight);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', calculateHeight);
|
||||
};
|
||||
}, [tableRef.current]);
|
||||
|
||||
return tableHeight;
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { type PATProps, PatBody } from './components/pat';
|
||||
export {
|
||||
disabledDate,
|
||||
ExpirationDate,
|
||||
getExpirationOptions,
|
||||
getExpireAt,
|
||||
getDetailTime,
|
||||
getExpirationTime,
|
||||
getStatus,
|
||||
} from './utils/time';
|
||||
export {
|
||||
LinkDocs,
|
||||
PATInstructionWrap,
|
||||
Tips,
|
||||
} from './components/instructions-wrap';
|
||||
export { useTableHeight } from './hooks/use-table-height';
|
||||
export { patColumn } from './components/pat/data-table/table-column';
|
||||
export { AuthTable } from './components/auth-table';
|
||||
export {
|
||||
PermissionModal,
|
||||
type PermissionModalProps,
|
||||
type PermissionModalRef,
|
||||
} from './components/pat/permission-modal';
|
||||
17
frontend/packages/studio/open-platform/open-auth/src/typings.d.ts
vendored
Normal file
17
frontend/packages/studio/open-platform/open-auth/src/typings.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/// <reference types='@coze-arch/bot-typings' />
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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 dayjs from 'dayjs';
|
||||
import { I18n, type I18nKeysNoOptionsType } from '@coze-arch/i18n';
|
||||
|
||||
const MAX_EXPIRATION_DAYS = 30;
|
||||
// 1-30天有效期
|
||||
export const disabledDate = (date?: Date) => {
|
||||
const today = dayjs().startOf('day'); // 当天的开始时间
|
||||
const thirtyDaysLater = today.add(MAX_EXPIRATION_DAYS, 'day'); // 30天后的日期
|
||||
|
||||
return (
|
||||
dayjs(date).isBefore(today, 'day') ||
|
||||
dayjs(date).isSame(today, 'day') ||
|
||||
dayjs(date).isAfter(thirtyDaysLater, 'day')
|
||||
);
|
||||
};
|
||||
|
||||
export enum ExpirationDate {
|
||||
ONE = '1',
|
||||
THIRTY = '30',
|
||||
CUSTOMIZE = 'customize',
|
||||
}
|
||||
enum ServerTimeValue {
|
||||
PERMANENT = -1,
|
||||
NOT_USE = -1,
|
||||
}
|
||||
|
||||
export const getExpirationOptions = () => {
|
||||
const dataOptionsList = [
|
||||
{
|
||||
label: '1天',
|
||||
value: ExpirationDate.ONE,
|
||||
},
|
||||
{
|
||||
label: '30天',
|
||||
value: ExpirationDate.THIRTY,
|
||||
},
|
||||
{
|
||||
label: I18n.t('customize_key_1'),
|
||||
value: ExpirationDate.CUSTOMIZE,
|
||||
},
|
||||
];
|
||||
const newOptions = dataOptionsList.map(item => {
|
||||
const { value } = item;
|
||||
if (value === ExpirationDate.CUSTOMIZE) {
|
||||
return item;
|
||||
}
|
||||
const currentDate = dayjs();
|
||||
const futureDate = currentDate.add(Number(value), 'day');
|
||||
const date = futureDate.format('YYYY-MM-DD');
|
||||
return {
|
||||
label: I18n.t('expired_time_days_1' as I18nKeysNoOptionsType, {
|
||||
num: Number(value),
|
||||
date,
|
||||
}),
|
||||
value,
|
||||
};
|
||||
});
|
||||
return newOptions;
|
||||
};
|
||||
|
||||
export const getExpireAt = (d: Date) => {
|
||||
const h = 23;
|
||||
const m = 59;
|
||||
const s = 59;
|
||||
const intDate = dayjs(d)
|
||||
.add(h, 'hour')
|
||||
.add(m, 'minute')
|
||||
.add(s, 'second')
|
||||
.unix();
|
||||
return intDate;
|
||||
};
|
||||
|
||||
export const getDetailTime = (d: number) => {
|
||||
if (d === ServerTimeValue.NOT_USE) {
|
||||
return '-';
|
||||
}
|
||||
const showDate = dayjs.unix(d).format('YYYY-MM-DD HH:mm:ss');
|
||||
return showDate;
|
||||
};
|
||||
|
||||
export const getExpirationTime = (d: number) => {
|
||||
if (d === ServerTimeValue.PERMANENT) {
|
||||
return I18n.t('api_status_permanent_1');
|
||||
}
|
||||
const showDate = dayjs.unix(d).format('YYYY-MM-DD');
|
||||
return showDate;
|
||||
};
|
||||
|
||||
export const getStatus = (d: number) => {
|
||||
if (d === ServerTimeValue.PERMANENT) {
|
||||
return true;
|
||||
}
|
||||
const current = dayjs().unix();
|
||||
return d >= current;
|
||||
};
|
||||
Reference in New Issue
Block a user