import type { SelectorParam } from 'i18next' import type { ReactNode } from 'react' import type { ChatVarType } from '../type' import type { ConversationVariable } from '@/app/components/workflow/types' import { checkKeys } from '@/utils/var' import { ChatVarType as ChatVarTypeEnum } from '../type' import { arrayBoolPlaceholder, arrayNumberPlaceholder, arrayObjectPlaceholder, arrayStringPlaceholder, objectPlaceholder, } from '../utils' import { DEFAULT_OBJECT_VALUE } from './object-value-item' export type ObjectValueItem = { key: string type: ChatVarType value: string | number | undefined } export type ToastPayload = { type?: 'success' | 'error' | 'warning' | 'info' size?: 'md' | 'sm' duration?: number message: string children?: ReactNode onClose?: () => void className?: string customComponent?: ReactNode } export const MAX_DESCRIPTION_LENGTH = 255 export type ChatVariableTranslator = ( selector: SelectorParam, options: { ns: Namespace } & Record, ) => string type VariableNameErrorKey = Exclude['errorMessageKey'], ''> const variableNameErrorSelectors: Record> = { canNoBeEmpty: ($) => $['varKeyError.canNoBeEmpty'], keyAlreadyExists: ($) => $['varKeyError.keyAlreadyExists'], notStartWithNumber: ($) => $['varKeyError.notStartWithNumber'], notValid: ($) => $['varKeyError.notValid'], tooLong: ($) => $['varKeyError.tooLong'], } export const typeList = [ ChatVarTypeEnum.String, ChatVarTypeEnum.Number, ChatVarTypeEnum.Boolean, ChatVarTypeEnum.Object, ChatVarTypeEnum.ArrayString, ChatVarTypeEnum.ArrayNumber, ChatVarTypeEnum.ArrayBoolean, ChatVarTypeEnum.ArrayObject, ] export const getEditorMinHeight = (type: ChatVarType) => type === ChatVarTypeEnum.ArrayObject ? '240px' : '120px' export const getPlaceholderByType = (type: ChatVarType) => { if (type === ChatVarTypeEnum.ArrayString) return arrayStringPlaceholder if (type === ChatVarTypeEnum.ArrayNumber) return arrayNumberPlaceholder if (type === ChatVarTypeEnum.ArrayObject) return arrayObjectPlaceholder if (type === ChatVarTypeEnum.ArrayBoolean) return arrayBoolPlaceholder return objectPlaceholder } export const buildObjectValueItems = (chatVar?: ConversationVariable): ObjectValueItem[] => { if (!chatVar || !chatVar.value || Object.keys(chatVar.value).length === 0) return [DEFAULT_OBJECT_VALUE] return Object.keys(chatVar.value).map((key) => { const itemValue = chatVar.value[key] return { key, type: typeof itemValue === 'string' ? ChatVarTypeEnum.String : ChatVarTypeEnum.Number, value: itemValue, } }) } export const formatObjectValueFromList = (list: ObjectValueItem[]) => { return list.reduce>((acc, curr) => { if (curr.key) acc[curr.key] = curr.value === '' || curr.value === undefined ? null : curr.value return acc }, {}) } export const formatChatVariableValue = ({ editInJSON, objectValue, type, value, }: { editInJSON: boolean objectValue: ObjectValueItem[] type: ChatVarType value: unknown }) => { const compactArrayValue = (items: unknown[]) => items.filter((item) => item !== null && item !== undefined && item !== '') switch (type) { case ChatVarTypeEnum.String: return value || '' case ChatVarTypeEnum.Number: return value || 0 case ChatVarTypeEnum.Boolean: return value === undefined ? true : value case ChatVarTypeEnum.Object: return editInJSON ? value : formatObjectValueFromList(objectValue) case ChatVarTypeEnum.ArrayString: case ChatVarTypeEnum.ArrayNumber: case ChatVarTypeEnum.ArrayObject: return Array.isArray(value) ? compactArrayValue(value) : [] case ChatVarTypeEnum.ArrayBoolean: return value || [] } } export const validateVariableName = ({ name, notify, t, }: { name: string notify: (props: ToastPayload) => void t: ChatVariableTranslator }) => { const { isValid, errorMessageKey } = checkKeys([name], false) if (!isValid) { notify({ type: 'error', message: t(variableNameErrorSelectors[errorMessageKey], { ns: 'appDebug', key: t(($) => $['env.modal.name'], { ns: 'workflow' }), }), }) return false } return true } export const getTypeChangeState = (nextType: ChatVarType) => { return { editInJSON: nextType === ChatVarTypeEnum.ArrayObject, editorContent: undefined as string | undefined, objectValue: nextType === ChatVarTypeEnum.Object ? [DEFAULT_OBJECT_VALUE] : undefined, value: nextType === ChatVarTypeEnum.Boolean ? false : nextType === ChatVarTypeEnum.ArrayBoolean ? [false] : undefined, } } export const parseEditorContent = ({ content, type }: { content: string; type: ChatVarType }) => { const parsed = JSON.parse(content) if (type !== ChatVarTypeEnum.ArrayBoolean) return parsed if (!Array.isArray(parsed)) throw new TypeError('ArrayBoolean editor content must be a JSON array') return parsed .map((item: string | boolean) => { if (item === 'True' || item === 'true' || item === true) return true if (item === 'False' || item === 'false' || item === false) return false return undefined }) .filter((item?: boolean) => item !== undefined) } export type EditorToggleLabelKey = | 'chatVariable.modal.editInForm' | 'chatVariable.modal.editInJSON' | 'chatVariable.modal.oneByOne' export const getEditorToggleLabelKey = ( type: ChatVarType, editInJSON: boolean, ): EditorToggleLabelKey => { if (type === ChatVarTypeEnum.Object) return editInJSON ? 'chatVariable.modal.editInForm' : 'chatVariable.modal.editInJSON' return editInJSON ? 'chatVariable.modal.oneByOne' : 'chatVariable.modal.editInJSON' }