index.tsx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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 = useAppStore(state => state.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. handleSyncWorkflowDraft(true)
  71. }, [handleSyncWorkflowDraft, workflowStore])
  72. const onPublish = useCallback(async () => {
  73. if (handleCheckBeforePublish()) {
  74. const res = await publishWorkflow(`/apps/${appID}/workflows/publish`)
  75. if (res) {
  76. notify({ type: 'success', message: t('common.api.actionSuccess') })
  77. workflowStore.getState().setPublishedAt(res.created_at)
  78. }
  79. }
  80. else {
  81. throw new Error('Checklist failed')
  82. }
  83. }, [appID, handleCheckBeforePublish, notify, t, workflowStore])
  84. const onStartRestoring = useCallback(() => {
  85. workflowStore.setState({ isRestoring: true })
  86. handleBackupDraft()
  87. handleRestoreFromPublishedWorkflow()
  88. }, [handleBackupDraft, handleRestoreFromPublishedWorkflow, workflowStore])
  89. const onPublisherToggle = useCallback((state: boolean) => {
  90. if (state)
  91. handleSyncWorkflowDraft(true)
  92. }, [handleSyncWorkflowDraft])
  93. return (
  94. <div
  95. className='absolute top-0 left-0 z-10 flex items-center justify-between w-full px-3 h-14'
  96. style={{
  97. background: 'linear-gradient(180deg, #F9FAFB 0%, rgba(249, 250, 251, 0.00) 100%)',
  98. }}
  99. >
  100. <div>
  101. {
  102. appSidebarExpand === 'collapse' && (
  103. <div className='text-xs font-medium text-gray-700'>{appDetail?.name}</div>
  104. )
  105. }
  106. {
  107. !nodesReadOnly && !isRestoring && <EditingTitle />
  108. }
  109. {
  110. nodesReadOnly && !isRestoring && <RunningTitle />
  111. }
  112. {
  113. isRestoring && <RestoringTitle />
  114. }
  115. </div>
  116. {
  117. !isRestoring && (
  118. <div className='flex items-center'>
  119. {
  120. nodesReadOnly && (
  121. <Button
  122. className={`
  123. mr-2 px-3 py-0 h-8 bg-white text-[13px] font-medium text-primary-600
  124. border-[0.5px] border-gray-200 shadow-xs
  125. `}
  126. onClick={handleGoBackToEdit}
  127. >
  128. <ArrowNarrowLeft className='w-4 h-4 mr-1' />
  129. {t('workflow.common.goBackToEdit')}
  130. </Button>
  131. )
  132. }
  133. <RunAndHistory />
  134. <div className='mx-2 w-[1px] h-3.5 bg-gray-200'></div>
  135. <Button
  136. className={`
  137. mr-2 px-3 py-0 h-8 bg-white text-[13px] font-medium text-gray-700
  138. border-[0.5px] border-gray-200 shadow-xs
  139. ${nodesReadOnly && !isRestoring && 'opacity-50 !cursor-not-allowed'}
  140. `}
  141. onClick={handleShowFeatures}
  142. >
  143. <Grid01 className='w-4 h-4 mr-1 text-gray-500' />
  144. {t('workflow.common.features')}
  145. </Button>
  146. <AppPublisher
  147. {...{
  148. publishedAt,
  149. draftUpdatedAt,
  150. disabled: Boolean(getNodesReadOnly()),
  151. onPublish,
  152. onRestore: onStartRestoring,
  153. onToggle: onPublisherToggle,
  154. crossAxisOffset: 53,
  155. }}
  156. />
  157. {
  158. !nodesReadOnly && (
  159. <>
  160. <div className='mx-2 w-[1px] h-3.5 bg-gray-200'></div>
  161. <Checklist />
  162. </>
  163. )
  164. }
  165. </div>
  166. )
  167. }
  168. {
  169. isRestoring && (
  170. <div className='flex items-center'>
  171. <Button
  172. className={`
  173. px-3 py-0 h-8 bg-white text-[13px] font-medium text-gray-700
  174. border-[0.5px] border-gray-200 shadow-xs
  175. `}
  176. onClick={handleShowFeatures}
  177. >
  178. <Grid01 className='w-4 h-4 mr-1 text-gray-500' />
  179. {t('workflow.common.features')}
  180. </Button>
  181. <div className='mx-2 w-[1px] h-3.5 bg-gray-200'></div>
  182. <Button
  183. 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'
  184. onClick={handleCancelRestore}
  185. >
  186. {t('common.operation.cancel')}
  187. </Button>
  188. <Button
  189. className='px-3 py-0 h-8 text-[13px] font-medium shadow-xs'
  190. onClick={handleRestore}
  191. type='primary'
  192. >
  193. {t('workflow.common.restore')}
  194. </Button>
  195. </div>
  196. )
  197. }
  198. </div>
  199. )
  200. }
  201. export default memo(Header)