coze-studio/frontend/packages/data/memory/variables/src/variables-config.tsx

192 lines
5.8 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.
*/
import React, { useEffect, useRef } from 'react';
import { useShallow } from 'zustand/react/shallow';
import cls from 'classnames';
import { IllustrationNoContent } from '@douyinfe/semi-illustrations';
import { I18n } from '@coze-arch/i18n';
import { VariableChannel } from '@coze-arch/bot-api/memory';
import { IconCozCross } from '@coze-arch/coze-design/icons';
import {
Button,
Empty,
Form,
type FormApi,
IconButton,
Spin,
} from '@coze-arch/coze-design';
import { useVariableGroupsStore, type Variable } from '@/store';
import { useLeaveWarning } from '@/hooks/use-case/use-leave-waring';
import { useInit } from '@/hooks/life-cycle/use-init';
import { useDestory } from '@/hooks/life-cycle/use-destory';
import { VariableContext } from '@/context';
import { VariableGroup as VariableGroupComponent } from '@/components/variable-group';
import {
checkProjectID,
submit,
} from './service/use-case-service/submit-service';
import { useChangeWarning } from './hooks/use-case/use-change-warning';
export interface VariableConfigProps {
projectID: string;
version?: string;
}
export const VariablesConfig = ({
projectID,
version,
}: VariableConfigProps) => {
const formApiRef = useRef<FormApi | null>(null);
const { loading } = useInit(projectID, version);
useDestory();
const { setHasUnsavedChanges, hasUnsavedChanges } = useLeaveWarning();
const { variableGroups, canEdit, saveHistory, getAllVariables } =
useVariableGroupsStore(
useShallow(state => ({
variableGroups: state.variableGroups,
canEdit: state.canEdit,
saveHistory: state.saveHistory,
getAllVariables: state.getAllVariables,
})),
);
const { isShowBanner, showBanner, hideBanner, hideBannerForever } =
useChangeWarning();
const isEmpty = !variableGroups.length;
const onVariableChange = (changeValue: Variable) => {
setHasUnsavedChanges(true);
if (changeValue.meta?.isHistory) {
showBanner();
}
};
const handleSubmit = async () => {
if (!checkProjectID(projectID)) {
return;
}
const formApi = formApiRef.current;
if (!formApi) {
return;
}
const isValid = await formApi.validate();
if (!isValid) {
return;
}
saveHistory();
await submit(projectID);
setHasUnsavedChanges(false);
};
const initValues = getAllVariables().reduce((acc, curr) => {
acc[curr.variableId] = { name: curr.name };
return acc;
}, {});
useEffect(() => {
if (loading) {
return;
}
formApiRef.current?.setValues(initValues);
}, [loading, initValues]);
return (
<VariableContext.Provider
value={{
variablePageCanEdit: canEdit,
groups: variableGroups,
}}
>
<div className="p-4 pb-[72px]">
{loading ? (
<div className="w-full h-full flex justify-center items-center">
<Spin />
</div>
) : isEmpty ? (
<div className="w-full h-full flex items-center justify-center">
<Empty
image={<IllustrationNoContent className="w-[140px] h-[140px]" />}
title={I18n.t('card_builder_varpanel_var_empty')}
/>
</div>
) : (
<>
{isShowBanner ? (
<div className="h-[36px] flex items-center justify-center coz-mg-hglt coz-fg-primary text-sm mb-4 mt-[-16px] mx-[-16px]">
<div className="flex items-center ml-auto">
{I18n.t('variable_config_change_banner')}
</div>
<div className="flex items-center ml-auto cursor-pointer">
<div
className="coz-fg-secondary text-xs"
onClick={hideBannerForever}
>
{I18n.t('do_not_remind_again')}
</div>
<IconButton
className="ml-2 !bg-transparent"
onClick={hideBanner}
icon={<IconCozCross />}
/>
</div>
</div>
) : null}
<Form<typeof initValues>
getFormApi={formApi => {
formApiRef.current = formApi;
}}
showValidateIcon={false}
autoScrollToError
initValues={initValues}
>
<div className="flex flex-col gap-2">
{variableGroups.map(item => (
<VariableGroupComponent
readonly={!canEdit || item.isReadOnly}
groupInfo={item}
onVariableChange={onVariableChange}
validateExistKeyword={item.channel === VariableChannel.APP}
/>
))}
</div>
</Form>
<div
className={cls(
'flex items-center justify-end',
'fixed bottom-[1px] right-[1px] left-[1px] pb-4 pt-6',
'bg-white mr-4 px-4',
)}
>
<Button
onClick={handleSubmit}
disabled={!canEdit || !hasUnsavedChanges}
>
{I18n.t('edit_variables_modal_ok_text')}
</Button>
</div>
</>
)}
</div>
</VariableContext.Provider>
);
};