index.tsx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useCallback, useState } from 'react'
  4. import { useTranslation } from 'react-i18next'
  5. import { useContext } from 'use-context-selector'
  6. import ModalFoot from '../modal-foot'
  7. import ConfigSelect from '../config-select'
  8. import ConfigString from '../config-string'
  9. import SelectTypeItem from '../select-type-item'
  10. import Field from './field'
  11. import Toast from '@/app/components/base/toast'
  12. import { checkKeys, getNewVarInWorkflow } from '@/utils/var'
  13. import ConfigContext from '@/context/debug-configuration'
  14. import type { InputVar, MoreInfo } from '@/app/components/workflow/types'
  15. import Modal from '@/app/components/base/modal'
  16. import Switch from '@/app/components/base/switch'
  17. import { ChangeType, InputVarType } from '@/app/components/workflow/types'
  18. const TEXT_MAX_LENGTH = 256
  19. const PARAGRAPH_MAX_LENGTH = 1032 * 32
  20. export type IConfigModalProps = {
  21. isCreate?: boolean
  22. payload?: InputVar
  23. isShow: boolean
  24. varKeys?: string[]
  25. onClose: () => void
  26. onConfirm: (newValue: InputVar, moreInfo?: MoreInfo) => void
  27. }
  28. const inputClassName = 'w-full px-3 text-sm leading-9 text-gray-900 border-0 rounded-lg grow h-9 bg-gray-100 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200'
  29. const ConfigModal: FC<IConfigModalProps> = ({
  30. isCreate,
  31. payload,
  32. isShow,
  33. onClose,
  34. onConfirm,
  35. }) => {
  36. const { modelConfig } = useContext(ConfigContext)
  37. const { t } = useTranslation()
  38. const [tempPayload, setTempPayload] = useState<InputVar>(payload || getNewVarInWorkflow('') as any)
  39. const { type, label, variable, options, max_length } = tempPayload
  40. const isStringInput = type === InputVarType.textInput || type === InputVarType.paragraph
  41. const handlePayloadChange = useCallback((key: string) => {
  42. return (value: any) => {
  43. if (key === 'variable') {
  44. const { isValid, errorKey, errorMessageKey } = checkKeys([value], true)
  45. if (!isValid) {
  46. Toast.notify({
  47. type: 'error',
  48. message: t(`appDebug.varKeyError.${errorMessageKey}`, { key: errorKey }),
  49. })
  50. return
  51. }
  52. }
  53. setTempPayload((prev) => {
  54. const newPayload = {
  55. ...prev,
  56. [key]: value,
  57. }
  58. return newPayload
  59. })
  60. }
  61. }, [t])
  62. const handleVarKeyBlur = useCallback((e: any) => {
  63. if (tempPayload.label)
  64. return
  65. setTempPayload((prev) => {
  66. return {
  67. ...prev,
  68. label: e.target.value,
  69. }
  70. })
  71. }, [tempPayload])
  72. const handleConfirm = () => {
  73. const moreInfo = tempPayload.variable === payload?.variable
  74. ? undefined
  75. : {
  76. type: ChangeType.changeVarName,
  77. payload: { beforeKey: payload?.variable || '', afterKey: tempPayload.variable },
  78. }
  79. if (!tempPayload.variable) {
  80. Toast.notify({ type: 'error', message: t('appDebug.variableConig.errorMsg.varNameRequired') })
  81. return
  82. }
  83. // TODO: check if key already exists. should the consider the edit case
  84. // if (varKeys.map(key => key?.trim()).includes(tempPayload.variable.trim())) {
  85. // Toast.notify({
  86. // type: 'error',
  87. // message: t('appDebug.varKeyError.keyAlreadyExists', { key: tempPayload.variable }),
  88. // })
  89. // return
  90. // }
  91. if (!tempPayload.label) {
  92. Toast.notify({ type: 'error', message: t('appDebug.variableConig.errorMsg.labelNameRequired') })
  93. return
  94. }
  95. if (isStringInput || type === InputVarType.number) {
  96. onConfirm(tempPayload, moreInfo)
  97. }
  98. else {
  99. if (options?.length === 0) {
  100. Toast.notify({ type: 'error', message: t('appDebug.variableConig.errorMsg.atLeastOneOption') })
  101. return
  102. }
  103. const obj: Record<string, boolean> = {}
  104. let hasRepeatedItem = false
  105. options?.forEach((o) => {
  106. if (obj[o]) {
  107. hasRepeatedItem = true
  108. return
  109. }
  110. obj[o] = true
  111. })
  112. if (hasRepeatedItem) {
  113. Toast.notify({ type: 'error', message: t('appDebug.variableConig.errorMsg.optionRepeat') })
  114. return
  115. }
  116. onConfirm(tempPayload, moreInfo)
  117. }
  118. }
  119. return (
  120. <Modal
  121. title={t(`appDebug.variableConig.${isCreate ? 'addModalTitle' : 'editModalTitle'}`)}
  122. isShow={isShow}
  123. onClose={onClose}
  124. wrapperClassName='!z-[100]'
  125. >
  126. <div className='mb-8'>
  127. <div className='space-y-2'>
  128. <Field title={t('appDebug.variableConig.fieldType')}>
  129. <div className='flex space-x-2'>
  130. <SelectTypeItem type={InputVarType.textInput} selected={type === InputVarType.textInput} onClick={() => handlePayloadChange('type')(InputVarType.textInput)} />
  131. <SelectTypeItem type={InputVarType.paragraph} selected={type === InputVarType.paragraph} onClick={() => handlePayloadChange('type')(InputVarType.paragraph)} />
  132. <SelectTypeItem type={InputVarType.select} selected={type === InputVarType.select} onClick={() => handlePayloadChange('type')(InputVarType.select)} />
  133. <SelectTypeItem type={InputVarType.number} selected={type === InputVarType.number} onClick={() => handlePayloadChange('type')(InputVarType.number)} />
  134. </div>
  135. </Field>
  136. <Field title={t('appDebug.variableConig.varName')}>
  137. <input
  138. type='text'
  139. className={inputClassName}
  140. value={variable}
  141. onChange={e => handlePayloadChange('variable')(e.target.value)}
  142. onBlur={handleVarKeyBlur}
  143. placeholder={t('appDebug.variableConig.inputPlaceholder')!}
  144. />
  145. </Field>
  146. <Field title={t('appDebug.variableConig.labelName')}>
  147. <input
  148. type='text'
  149. className={inputClassName}
  150. value={label as string}
  151. onChange={e => handlePayloadChange('label')(e.target.value)}
  152. placeholder={t('appDebug.variableConig.inputPlaceholder')!}
  153. />
  154. </Field>
  155. {isStringInput && (
  156. <Field title={t('appDebug.variableConig.maxLength')}>
  157. <ConfigString maxLength={type === InputVarType.textInput ? TEXT_MAX_LENGTH : PARAGRAPH_MAX_LENGTH} modelId={modelConfig.model_id} value={max_length} onChange={handlePayloadChange('max_length')} />
  158. </Field>
  159. )}
  160. {type === InputVarType.select && (
  161. <Field title={t('appDebug.variableConig.options')}>
  162. <ConfigSelect options={options || []} onChange={handlePayloadChange('options')} />
  163. </Field>
  164. )}
  165. <Field title={t('appDebug.variableConig.required')}>
  166. <Switch defaultValue={tempPayload.required} onChange={handlePayloadChange('required')} />
  167. </Field>
  168. </div>
  169. </div>
  170. <ModalFoot
  171. onConfirm={handleConfirm}
  172. onCancel={onClose}
  173. />
  174. </Modal>
  175. )
  176. }
  177. export default React.memo(ConfigModal)