index.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. 'use client'
  2. import type { MouseEventHandler } from 'react'
  3. import { useRef, useState } from 'react'
  4. import { useRouter } from 'next/navigation'
  5. import { useContext } from 'use-context-selector'
  6. import { useTranslation } from 'react-i18next'
  7. import Uploader from './uploader'
  8. import Button from '@/app/components/base/button'
  9. import Modal from '@/app/components/base/modal'
  10. import { ToastContext } from '@/app/components/base/toast'
  11. import { importApp } from '@/service/apps'
  12. import { useAppContext } from '@/context/app-context'
  13. import { useProviderContext } from '@/context/provider-context'
  14. import AppsFull from '@/app/components/billing/apps-full-in-dialog'
  15. import { XClose } from '@/app/components/base/icons/src/vender/line/general'
  16. import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
  17. import { getRedirection } from '@/utils/app-redirection'
  18. type CreateFromDSLModalProps = {
  19. show: boolean
  20. onSuccess?: () => void
  21. onClose: () => void
  22. }
  23. const CreateFromDSLModal = ({ show, onSuccess, onClose }: CreateFromDSLModalProps) => {
  24. const { push } = useRouter()
  25. const { t } = useTranslation()
  26. const { notify } = useContext(ToastContext)
  27. const [currentFile, setDSLFile] = useState<File>()
  28. const [fileContent, setFileContent] = useState<string>()
  29. const readFile = (file: File) => {
  30. const reader = new FileReader()
  31. reader.onload = function (event) {
  32. const content = event.target?.result
  33. setFileContent(content as string)
  34. }
  35. reader.readAsText(file)
  36. }
  37. const handleFile = (file?: File) => {
  38. setDSLFile(file)
  39. if (file)
  40. readFile(file)
  41. if (!file)
  42. setFileContent('')
  43. }
  44. const { isCurrentWorkspaceEditor } = useAppContext()
  45. const { plan, enableBilling } = useProviderContext()
  46. const isAppsFull = (enableBilling && plan.usage.buildApps >= plan.total.buildApps)
  47. const isCreatingRef = useRef(false)
  48. const onCreate: MouseEventHandler = async () => {
  49. if (isCreatingRef.current)
  50. return
  51. isCreatingRef.current = true
  52. if (!currentFile)
  53. return
  54. try {
  55. const app = await importApp({
  56. data: fileContent || '',
  57. })
  58. if (onSuccess)
  59. onSuccess()
  60. if (onClose)
  61. onClose()
  62. notify({ type: 'success', message: t('app.newApp.appCreated') })
  63. localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
  64. getRedirection(isCurrentWorkspaceEditor, app, push)
  65. }
  66. catch (e) {
  67. notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
  68. }
  69. isCreatingRef.current = false
  70. }
  71. return (
  72. <Modal
  73. className='px-8 py-6 max-w-[520px] w-[520px] rounded-xl'
  74. isShow={show}
  75. onClose={() => {}}
  76. >
  77. <div className='relative pb-2 text-xl font-medium leading-[30px] text-gray-900'>{t('app.createFromConfigFile')}</div>
  78. <div className='absolute right-4 top-4 p-2 cursor-pointer' onClick={onClose}>
  79. <XClose className='w-4 h-4 text-gray-500' />
  80. </div>
  81. <Uploader
  82. file={currentFile}
  83. updateFile={handleFile}
  84. />
  85. {isAppsFull && <AppsFull loc='app-create-dsl' />}
  86. <div className='pt-6 flex justify-end'>
  87. <Button className='mr-2 text-gray-700 text-sm font-medium' onClick={onClose}>{t('app.newApp.Cancel')}</Button>
  88. <Button className='text-sm font-medium' disabled={isAppsFull || !currentFile} type="primary" onClick={onCreate}>{t('app.newApp.Create')}</Button>
  89. </div>
  90. </Modal>
  91. )
  92. }
  93. export default CreateFromDSLModal