Files
coze-studio/frontend/packages/agent-ide/bot-plugin/mock-set/src/component/mock-data-create-card.tsx

280 lines
7.4 KiB
TypeScript

/*
* 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.
*/
/* eslint-disable @coze-arch/max-line-per-function */
import { useParams } from 'react-router-dom';
import { useEffect, useRef, useState } from 'react';
import { I18n } from '@coze-arch/i18n';
import { type DynamicParams } from '@coze-arch/bot-typings/teamspace';
import {
type PluginMockSetCommonParams,
type PluginMockDataGenerateMode,
} from '@coze-arch/bot-tea';
import { useSpaceStore } from '@coze-arch/bot-studio-store';
import {
Space,
Toast,
UIButton,
UIModal,
Typography,
Divider,
} from '@coze-arch/bot-semi';
import { SpaceType } from '@coze-arch/bot-api/playground_api';
import { type mockset, type infra } from '@coze-arch/bot-api/debugger_api';
import {
calcStringSize,
type MockDataInfo,
MAX_SUBMIT_LENGTH,
getEnvironment,
} from '@coze-studio/mockset-shared';
import {
MocksetEditor,
type EditorAreaActions,
} from '@coze-studio/mockset-editor-adapter';
import {
PRE_DEFINED_NO_EMPTY_KEY,
useTransSchema,
} from '../hook/use-trans-schema';
import { useSaveMockData } from '../hook/use-save-mock-data';
import s from './index.module.less';
export enum CreationMode {
/** pop-up window */
MODAL = 'modal',
/** embed page */
CARD = 'card',
}
interface MockDataCreateCardProps {
mode: CreationMode;
mockInfo?: MockDataInfo;
// Effective when modal mode
visible?: boolean;
// Effective when modal mode
onCancel?: () => void;
onSuccess: (data?: mockset.MockRule[]) => void;
bizCtx: infra.BizCtx;
forceGenerate?: {
mode: PluginMockDataGenerateMode;
count: number;
};
}
/** Create or edit mock data - */
export function MockDataCreateCard({
mode,
mockInfo,
visible,
onCancel,
onSuccess,
bizCtx,
forceGenerate,
}: MockDataCreateCardProps) {
const { schema } = mockInfo || {};
const editorsRef = useRef<EditorAreaActions>(null);
const [disableSubmit, setDisableSubmit] = useState(false);
const [disableSubmitWhenGenerating, setDisableSubmitWhenGenerating] =
useState(false);
const { testValueValid, formattedResultExample: initialExample } =
useTransSchema(schema);
const { mock_set_id, tool_id } = useParams<DynamicParams>();
// Space information
const spaceType = useSpaceStore(store => store.space.space_type);
const isPersonal = spaceType === SpaceType.Personal;
const basicParams: PluginMockSetCommonParams = {
environment: getEnvironment(),
workspace_id: bizCtx.bizSpaceID || '',
workspace_type: isPersonal ? 'personal_workspace' : 'team_workspace',
tool_id: tool_id || '',
mock_set_id: mock_set_id || '',
};
const { save, loading } = useSaveMockData({
mockSetId: mock_set_id,
basicParams,
bizCtx,
onSuccess,
});
const confirmHandler = () => {
const values = editorsRef.current?.getValue();
if (!values) {
return;
}
for (const value of values) {
if (!value) {
Toast.error('no data');
return;
}
if (calcStringSize(value) > MAX_SUBMIT_LENGTH) {
Toast.error({
content: I18n.t('mockset_toast_data_size_limit'),
showClose: false,
});
return;
}
if (!testValueValid(value)) {
Toast.error({
content: I18n.t('mockdata_field_empty', {
fieldName: PRE_DEFINED_NO_EMPTY_KEY,
}),
showClose: false,
});
return;
}
}
const mockDataId = String(mockInfo?.mock?.id || 0);
save(values, mockDataId);
};
const validateHandler = (isValid: boolean[]) => {
setDisableSubmit(isValid.some(v => !v));
};
useEffect(() => {
// @ts-expect-error -- linter-disable-autofix
const unloadHandler = e => {
const info = I18n.t('mockset_tip_data_will_lose');
e.preventDefault();
e.returnValue = info;
return info;
};
if (
(mode === CreationMode.MODAL && visible) ||
mode === CreationMode.CARD
) {
window.addEventListener('beforeunload', unloadHandler);
}
return () => {
window.removeEventListener('beforeunload', unloadHandler);
};
}, [mode, visible]);
useEffect(() => {
if (forceGenerate) {
editorsRef.current?.forceStartGenerate?.(
forceGenerate.mode,
forceGenerate.count,
);
}
}, []);
return mode === CreationMode.MODAL ? (
<UIModal
visible={visible}
title={
mockInfo?.mock ? I18n.t('edit_mock_data') : I18n.t('add_mock_data')
}
className={s['mock-creation-modal']}
keepDOM={false}
footer={
<>
<span className="mr-[8px]">{I18n.t('mockset_save_description')}</span>
<Divider layout="vertical" margin="0px" />
<UIButton type={'tertiary'} key="Cancel" onClick={onCancel}>
{I18n.t('cancel')}
</UIButton>
<UIButton
type={'primary'}
theme={'solid'}
key="Confirm"
onClick={confirmHandler}
loading={loading}
disabled={disableSubmit || disableSubmitWhenGenerating}
>
{I18n.t('confirm')}
</UIButton>
</>
}
width={1000}
maskClosable={false}
onCancel={onCancel}
>
<MocksetEditor
className={s['mock-creation-modal-editor']}
mockInfo={{
mergedResultExample: initialExample,
...mockInfo,
}}
readOnly={false}
ref={editorsRef}
onValidate={validateHandler}
environment={{
spaceId: bizCtx.bizSpaceID,
mockSetId: mock_set_id,
basicParams,
}}
isCreateScene={!mockInfo?.mock}
onGenerationStatusChange={isGenerating =>
setDisableSubmitWhenGenerating(isGenerating)
}
/>
</UIModal>
) : (
<div className={s['mock-creation-card']}>
<div className={s['mock-creation-card-editor']}>
<MocksetEditor
mockInfo={{
mergedResultExample: initialExample,
...mockInfo,
}}
ref={editorsRef}
onValidate={validateHandler}
environment={{
spaceId: bizCtx.bizSpaceID,
mockSetId: mock_set_id,
basicParams,
}}
isCreateScene={!mockInfo?.mock}
onGenerationStatusChange={isGenerating =>
setDisableSubmitWhenGenerating(isGenerating)
}
/>
</div>
<div className={s['mock-creation-card-operation']}>
<Space>
<Typography.Text>
{I18n.t('mockset_save_description')}
</Typography.Text>
<Divider layout="vertical" margin="0px" />
<UIButton
type={'primary'}
theme={'solid'}
onClick={confirmHandler}
loading={loading}
disabled={disableSubmit || disableSubmitWhenGenerating}
>
{I18n.t('mockset_save')}
</UIButton>
</Space>
</div>
</div>
);
}