| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 | import {  memo,  useCallback,  useState,} from 'react'import {  useStoreApi,} from 'reactflow'import { RiCloseLine } from '@remixicon/react'import { useTranslation } from 'react-i18next'import { useStore } from '@/app/components/workflow/store'import VariableTrigger from '@/app/components/workflow/panel/env-panel/variable-trigger'import EnvItem from '@/app/components/workflow/panel/env-panel/env-item'import type {  EnvironmentVariable,} from '@/app/components/workflow/types'import { findUsedVarNodes, updateNodeVars } from '@/app/components/workflow/nodes/_base/components/variable/utils'import RemoveEffectVarConfirm from '@/app/components/workflow/nodes/_base/components/remove-effect-var-confirm'import cn from '@/utils/classnames'import { useNodesSyncDraft } from '@/app/components/workflow/hooks/use-nodes-sync-draft'const EnvPanel = () => {  const { t } = useTranslation()  const store = useStoreApi()  const setShowEnvPanel = useStore(s => s.setShowEnvPanel)  const envList = useStore(s => s.environmentVariables) as EnvironmentVariable[]  const envSecrets = useStore(s => s.envSecrets)  const updateEnvList = useStore(s => s.setEnvironmentVariables)  const setEnvSecrets = useStore(s => s.setEnvSecrets)  const { doSyncWorkflowDraft } = useNodesSyncDraft()  const [showVariableModal, setShowVariableModal] = useState(false)  const [currentVar, setCurrentVar] = useState<EnvironmentVariable>()  const [showRemoveVarConfirm, setShowRemoveConfirm] = useState(false)  const [cacheForDelete, setCacheForDelete] = useState<EnvironmentVariable>()  const formatSecret = (s: string) => {    return s.length > 8 ? `${s.slice(0, 6)}************${s.slice(-2)}` : '********************'  }  const getEffectedNodes = useCallback((env: EnvironmentVariable) => {    const { getNodes } = store.getState()    const allNodes = getNodes()    return findUsedVarNodes(      ['env', env.name],      allNodes,    )  }, [store])  const removeUsedVarInNodes = useCallback((env: EnvironmentVariable) => {    const { getNodes, setNodes } = store.getState()    const effectedNodes = getEffectedNodes(env)    const newNodes = getNodes().map((node) => {      if (effectedNodes.find(n => n.id === node.id))        return updateNodeVars(node, ['env', env.name], [])      return node    })    setNodes(newNodes)  }, [getEffectedNodes, store])  const handleEdit = (env: EnvironmentVariable) => {    setCurrentVar(env)    setShowVariableModal(true)  }  const handleDelete = useCallback((env: EnvironmentVariable) => {    removeUsedVarInNodes(env)    updateEnvList(envList.filter(e => e.id !== env.id))    setCacheForDelete(undefined)    setShowRemoveConfirm(false)    doSyncWorkflowDraft()    if (env.value_type === 'secret') {      const newMap = { ...envSecrets }      delete newMap[env.id]      setEnvSecrets(newMap)    }  }, [doSyncWorkflowDraft, envList, envSecrets, removeUsedVarInNodes, setEnvSecrets, updateEnvList])  const deleteCheck = useCallback((env: EnvironmentVariable) => {    const effectedNodes = getEffectedNodes(env)    if (effectedNodes.length > 0) {      setCacheForDelete(env)      setShowRemoveConfirm(true)    }    else {      handleDelete(env)    }  }, [getEffectedNodes, handleDelete])  const handleSave = useCallback(async (env: EnvironmentVariable) => {    // add env    let newEnv = env    if (!currentVar) {      if (env.value_type === 'secret') {        setEnvSecrets({          ...envSecrets,          [env.id]: formatSecret(env.value),        })      }      const newList = [env, ...envList]      updateEnvList(newList)      await doSyncWorkflowDraft()      updateEnvList(newList.map(e => (e.id === env.id && env.value_type === 'secret') ? { ...e, value: '[__HIDDEN__]' } : e))      return    }    else if (currentVar.value_type === 'secret') {      if (env.value_type === 'secret') {        if (envSecrets[currentVar.id] !== env.value) {          newEnv = env          setEnvSecrets({            ...envSecrets,            [env.id]: formatSecret(env.value),          })        }        else {          newEnv = { ...env, value: '[__HIDDEN__]' }        }      }    }    else {      if (env.value_type === 'secret') {        newEnv = env        setEnvSecrets({          ...envSecrets,          [env.id]: formatSecret(env.value),        })      }    }    const newList = envList.map(e => e.id === currentVar.id ? newEnv : e)    updateEnvList(newList)    // side effects of rename env    if (currentVar.name !== env.name) {      const { getNodes, setNodes } = store.getState()      const effectedNodes = getEffectedNodes(currentVar)      const newNodes = getNodes().map((node) => {        if (effectedNodes.find(n => n.id === node.id))          return updateNodeVars(node, ['env', currentVar.name], ['env', env.name])        return node      })      setNodes(newNodes)    }    await doSyncWorkflowDraft()    updateEnvList(newList.map(e => (e.id === env.id && env.value_type === 'secret') ? { ...e, value: '[__HIDDEN__]' } : e))  }, [currentVar, doSyncWorkflowDraft, envList, envSecrets, getEffectedNodes, setEnvSecrets, store, updateEnvList])  return (    <div      className={cn(        'relative flex flex-col w-[420px] bg-components-panel-bg-alt rounded-l-2xl h-full border border-components-panel-border',      )}    >      <div className='shrink-0 flex items-center justify-between p-4 pb-0 text-text-primary system-xl-semibold'>        {t('workflow.env.envPanelTitle')}        <div className='flex items-center'>          <div            className='flex items-center justify-center w-6 h-6 cursor-pointer'            onClick={() => setShowEnvPanel(false)}          >            <RiCloseLine className='w-4 h-4 text-text-tertiary' />          </div>        </div>      </div>      <div className='shrink-0 py-1 px-4 system-sm-regular text-text-tertiary'>{t('workflow.env.envDescription')}</div>      <div className='shrink-0 px-4 pt-2 pb-3'>        <VariableTrigger          open={showVariableModal}          setOpen={setShowVariableModal}          env={currentVar}          onSave={handleSave}          onClose={() => setCurrentVar(undefined)}        />      </div>      <div className='grow px-4 rounded-b-2xl overflow-y-auto'>        {envList.map(env => (          <EnvItem            key={env.id}            env={env}            onEdit={handleEdit}            onDelete={deleteCheck}          />        ))}      </div>      <RemoveEffectVarConfirm        isShow={showRemoveVarConfirm}        onCancel={() => setShowRemoveConfirm(false)}        onConfirm={() => cacheForDelete && handleDelete(cacheForDelete)}      />    </div>  )}export default memo(EnvPanel)
 |