index.tsx 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. import type { FC } from 'react'
  2. import {
  3. memo,
  4. useCallback,
  5. } from 'react'
  6. import { useTranslation } from 'react-i18next'
  7. import { useContext } from 'use-context-selector'
  8. import {
  9. useStore,
  10. useWorkflowStore,
  11. } from '../store'
  12. import {
  13. useChecklistBeforePublish,
  14. useNodesReadOnly,
  15. useNodesSyncDraft,
  16. useWorkflowMode,
  17. useWorkflowRun,
  18. } from '../hooks'
  19. import AppPublisher from '../../app/app-publisher'
  20. import { ToastContext } from '../../base/toast'
  21. import RunAndHistory from './run-and-history'
  22. import EditingTitle from './editing-title'
  23. import RunningTitle from './running-title'
  24. import RestoringTitle from './restoring-title'
  25. import ViewHistory from './view-history'
  26. import Checklist from './checklist'
  27. import { Grid01 } from '@/app/components/base/icons/src/vender/line/layout'
  28. import Button from '@/app/components/base/button'
  29. import { useStore as useAppStore } from '@/app/components/app/store'
  30. import { publishWorkflow } from '@/service/workflow'
  31. import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows'
  32. const Header: FC = () => {
  33. const { t } = useTranslation()
  34. const workflowStore = useWorkflowStore()
  35. const appDetail = useAppStore(s => s.appDetail)
  36. const appSidebarExpand = useAppStore(s => s.appSidebarExpand)
  37. const appID = appDetail?.id
  38. const {
  39. nodesReadOnly,
  40. getNodesReadOnly,
  41. } = useNodesReadOnly()
  42. const publishedAt = useStore(s => s.publishedAt)
  43. const draftUpdatedAt = useStore(s => s.draftUpdatedAt)
  44. const {
  45. handleLoadBackupDraft,
  46. handleBackupDraft,
  47. handleRestoreFromPublishedWorkflow,
  48. } = useWorkflowRun()
  49. const { handleCheckBeforePublish } = useChecklistBeforePublish()
  50. const { handleSyncWorkflowDraft } = useNodesSyncDraft()
  51. const { notify } = useContext(ToastContext)
  52. const {
  53. normal,
  54. restoring,
  55. viewHistory,
  56. } = useWorkflowMode()
  57. const handleShowFeatures = useCallback(() => {
  58. const {
  59. isRestoring,
  60. setShowFeaturesPanel,
  61. } = workflowStore.getState()
  62. if (getNodesReadOnly() && !isRestoring)
  63. return
  64. setShowFeaturesPanel(true)
  65. }, [workflowStore, getNodesReadOnly])
  66. const handleCancelRestore = useCallback(() => {
  67. handleLoadBackupDraft()
  68. workflowStore.setState({ isRestoring: false })
  69. }, [workflowStore, handleLoadBackupDraft])
  70. const handleRestore = useCallback(() => {
  71. workflowStore.setState({ isRestoring: false })
  72. workflowStore.setState({ backupDraft: undefined })
  73. handleSyncWorkflowDraft(true)
  74. }, [handleSyncWorkflowDraft, workflowStore])
  75. const onPublish = useCallback(async () => {
  76. if (handleCheckBeforePublish()) {
  77. const res = await publishWorkflow(`/apps/${appID}/workflows/publish`)
  78. if (res) {
  79. notify({ type: 'success', message: t('common.api.actionSuccess') })
  80. workflowStore.getState().setPublishedAt(res.created_at)
  81. }
  82. }
  83. else {
  84. throw new Error('Checklist failed')
  85. }
  86. }, [appID, handleCheckBeforePublish, notify, t, workflowStore])
  87. const onStartRestoring = useCallback(() => {
  88. workflowStore.setState({ isRestoring: true })
  89. handleBackupDraft()
  90. handleRestoreFromPublishedWorkflow()
  91. }, [handleBackupDraft, handleRestoreFromPublishedWorkflow, workflowStore])
  92. const onPublisherToggle = useCallback((state: boolean) => {
  93. if (state)
  94. handleSyncWorkflowDraft(true)
  95. }, [handleSyncWorkflowDraft])
  96. const handleGoBackToEdit = useCallback(() => {
  97. handleLoadBackupDraft()
  98. workflowStore.setState({ historyWorkflowData: undefined })
  99. }, [workflowStore, handleLoadBackupDraft])
  100. return (
  101. <div
  102. className='absolute top-0 left-0 z-10 flex items-center justify-between w-full px-3 h-14'
  103. style={{
  104. background: 'linear-gradient(180deg, #F9FAFB 0%, rgba(249, 250, 251, 0.00) 100%)',
  105. }}
  106. >
  107. <div>
  108. {
  109. appSidebarExpand === 'collapse' && (
  110. <div className='text-xs font-medium text-gray-700'>{appDetail?.name}</div>
  111. )
  112. }
  113. {
  114. normal && <EditingTitle />
  115. }
  116. {
  117. viewHistory && <RunningTitle />
  118. }
  119. {
  120. restoring && <RestoringTitle />
  121. }
  122. </div>
  123. {
  124. normal && (
  125. <div className='flex items-center'>
  126. <RunAndHistory />
  127. <div className='mx-2 w-[1px] h-3.5 bg-gray-200'></div>
  128. <Button
  129. className={`
  130. mr-2 px-3 py-0 h-8 bg-white text-[13px] font-medium text-gray-700
  131. border-[0.5px] border-gray-200 shadow-xs
  132. ${nodesReadOnly && 'opacity-50 !cursor-not-allowed'}
  133. `}
  134. onClick={handleShowFeatures}
  135. >
  136. <Grid01 className='w-4 h-4 mr-1 text-gray-500' />
  137. {t('workflow.common.features')}
  138. </Button>
  139. <AppPublisher
  140. {...{
  141. publishedAt,
  142. draftUpdatedAt,
  143. disabled: Boolean(getNodesReadOnly()),
  144. onPublish,
  145. onRestore: onStartRestoring,
  146. onToggle: onPublisherToggle,
  147. crossAxisOffset: 53,
  148. }}
  149. />
  150. <div className='mx-2 w-[1px] h-3.5 bg-gray-200'></div>
  151. <Checklist disabled={nodesReadOnly} />
  152. </div>
  153. )
  154. }
  155. {
  156. viewHistory && (
  157. <div className='flex items-center'>
  158. <ViewHistory withText />
  159. <div className='mx-2 w-[1px] h-3.5 bg-gray-200'></div>
  160. <Button
  161. type='primary'
  162. className={`
  163. mr-2 px-3 py-0 h-8 text-[13px] font-medium
  164. border-[0.5px] border-gray-200 shadow-xs
  165. `}
  166. onClick={handleGoBackToEdit}
  167. >
  168. <ArrowNarrowLeft className='w-4 h-4 mr-1' />
  169. {t('workflow.common.goBackToEdit')}
  170. </Button>
  171. </div>
  172. )
  173. }
  174. {
  175. restoring && (
  176. <div className='flex items-center'>
  177. <Button
  178. className={`
  179. px-3 py-0 h-8 bg-white text-[13px] font-medium text-gray-700
  180. border-[0.5px] border-gray-200 shadow-xs
  181. `}
  182. onClick={handleShowFeatures}
  183. >
  184. <Grid01 className='w-4 h-4 mr-1 text-gray-500' />
  185. {t('workflow.common.features')}
  186. </Button>
  187. <div className='mx-2 w-[1px] h-3.5 bg-gray-200'></div>
  188. <Button
  189. className='mr-2 px-3 py-0 h-8 bg-white text-[13px] text-gray-700 font-medium border-[0.5px] border-gray-200 shadow-xs'
  190. onClick={handleCancelRestore}
  191. >
  192. {t('common.operation.cancel')}
  193. </Button>
  194. <Button
  195. className='px-3 py-0 h-8 text-[13px] font-medium shadow-xs'
  196. onClick={handleRestore}
  197. type='primary'
  198. >
  199. {t('workflow.common.restore')}
  200. </Button>
  201. </div>
  202. )
  203. }
  204. </div>
  205. )
  206. }
  207. export default memo(Header)