modal-context.tsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. 'use client'
  2. import type { Dispatch, SetStateAction } from 'react'
  3. import { useCallback, useState } from 'react'
  4. import { createContext, useContext, useContextSelector } from 'use-context-selector'
  5. import { useRouter, useSearchParams } from 'next/navigation'
  6. import AccountSetting from '@/app/components/header/account-setting'
  7. import ApiBasedExtensionModal from '@/app/components/header/account-setting/api-based-extension-page/modal'
  8. import ModerationSettingModal from '@/app/components/base/features/new-feature-panel/moderation/moderation-setting-modal'
  9. import ExternalDataToolModal from '@/app/components/app/configuration/tools/external-data-tool-modal'
  10. import AnnotationFullModal from '@/app/components/billing/annotation-full/modal'
  11. import ModelModal from '@/app/components/header/account-setting/model-provider-page/model-modal'
  12. import ExternalAPIModal from '@/app/components/datasets/external-api/external-api-modal'
  13. import type {
  14. ConfigurationMethodEnum,
  15. CustomConfigurationModelFixedFields,
  16. ModelLoadBalancingConfigEntry,
  17. ModelProvider,
  18. } from '@/app/components/header/account-setting/model-provider-page/declarations'
  19. import {
  20. EDUCATION_VERIFYING_LOCALSTORAGE_ITEM,
  21. } from '@/app/education-apply/constants'
  22. import Pricing from '@/app/components/billing/pricing'
  23. import type { ModerationConfig, PromptVariable } from '@/models/debug'
  24. import type {
  25. ApiBasedExtension,
  26. ExternalDataTool,
  27. } from '@/models/common'
  28. import type { CreateExternalAPIReq } from '@/app/components/datasets/external-api/declarations'
  29. import ModelLoadBalancingEntryModal from '@/app/components/header/account-setting/model-provider-page/model-modal/model-load-balancing-entry-modal'
  30. import type { ModelLoadBalancingModalProps } from '@/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal'
  31. import ModelLoadBalancingModal from '@/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal'
  32. import OpeningSettingModal from '@/app/components/base/features/new-feature-panel/conversation-opener/modal'
  33. import type { OpeningStatement } from '@/app/components/base/features/types'
  34. import type { InputVar } from '@/app/components/workflow/types'
  35. import type { UpdatePluginPayload } from '@/app/components/plugins/types'
  36. import UpdatePlugin from '@/app/components/plugins/update-plugin'
  37. import { removeSpecificQueryParam } from '@/utils'
  38. export type ModalState<T> = {
  39. payload: T
  40. onCancelCallback?: () => void
  41. onSaveCallback?: (newPayload: T) => void
  42. onRemoveCallback?: (newPayload: T) => void
  43. onEditCallback?: (newPayload: T) => void
  44. onValidateBeforeSaveCallback?: (newPayload: T) => boolean
  45. isEditMode?: boolean
  46. datasetBindings?: { id: string; name: string }[]
  47. }
  48. export type ModelModalType = {
  49. currentProvider: ModelProvider
  50. currentConfigurationMethod: ConfigurationMethodEnum
  51. currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
  52. }
  53. export type LoadBalancingEntryModalType = ModelModalType & {
  54. entry?: ModelLoadBalancingConfigEntry
  55. index?: number
  56. }
  57. export type ModalContextState = {
  58. setShowAccountSettingModal: Dispatch<SetStateAction<ModalState<string> | null>>
  59. setShowApiBasedExtensionModal: Dispatch<SetStateAction<ModalState<ApiBasedExtension> | null>>
  60. setShowModerationSettingModal: Dispatch<SetStateAction<ModalState<ModerationConfig> | null>>
  61. setShowExternalDataToolModal: Dispatch<SetStateAction<ModalState<ExternalDataTool> | null>>
  62. setShowPricingModal: () => void
  63. setShowAnnotationFullModal: () => void
  64. setShowModelModal: Dispatch<SetStateAction<ModalState<ModelModalType> | null>>
  65. setShowExternalKnowledgeAPIModal: Dispatch<SetStateAction<ModalState<CreateExternalAPIReq> | null>>
  66. setShowModelLoadBalancingModal: Dispatch<SetStateAction<ModelLoadBalancingModalProps | null>>
  67. setShowModelLoadBalancingEntryModal: Dispatch<SetStateAction<ModalState<LoadBalancingEntryModalType> | null>>
  68. setShowOpeningModal: Dispatch<SetStateAction<ModalState<OpeningStatement & {
  69. promptVariables?: PromptVariable[]
  70. workflowVariables?: InputVar[]
  71. onAutoAddPromptVariable?: (variable: PromptVariable[]) => void
  72. }> | null>>
  73. setShowUpdatePluginModal: Dispatch<SetStateAction<ModalState<UpdatePluginPayload> | null>>
  74. }
  75. const ModalContext = createContext<ModalContextState>({
  76. setShowAccountSettingModal: () => { },
  77. setShowApiBasedExtensionModal: () => { },
  78. setShowModerationSettingModal: () => { },
  79. setShowExternalDataToolModal: () => { },
  80. setShowPricingModal: () => { },
  81. setShowAnnotationFullModal: () => { },
  82. setShowModelModal: () => { },
  83. setShowExternalKnowledgeAPIModal: () => { },
  84. setShowModelLoadBalancingModal: () => { },
  85. setShowModelLoadBalancingEntryModal: () => { },
  86. setShowOpeningModal: () => { },
  87. setShowUpdatePluginModal: () => { },
  88. })
  89. export const useModalContext = () => useContext(ModalContext)
  90. // Adding a dangling comma to avoid the generic parsing issue in tsx, see:
  91. // https://github.com/microsoft/TypeScript/issues/15713
  92. export const useModalContextSelector = <T,>(selector: (state: ModalContextState) => T): T =>
  93. useContextSelector(ModalContext, selector)
  94. type ModalContextProviderProps = {
  95. children: React.ReactNode
  96. }
  97. export const ModalContextProvider = ({
  98. children,
  99. }: ModalContextProviderProps) => {
  100. const [showAccountSettingModal, setShowAccountSettingModal] = useState<ModalState<string> | null>(null)
  101. const [showApiBasedExtensionModal, setShowApiBasedExtensionModal] = useState<ModalState<ApiBasedExtension> | null>(null)
  102. const [showModerationSettingModal, setShowModerationSettingModal] = useState<ModalState<ModerationConfig> | null>(null)
  103. const [showExternalDataToolModal, setShowExternalDataToolModal] = useState<ModalState<ExternalDataTool> | null>(null)
  104. const [showModelModal, setShowModelModal] = useState<ModalState<ModelModalType> | null>(null)
  105. const [showExternalKnowledgeAPIModal, setShowExternalKnowledgeAPIModal] = useState<ModalState<CreateExternalAPIReq> | null>(null)
  106. const [showModelLoadBalancingModal, setShowModelLoadBalancingModal] = useState<ModelLoadBalancingModalProps | null>(null)
  107. const [showModelLoadBalancingEntryModal, setShowModelLoadBalancingEntryModal] = useState<ModalState<LoadBalancingEntryModalType> | null>(null)
  108. const [showOpeningModal, setShowOpeningModal] = useState<ModalState<OpeningStatement & {
  109. promptVariables?: PromptVariable[]
  110. workflowVariables?: InputVar[]
  111. onAutoAddPromptVariable?: (variable: PromptVariable[]) => void
  112. }> | null>(null)
  113. const [showUpdatePluginModal, setShowUpdatePluginModal] = useState<ModalState<UpdatePluginPayload> | null>(null)
  114. const searchParams = useSearchParams()
  115. const router = useRouter()
  116. const [showPricingModal, setShowPricingModal] = useState(searchParams.get('show-pricing') === '1')
  117. const [showAnnotationFullModal, setShowAnnotationFullModal] = useState(false)
  118. const handleCancelAccountSettingModal = () => {
  119. const educationVerifying = localStorage.getItem(EDUCATION_VERIFYING_LOCALSTORAGE_ITEM)
  120. if (educationVerifying === 'yes')
  121. localStorage.removeItem(EDUCATION_VERIFYING_LOCALSTORAGE_ITEM)
  122. removeSpecificQueryParam('action')
  123. setShowAccountSettingModal(null)
  124. if (showAccountSettingModal?.onCancelCallback)
  125. showAccountSettingModal?.onCancelCallback()
  126. }
  127. const handleCancelModerationSettingModal = () => {
  128. setShowModerationSettingModal(null)
  129. if (showModerationSettingModal?.onCancelCallback)
  130. showModerationSettingModal.onCancelCallback()
  131. }
  132. const handleCancelExternalDataToolModal = () => {
  133. setShowExternalDataToolModal(null)
  134. if (showExternalDataToolModal?.onCancelCallback)
  135. showExternalDataToolModal.onCancelCallback()
  136. }
  137. const handleCancelModelModal = useCallback(() => {
  138. setShowModelModal(null)
  139. if (showModelModal?.onCancelCallback)
  140. showModelModal.onCancelCallback()
  141. }, [showModelModal])
  142. const handleSaveModelModal = useCallback(() => {
  143. if (showModelModal?.onSaveCallback)
  144. showModelModal.onSaveCallback(showModelModal.payload)
  145. setShowModelModal(null)
  146. }, [showModelModal])
  147. const handleCancelExternalApiModal = useCallback(() => {
  148. setShowExternalKnowledgeAPIModal(null)
  149. if (showExternalKnowledgeAPIModal?.onCancelCallback)
  150. showExternalKnowledgeAPIModal.onCancelCallback()
  151. }, [showExternalKnowledgeAPIModal])
  152. const handleSaveExternalApiModal = useCallback(async (updatedFormValue: CreateExternalAPIReq) => {
  153. if (showExternalKnowledgeAPIModal?.onSaveCallback)
  154. showExternalKnowledgeAPIModal.onSaveCallback(updatedFormValue)
  155. setShowExternalKnowledgeAPIModal(null)
  156. }, [showExternalKnowledgeAPIModal])
  157. const handleEditExternalApiModal = useCallback(async (updatedFormValue: CreateExternalAPIReq) => {
  158. if (showExternalKnowledgeAPIModal?.onEditCallback)
  159. showExternalKnowledgeAPIModal.onEditCallback(updatedFormValue)
  160. setShowExternalKnowledgeAPIModal(null)
  161. }, [showExternalKnowledgeAPIModal])
  162. const handleCancelModelLoadBalancingEntryModal = useCallback(() => {
  163. showModelLoadBalancingEntryModal?.onCancelCallback?.()
  164. setShowModelLoadBalancingEntryModal(null)
  165. }, [showModelLoadBalancingEntryModal])
  166. const handleCancelOpeningModal = useCallback(() => {
  167. setShowOpeningModal(null)
  168. if (showOpeningModal?.onCancelCallback)
  169. showOpeningModal.onCancelCallback()
  170. }, [showOpeningModal])
  171. const handleSaveModelLoadBalancingEntryModal = useCallback((entry: ModelLoadBalancingConfigEntry) => {
  172. showModelLoadBalancingEntryModal?.onSaveCallback?.({
  173. ...showModelLoadBalancingEntryModal.payload,
  174. entry,
  175. })
  176. setShowModelLoadBalancingEntryModal(null)
  177. }, [showModelLoadBalancingEntryModal])
  178. const handleRemoveModelLoadBalancingEntry = useCallback(() => {
  179. showModelLoadBalancingEntryModal?.onRemoveCallback?.(showModelLoadBalancingEntryModal.payload)
  180. setShowModelLoadBalancingEntryModal(null)
  181. }, [showModelLoadBalancingEntryModal])
  182. const handleSaveApiBasedExtension = (newApiBasedExtension: ApiBasedExtension) => {
  183. if (showApiBasedExtensionModal?.onSaveCallback)
  184. showApiBasedExtensionModal.onSaveCallback(newApiBasedExtension)
  185. setShowApiBasedExtensionModal(null)
  186. }
  187. const handleSaveModeration = (newModerationConfig: ModerationConfig) => {
  188. if (showModerationSettingModal?.onSaveCallback)
  189. showModerationSettingModal.onSaveCallback(newModerationConfig)
  190. setShowModerationSettingModal(null)
  191. }
  192. const handleSaveExternalDataTool = (newExternalDataTool: ExternalDataTool) => {
  193. if (showExternalDataToolModal?.onSaveCallback)
  194. showExternalDataToolModal.onSaveCallback(newExternalDataTool)
  195. setShowExternalDataToolModal(null)
  196. }
  197. const handleValidateBeforeSaveExternalDataTool = (newExternalDataTool: ExternalDataTool) => {
  198. if (showExternalDataToolModal?.onValidateBeforeSaveCallback)
  199. return showExternalDataToolModal?.onValidateBeforeSaveCallback(newExternalDataTool)
  200. return true
  201. }
  202. const handleSaveOpeningModal = (newOpening: OpeningStatement) => {
  203. if (showOpeningModal?.onSaveCallback)
  204. showOpeningModal.onSaveCallback(newOpening)
  205. setShowOpeningModal(null)
  206. }
  207. return (
  208. <ModalContext.Provider value={{
  209. setShowAccountSettingModal,
  210. setShowApiBasedExtensionModal,
  211. setShowModerationSettingModal,
  212. setShowExternalDataToolModal,
  213. setShowPricingModal: () => setShowPricingModal(true),
  214. setShowAnnotationFullModal: () => setShowAnnotationFullModal(true),
  215. setShowModelModal,
  216. setShowExternalKnowledgeAPIModal,
  217. setShowModelLoadBalancingModal,
  218. setShowModelLoadBalancingEntryModal,
  219. setShowOpeningModal,
  220. setShowUpdatePluginModal,
  221. }}>
  222. <>
  223. {children}
  224. {
  225. !!showAccountSettingModal && (
  226. <AccountSetting
  227. activeTab={showAccountSettingModal.payload}
  228. onCancel={handleCancelAccountSettingModal}
  229. />
  230. )
  231. }
  232. {
  233. !!showApiBasedExtensionModal && (
  234. <ApiBasedExtensionModal
  235. data={showApiBasedExtensionModal.payload}
  236. onCancel={() => setShowApiBasedExtensionModal(null)}
  237. onSave={handleSaveApiBasedExtension}
  238. />
  239. )
  240. }
  241. {
  242. !!showModerationSettingModal && (
  243. <ModerationSettingModal
  244. data={showModerationSettingModal.payload}
  245. onCancel={handleCancelModerationSettingModal}
  246. onSave={handleSaveModeration}
  247. />
  248. )
  249. }
  250. {
  251. !!showExternalDataToolModal && (
  252. <ExternalDataToolModal
  253. data={showExternalDataToolModal.payload}
  254. onCancel={handleCancelExternalDataToolModal}
  255. onSave={handleSaveExternalDataTool}
  256. onValidateBeforeSave={handleValidateBeforeSaveExternalDataTool}
  257. />
  258. )
  259. }
  260. {
  261. !!showPricingModal && (
  262. <Pricing onCancel={() => {
  263. if (searchParams.get('show-pricing') === '1')
  264. router.push(location.pathname, { forceOptimisticNavigation: true } as any)
  265. setShowPricingModal(false)
  266. }} />
  267. )
  268. }
  269. {
  270. showAnnotationFullModal && (
  271. <AnnotationFullModal
  272. show={showAnnotationFullModal}
  273. onHide={() => setShowAnnotationFullModal(false)} />
  274. )
  275. }
  276. {
  277. !!showModelModal && (
  278. <ModelModal
  279. provider={showModelModal.payload.currentProvider}
  280. configurateMethod={showModelModal.payload.currentConfigurationMethod}
  281. currentCustomConfigurationModelFixedFields={showModelModal.payload.currentCustomConfigurationModelFixedFields}
  282. onCancel={handleCancelModelModal}
  283. onSave={handleSaveModelModal}
  284. />
  285. )
  286. }
  287. {
  288. !!showExternalKnowledgeAPIModal && (
  289. <ExternalAPIModal
  290. data={showExternalKnowledgeAPIModal.payload}
  291. datasetBindings={showExternalKnowledgeAPIModal.datasetBindings ?? []}
  292. onSave={handleSaveExternalApiModal}
  293. onCancel={handleCancelExternalApiModal}
  294. onEdit={handleEditExternalApiModal}
  295. isEditMode={showExternalKnowledgeAPIModal.isEditMode ?? false}
  296. />
  297. )
  298. }
  299. {
  300. Boolean(showModelLoadBalancingModal) && (
  301. <ModelLoadBalancingModal {...showModelLoadBalancingModal!} />
  302. )
  303. }
  304. {
  305. !!showModelLoadBalancingEntryModal && (
  306. <ModelLoadBalancingEntryModal
  307. provider={showModelLoadBalancingEntryModal.payload.currentProvider}
  308. configurationMethod={showModelLoadBalancingEntryModal.payload.currentConfigurationMethod}
  309. currentCustomConfigurationModelFixedFields={showModelLoadBalancingEntryModal.payload.currentCustomConfigurationModelFixedFields}
  310. entry={showModelLoadBalancingEntryModal.payload.entry}
  311. onCancel={handleCancelModelLoadBalancingEntryModal}
  312. onSave={handleSaveModelLoadBalancingEntryModal}
  313. onRemove={handleRemoveModelLoadBalancingEntry}
  314. />
  315. )
  316. }
  317. {showOpeningModal && (
  318. <OpeningSettingModal
  319. data={showOpeningModal.payload}
  320. onSave={handleSaveOpeningModal}
  321. onCancel={handleCancelOpeningModal}
  322. promptVariables={showOpeningModal.payload.promptVariables}
  323. workflowVariables={showOpeningModal.payload.workflowVariables}
  324. onAutoAddPromptVariable={showOpeningModal.payload.onAutoAddPromptVariable}
  325. />
  326. )}
  327. {
  328. !!showUpdatePluginModal && (
  329. <UpdatePlugin
  330. {...showUpdatePluginModal.payload}
  331. onCancel={() => {
  332. setShowUpdatePluginModal(null)
  333. showUpdatePluginModal.onCancelCallback?.()
  334. }}
  335. onSave={() => {
  336. setShowUpdatePluginModal(null)
  337. showUpdatePluginModal.onSaveCallback?.({} as any)
  338. }}
  339. />
  340. )
  341. }
  342. </>
  343. </ModalContext.Provider>
  344. )
  345. }
  346. export default ModalContext