| 'use client' | |
| import React, { useState } from 'react' | |
| import { useTranslation } from 'react-i18next' | |
| import { RiCloseLine } from '@remixicon/react' | |
| import AppIconPicker from '../../base/app-icon-picker' | |
| import Modal from '@/app/components/base/modal' | |
| import Button from '@/app/components/base/button' | |
| import Input from '@/app/components/base/input' | |
| import Textarea from '@/app/components/base/textarea' | |
| import Switch from '@/app/components/base/switch' | |
| import Toast from '@/app/components/base/toast' | |
| import AppIcon from '@/app/components/base/app-icon' | |
| import { useProviderContext } from '@/context/provider-context' | |
| import AppsFull from '@/app/components/billing/apps-full-in-dialog' | |
| import type { AppIconType } from '@/types/app' | |
| export type CreateAppModalProps = { | |
| show: boolean | |
| isEditModal?: boolean | |
| appName: string | |
| appDescription: string | |
| appIconType: AppIconType | null | |
| appIcon: string | |
| appIconBackground?: string | null | |
| appIconUrl?: string | null | |
| appMode?: string | |
| appUseIconAsAnswerIcon?: boolean | |
| onConfirm: (info: { | |
| name: string | |
| icon_type: AppIconType | |
| icon: string | |
| icon_background?: string | |
| description: string | |
| use_icon_as_answer_icon?: boolean | |
| }) => Promise<void> | |
| onHide: () => void | |
| } | |
| const CreateAppModal = ({ | |
| show = false, | |
| isEditModal = false, | |
| appIconType, | |
| appIcon: _appIcon, | |
| appIconBackground, | |
| appIconUrl, | |
| appName, | |
| appDescription, | |
| appMode, | |
| appUseIconAsAnswerIcon, | |
| onConfirm, | |
| onHide, | |
| }: CreateAppModalProps) => { | |
| const { t } = useTranslation() | |
| const [name, setName] = React.useState(appName) | |
| const [appIcon, setAppIcon] = useState( | |
| () => appIconType === 'image' | |
| ? { type: 'image' as const, fileId: _appIcon, url: appIconUrl } | |
| : { type: 'emoji' as const, icon: _appIcon, background: appIconBackground }, | |
| ) | |
| const [showAppIconPicker, setShowAppIconPicker] = useState(false) | |
| const [description, setDescription] = useState(appDescription || '') | |
| const [useIconAsAnswerIcon, setUseIconAsAnswerIcon] = useState(appUseIconAsAnswerIcon || false) | |
| const { plan, enableBilling } = useProviderContext() | |
| const isAppsFull = (enableBilling && plan.usage.buildApps >= plan.total.buildApps) | |
| const submit = () => { | |
| if (!name.trim()) { | |
| Toast.notify({ type: 'error', message: t('explore.appCustomize.nameRequired') }) | |
| return | |
| } | |
| onConfirm({ | |
| name, | |
| icon_type: appIcon.type, | |
| icon: appIcon.type === 'emoji' ? appIcon.icon : appIcon.fileId, | |
| icon_background: appIcon.type === 'emoji' ? appIcon.background! : undefined, | |
| description, | |
| use_icon_as_answer_icon: useIconAsAnswerIcon, | |
| }) | |
| onHide() | |
| } | |
| return ( | |
| <> | |
| <Modal | |
| isShow={show} | |
| onClose={() => {}} | |
| className='relative !max-w-[480px] px-8' | |
| > | |
| <div className='absolute right-4 top-4 p-2 cursor-pointer' onClick={onHide}> | |
| <RiCloseLine className='w-4 h-4 text-gray-500' /> | |
| </div> | |
| {isEditModal && ( | |
| <div className='mb-9 font-semibold text-xl leading-[30px] text-gray-900'>{t('app.editAppTitle')}</div> | |
| )} | |
| {!isEditModal && ( | |
| <div className='mb-9 font-semibold text-xl leading-[30px] text-gray-900'>{t('explore.appCustomize.title', { name: appName })}</div> | |
| )} | |
| <div className='mb-9'> | |
| {/* icon & name */} | |
| <div className='pt-2'> | |
| <div className='py-2 text-sm font-medium leading-[20px] text-gray-900'>{t('app.newApp.captionName')}</div> | |
| <div className='flex items-center justify-between space-x-2'> | |
| <AppIcon | |
| size='large' | |
| onClick={() => { setShowAppIconPicker(true) }} | |
| className='cursor-pointer' | |
| iconType={appIcon.type} | |
| icon={appIcon.type === 'image' ? appIcon.fileId : appIcon.icon} | |
| background={appIcon.type === 'image' ? undefined : appIcon.background} | |
| imageUrl={appIcon.type === 'image' ? appIcon.url : undefined} | |
| /> | |
| <Input | |
| value={name} | |
| onChange={e => setName(e.target.value)} | |
| placeholder={t('app.newApp.appNamePlaceholder') || ''} | |
| className='grow h-10' | |
| /> | |
| </div> | |
| </div> | |
| {/* description */} | |
| <div className='pt-2'> | |
| <div className='py-2 text-sm font-medium leading-[20px] text-gray-900'>{t('app.newApp.captionDescription')}</div> | |
| <Textarea | |
| className='resize-none' | |
| placeholder={t('app.newApp.appDescriptionPlaceholder') || ''} | |
| value={description} | |
| onChange={e => setDescription(e.target.value)} | |
| /> | |
| </div> | |
| {/* answer icon */} | |
| {isEditModal && (appMode === 'chat' || appMode === 'advanced-chat' || appMode === 'agent-chat') && ( | |
| <div className='pt-2'> | |
| <div className='flex justify-between items-center'> | |
| <div className='py-2 text-sm font-medium leading-[20px] text-gray-900'>{t('app.answerIcon.title')}</div> | |
| <Switch | |
| defaultValue={useIconAsAnswerIcon} | |
| onChange={v => setUseIconAsAnswerIcon(v)} | |
| /> | |
| </div> | |
| <p className='body-xs-regular text-gray-500'>{t('app.answerIcon.descriptionInExplore')}</p> | |
| </div> | |
| )} | |
| {!isEditModal && isAppsFull && <AppsFull loc='app-explore-create' />} | |
| </div> | |
| <div className='flex flex-row-reverse'> | |
| <Button disabled={!isEditModal && isAppsFull} className='w-24 ml-2' variant='primary' onClick={submit}>{!isEditModal ? t('common.operation.create') : t('common.operation.save')}</Button> | |
| <Button className='w-24' onClick={onHide}>{t('common.operation.cancel')}</Button> | |
| </div> | |
| </Modal> | |
| {showAppIconPicker && <AppIconPicker | |
| onSelect={(payload) => { | |
| setAppIcon(payload) | |
| setShowAppIconPicker(false) | |
| }} | |
| onClose={() => { | |
| setAppIcon(appIconType === 'image' | |
| ? { type: 'image' as const, url: appIconUrl, fileId: _appIcon } | |
| : { type: 'emoji' as const, icon: _appIcon, background: appIconBackground }) | |
| setShowAppIconPicker(false) | |
| }} | |
| />} | |
| </> | |
| ) | |
| } | |
| export default CreateAppModal | |