index.tsx 6.8 KB

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