index.tsx 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. 'use client'
  2. import React, { useState } from 'react'
  3. import cn from 'classnames'
  4. import { useTranslation } from 'react-i18next'
  5. import s from './style.module.css'
  6. import Modal from '@/app/components/base/modal'
  7. import Button from '@/app/components/base/button'
  8. import Toast from '@/app/components/base/toast'
  9. import AppIcon from '@/app/components/base/app-icon'
  10. import EmojiPicker from '@/app/components/base/emoji-picker'
  11. import { useProviderContext } from '@/context/provider-context'
  12. import AppsFull from '@/app/components/billing/apps-full-in-dialog'
  13. export type DuplicateAppModalProps = {
  14. appName: string
  15. icon: string
  16. icon_background: string
  17. show: boolean
  18. onConfirm: (info: {
  19. name: string
  20. icon: string
  21. icon_background: string
  22. }) => Promise<void>
  23. onHide: () => void
  24. }
  25. const DuplicateAppModal = ({
  26. appName,
  27. icon,
  28. icon_background,
  29. show = false,
  30. onConfirm,
  31. onHide,
  32. }: DuplicateAppModalProps) => {
  33. const { t } = useTranslation()
  34. const [name, setName] = React.useState(appName)
  35. const [showEmojiPicker, setShowEmojiPicker] = useState(false)
  36. const [emoji, setEmoji] = useState({ icon, icon_background })
  37. const { plan, enableBilling } = useProviderContext()
  38. const isAppsFull = (enableBilling && plan.usage.buildApps >= plan.total.buildApps)
  39. const submit = () => {
  40. if (!name.trim()) {
  41. Toast.notify({ type: 'error', message: t('explore.appCustomize.nameRequired') })
  42. return
  43. }
  44. onConfirm({
  45. name,
  46. ...emoji,
  47. })
  48. onHide()
  49. }
  50. return (
  51. <>
  52. <Modal
  53. isShow={show}
  54. onClose={() => { }}
  55. className={cn(s.modal, '!max-w-[480px]', 'px-8')}
  56. >
  57. <span className={s.close} onClick={onHide} />
  58. <div className={s.title}>{t('app.duplicateTitle')}</div>
  59. <div className={s.content}>
  60. <div className={s.subTitle}>{t('explore.appCustomize.subTitle')}</div>
  61. <div className='flex items-center justify-between space-x-2'>
  62. <AppIcon size='large' onClick={() => { setShowEmojiPicker(true) }} className='cursor-pointer' icon={emoji.icon} background={emoji.icon_background} />
  63. <input
  64. value={name}
  65. onChange={e => setName(e.target.value)}
  66. className='h-10 px-3 text-sm font-normal bg-gray-100 rounded-lg grow'
  67. />
  68. </div>
  69. {isAppsFull && <AppsFull loc='app-duplicate-create' />}
  70. </div>
  71. <div className='flex flex-row-reverse'>
  72. <Button disabled={isAppsFull} className='w-24 ml-2' variant='primary' onClick={submit}>{t('app.duplicate')}</Button>
  73. <Button className='w-24' onClick={onHide}>{t('common.operation.cancel')}</Button>
  74. </div>
  75. </Modal>
  76. {showEmojiPicker && <EmojiPicker
  77. onSelect={(icon, icon_background) => {
  78. setEmoji({ icon, icon_background })
  79. setShowEmojiPicker(false)
  80. }}
  81. onClose={() => {
  82. setEmoji({ icon, icon_background })
  83. setShowEmojiPicker(false)
  84. }}
  85. />}
  86. </>
  87. )
  88. }
  89. export default DuplicateAppModal