Initial commit
76
web/app/(commonLayout)/apps/AppCard.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
'use client'
|
||||
|
||||
import { useContext, useContextSelector } from 'use-context-selector'
|
||||
import Link from 'next/link'
|
||||
import type { MouseEventHandler } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import style from '../list.module.css'
|
||||
import AppModeLabel from './AppModeLabel'
|
||||
import type { App } from '@/types/app'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
import { deleteApp } from '@/service/apps'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import AppsContext from '@/context/app-context'
|
||||
|
||||
export type AppCardProps = {
|
||||
app: App
|
||||
}
|
||||
|
||||
const AppCard = ({
|
||||
app,
|
||||
}: AppCardProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { notify } = useContext(ToastContext)
|
||||
|
||||
const mutateApps = useContextSelector(AppsContext, state => state.mutateApps)
|
||||
|
||||
const [showConfirmDelete, setShowConfirmDelete] = useState(false)
|
||||
const onDeleteClick: MouseEventHandler = useCallback((e) => {
|
||||
e.preventDefault()
|
||||
setShowConfirmDelete(true)
|
||||
}, [])
|
||||
const onConfirmDelete = useCallback(async () => {
|
||||
try {
|
||||
await deleteApp(app.id)
|
||||
notify({ type: 'success', message: t('app.appDeleted') })
|
||||
mutateApps()
|
||||
}
|
||||
catch (e: any) {
|
||||
notify({ type: 'error', message: `${t('app.appDeleteFailed')}${'message' in e ? `: ${e.message}` : ''}` })
|
||||
}
|
||||
setShowConfirmDelete(false)
|
||||
}, [app.id])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Link href={`/app/${app.id}/overview`} className={style.listItem}>
|
||||
<div className={style.listItemTitle}>
|
||||
<AppIcon size='small' />
|
||||
<div className={style.listItemHeading}>
|
||||
<div className={style.listItemHeadingContent}>{app.name}</div>
|
||||
</div>
|
||||
<span className={style.deleteAppIcon} onClick={onDeleteClick} />
|
||||
</div>
|
||||
<div className={style.listItemDescription}>{app.model_config?.pre_prompt}</div>
|
||||
<div className={style.listItemFooter}>
|
||||
<AppModeLabel mode={app.mode} />
|
||||
</div>
|
||||
|
||||
{showConfirmDelete && (
|
||||
<Confirm
|
||||
title={t('app.deleteAppConfirmTitle')}
|
||||
content={t('app.deleteAppConfirmContent')}
|
||||
isShow={showConfirmDelete}
|
||||
onClose={() => setShowConfirmDelete(false)}
|
||||
onConfirm={onConfirmDelete}
|
||||
onCancel={() => setShowConfirmDelete(false)}
|
||||
/>
|
||||
)}
|
||||
</Link>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default AppCard
|
||||
26
web/app/(commonLayout)/apps/AppModeLabel.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
'use client'
|
||||
|
||||
import classNames from 'classnames'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { type AppMode } from '@/types/app'
|
||||
import style from '../list.module.css'
|
||||
|
||||
export type AppModeLabelProps = {
|
||||
mode: AppMode
|
||||
className?: string
|
||||
}
|
||||
|
||||
const AppModeLabel = ({
|
||||
mode,
|
||||
className,
|
||||
}: AppModeLabelProps) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<span className={classNames('flex items-center w-fit h-6 gap-1 px-2 text-gray-500 text-xs border border-gray-100 rounded', className)}>
|
||||
<span className={classNames(style.listItemFooterIcon, mode === 'chat' && style.solidChatIcon, mode === 'completion' && style.solidCompletionIcon)} />
|
||||
{t(`app.modes.${mode}`)}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export default AppModeLabel
|
||||
23
web/app/(commonLayout)/apps/Apps.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect } from 'react'
|
||||
import AppCard from './AppCard'
|
||||
import NewAppCard from './NewAppCard'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
|
||||
const Apps = () => {
|
||||
const { apps, mutateApps } = useAppContext()
|
||||
|
||||
useEffect(() => {
|
||||
mutateApps()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<nav className='grid content-start grid-cols-1 gap-4 px-12 pt-8 sm:grid-cols-2 lg:grid-cols-4 grow shrink-0'>
|
||||
{apps.map(app => (<AppCard key={app.id} app={app} />))}
|
||||
<NewAppCard />
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
export default Apps
|
||||
29
web/app/(commonLayout)/apps/NewAppCard.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import classNames from 'classnames'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import style from '../list.module.css'
|
||||
import NewAppDialog from './NewAppDialog'
|
||||
|
||||
const CreateAppCard = () => {
|
||||
const { t } = useTranslation()
|
||||
const [showNewAppDialog, setShowNewAppDialog] = useState(false)
|
||||
|
||||
return (
|
||||
<a className={classNames(style.listItem, style.newItemCard)} onClick={() => setShowNewAppDialog(true)}>
|
||||
<div className={style.listItemTitle}>
|
||||
<span className={style.newItemIcon}>
|
||||
<span className={classNames(style.newItemIconImage, style.newItemIconAdd)} />
|
||||
</span>
|
||||
<div className={classNames(style.listItemHeading, style.newItemCardHeading)}>
|
||||
{t('app.createApp')}
|
||||
</div>
|
||||
</div>
|
||||
{/* <div className='text-xs text-gray-500'>{t('app.createFromConfigFile')}</div> */}
|
||||
<NewAppDialog show={showNewAppDialog} onClose={() => setShowNewAppDialog(false)} />
|
||||
</a>
|
||||
)
|
||||
}
|
||||
|
||||
export default CreateAppCard
|
||||
193
web/app/(commonLayout)/apps/NewAppDialog.tsx
Normal file
@@ -0,0 +1,193 @@
|
||||
'use client'
|
||||
|
||||
import type { MouseEventHandler } from 'react'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import useSWR from 'swr'
|
||||
import classNames from 'classnames'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useContext, useContextSelector } from 'use-context-selector'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import style from '../list.module.css'
|
||||
import AppModeLabel from './AppModeLabel'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Dialog from '@/app/components/base/dialog'
|
||||
import type { AppMode } from '@/types/app'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
import { createApp, fetchAppTemplates } from '@/service/apps'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import AppsContext from '@/context/app-context'
|
||||
|
||||
type NewAppDialogProps = {
|
||||
show: boolean
|
||||
onClose?: () => void
|
||||
}
|
||||
|
||||
const NewAppDialog = ({ show, onClose }: NewAppDialogProps) => {
|
||||
const router = useRouter()
|
||||
const { notify } = useContext(ToastContext)
|
||||
const { t } = useTranslation()
|
||||
|
||||
const nameInputRef = useRef<HTMLInputElement>(null)
|
||||
const [newAppMode, setNewAppMode] = useState<AppMode>()
|
||||
const [isWithTemplate, setIsWithTemplate] = useState(false)
|
||||
const [selectedTemplateIndex, setSelectedTemplateIndex] = useState<number>(-1)
|
||||
const mutateApps = useContextSelector(AppsContext, state => state.mutateApps)
|
||||
|
||||
const { data: templates, mutate } = useSWR({ url: '/app-templates' }, fetchAppTemplates)
|
||||
const mutateTemplates = useCallback(
|
||||
() => mutate(),
|
||||
[],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
mutateTemplates()
|
||||
setIsWithTemplate(false)
|
||||
}
|
||||
}, [show])
|
||||
|
||||
const isCreatingRef = useRef(false)
|
||||
const onCreate: MouseEventHandler = useCallback(async () => {
|
||||
const name = nameInputRef.current?.value
|
||||
if (!name) {
|
||||
notify({ type: 'error', message: t('app.newApp.nameNotEmpty') })
|
||||
return
|
||||
}
|
||||
if (!templates || (isWithTemplate && !(selectedTemplateIndex > -1))) {
|
||||
notify({ type: 'error', message: t('app.newApp.appTemplateNotSelected') })
|
||||
return
|
||||
}
|
||||
if (!isWithTemplate && !newAppMode) {
|
||||
notify({ type: 'error', message: t('app.newApp.appTypeRequired') })
|
||||
return
|
||||
}
|
||||
if (isCreatingRef.current)
|
||||
return
|
||||
isCreatingRef.current = true
|
||||
try {
|
||||
const app = await createApp({
|
||||
name,
|
||||
mode: isWithTemplate ? templates.data[selectedTemplateIndex].mode : newAppMode!,
|
||||
config: isWithTemplate ? templates.data[selectedTemplateIndex].model_config : undefined,
|
||||
})
|
||||
if (onClose)
|
||||
onClose()
|
||||
notify({ type: 'success', message: t('app.newApp.appCreated') })
|
||||
mutateApps()
|
||||
router.push(`/app/${app.id}/overview`)
|
||||
}
|
||||
catch (e) {
|
||||
notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
|
||||
}
|
||||
isCreatingRef.current = false
|
||||
}, [isWithTemplate, newAppMode, notify, router, templates, selectedTemplateIndex])
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
show={show}
|
||||
title={t('app.newApp.startToCreate')}
|
||||
footer={
|
||||
<>
|
||||
<Button onClick={onClose}>{t('app.newApp.Cancel')}</Button>
|
||||
<Button type="primary" onClick={onCreate}>{t('app.newApp.Create')}</Button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<h3 className={style.newItemCaption}>{t('app.newApp.captionName')}</h3>
|
||||
|
||||
<div className='flex items-center justify-between gap-3 mb-8'>
|
||||
<AppIcon size='large' />
|
||||
<input ref={nameInputRef} className='h-10 px-3 text-sm font-normal bg-gray-100 rounded-lg grow' />
|
||||
</div>
|
||||
|
||||
<div className='h-[247px]'>
|
||||
<div className={style.newItemCaption}>
|
||||
<h3 className='inline'>{t('app.newApp.captionAppType')}</h3>
|
||||
{isWithTemplate && (
|
||||
<>
|
||||
<span className='block ml-[9px] mr-[9px] w-[1px] h-[13px] bg-gray-200' />
|
||||
<span
|
||||
className='inline-flex items-center gap-1 text-xs font-medium cursor-pointer text-primary-600'
|
||||
onClick={() => setIsWithTemplate(false)}
|
||||
>
|
||||
{t('app.newApp.hideTemplates')}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{isWithTemplate
|
||||
? (
|
||||
<ul className='grid grid-cols-2 gap-4'>
|
||||
{templates?.data?.map((template, index) => (
|
||||
<li
|
||||
key={index}
|
||||
className={classNames(style.listItem, style.selectable, selectedTemplateIndex === index && style.selected)}
|
||||
onClick={() => setSelectedTemplateIndex(index)}
|
||||
>
|
||||
<div className={style.listItemTitle}>
|
||||
<AppIcon size='small' />
|
||||
<div className={style.listItemHeading}>
|
||||
<div className={style.listItemHeadingContent}>{template.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={style.listItemDescription}>{template.model_config?.pre_prompt}</div>
|
||||
<AppModeLabel mode={template.mode} className='mt-2' />
|
||||
{/* <AppModeLabel mode='chat' className='mt-2' /> */}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
: (
|
||||
<>
|
||||
<ul className='grid grid-cols-2 gap-4'>
|
||||
<li
|
||||
className={classNames(style.listItem, style.selectable, newAppMode === 'chat' && style.selected)}
|
||||
onClick={() => setNewAppMode('chat')}
|
||||
>
|
||||
<div className={style.listItemTitle}>
|
||||
<span className={style.newItemIcon}>
|
||||
<span className={classNames(style.newItemIconImage, style.newItemIconChat)} />
|
||||
</span>
|
||||
<div className={style.listItemHeading}>
|
||||
<div className={style.listItemHeadingContent}>{t('app.newApp.chatApp')}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={style.listItemDescription}>{t('app.newApp.chatAppIntro')}</div>
|
||||
<div className={classNames(style.listItemFooter, 'justify-end')}>
|
||||
<a className={style.listItemLink} href='https://udify.app/chat/7CQBa5yyvYLSkZtx' target='_blank'>{t('app.newApp.previewDemo')}<span className={classNames(style.linkIcon, style.grayLinkIcon)} /></a>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className={classNames(style.listItem, style.selectable, newAppMode === 'completion' && style.selected)}
|
||||
onClick={() => setNewAppMode('completion')}
|
||||
>
|
||||
<div className={style.listItemTitle}>
|
||||
<span className={style.newItemIcon}>
|
||||
<span className={classNames(style.newItemIconImage, style.newItemIconComplete)} />
|
||||
</span>
|
||||
<div className={style.listItemHeading}>
|
||||
<div className={style.listItemHeadingContent}>{t('app.newApp.completeApp')}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={style.listItemDescription}>{t('app.newApp.completeAppIntro')}</div>
|
||||
<div className={classNames(style.listItemFooter, 'justify-end')}>
|
||||
<a className={style.listItemLink} href='https://udify.app/completion/aeFTj0VCb3Ok3TUE' target='_blank'>{t('app.newApp.previewDemo')}<span className={classNames(style.linkIcon, style.grayLinkIcon)} /></a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div className='flex items-center h-[34px] mt-2'>
|
||||
<span
|
||||
className='inline-flex items-center gap-1 text-xs font-medium cursor-pointer text-primary-600'
|
||||
onClick={() => setIsWithTemplate(true)}
|
||||
>
|
||||
{t('app.newApp.showTemplates')}<span className={style.rightIcon} />
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
export default NewAppDialog
|
||||
3
web/app/(commonLayout)/apps/assets/add.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8 4V8M8 8V12M8 8H12M8 8H4" stroke="#6B7280" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 206 B |
4
web/app/(commonLayout)/apps/assets/chat-solid.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.631586 8.25C0.631586 6.46656 2.04586 5 3.8158 5C5.58573 5 7.00001 6.46656 7.00001 8.25C7.00001 10.0334 5.58573 11.5 3.8158 11.5C3.45197 11.5 3.10149 11.4375 2.77474 11.3222C2.72073 11.3031 2.68723 11.2913 2.66266 11.2832C2.65821 11.2817 2.65456 11.2806 2.65164 11.2796L2.64892 11.2799C2.63177 11.2818 2.60839 11.285 2.56507 11.2909L1.06766 11.4954C0.905637 11.5175 0.743029 11.459 0.632239 11.3387C0.521449 11.2185 0.476481 11.0516 0.511825 10.8919L0.817497 9.51109C0.828118 9.46311 0.833802 9.43722 0.837453 9.41817C0.83766 9.4171 0.838022 9.41517 0.838022 9.41517C0.837114 9.412 0.835963 9.40808 0.834525 9.40332C0.826292 9.37605 0.814183 9.33888 0.794499 9.27863C0.688657 8.95463 0.631586 8.60857 0.631586 8.25Z" fill="#98A2B3"/>
|
||||
<path d="M2.57377 4.1863C2.96256 4.06535 3.37698 4 3.80894 4C6.16566 4 8.00006 5.94534 8.00006 8.24999C8.00006 8.65682 7.9429 9.05245 7.8358 9.42816C8.10681 9.37948 8.36964 9.30678 8.6219 9.21229C8.65748 9.19897 8.69298 9.18534 8.72893 9.17304C8.75795 9.17641 8.78684 9.18093 8.81574 9.18517L10.4222 9.42065C10.498 9.43179 10.5841 9.44444 10.6591 9.4487C10.7422 9.45343 10.8713 9.45292 11.0081 9.39408C11.1789 9.32061 11.3164 9.18628 11.3938 9.01716C11.4558 8.88174 11.4593 8.75269 11.4564 8.66955C11.4539 8.59442 11.4433 8.5081 11.4339 8.43202L11.2309 6.78307C11.2256 6.7402 11.2229 6.71768 11.2213 6.70118C11.23 6.66505 11.2466 6.6301 11.2598 6.59546C11.4492 6.09896 11.5526 5.56093 11.5526 5C11.5526 2.51163 9.52304 0.5 7.02632 0.5C4.80843 0.5 2.95915 2.08742 2.57377 4.1863Z" fill="#98A2B3"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
3
web/app/(commonLayout)/apps/assets/chat.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.1667 6.66634H15.8333C16.2754 6.66634 16.6993 6.84194 17.0118 7.1545C17.3244 7.46706 17.5 7.89098 17.5 8.33301V13.333C17.5 13.775 17.3244 14.199 17.0118 14.5115C16.6993 14.8241 16.2754 14.9997 15.8333 14.9997H14.1667V18.333L10.8333 14.9997H7.5C7.28111 14.9999 7.06433 14.9569 6.86211 14.8731C6.6599 14.7893 6.47623 14.6663 6.32167 14.5113M6.32167 14.5113L9.16667 11.6663H12.5C12.942 11.6663 13.366 11.4907 13.6785 11.1782C13.9911 10.8656 14.1667 10.4417 14.1667 9.99967V4.99967C14.1667 4.55765 13.9911 4.13372 13.6785 3.82116C13.366 3.5086 12.942 3.33301 12.5 3.33301H4.16667C3.72464 3.33301 3.30072 3.5086 2.98816 3.82116C2.67559 4.13372 2.5 4.55765 2.5 4.99967V9.99967C2.5 10.4417 2.67559 10.8656 2.98816 11.1782C3.30072 11.4907 3.72464 11.6663 4.16667 11.6663H5.83333V14.9997L6.32167 14.5113Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1002 B |
4
web/app/(commonLayout)/apps/assets/completion-solid.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.5 1.00779C6.5 0.994638 6.5 0.988062 6.49943 0.976137C6.48764 0.729248 6.27052 0.51224 6.02363 0.50056C6.01171 0.499996 6.0078 0.499998 6.00001 0.5H4.37933C3.97686 0.499995 3.64468 0.49999 3.37409 0.522098C3.09304 0.545061 2.83469 0.594343 2.59202 0.717989C2.2157 0.909735 1.90973 1.2157 1.71799 1.59202C1.59434 1.83469 1.54506 2.09304 1.5221 2.37409C1.49999 2.64468 1.49999 2.97686 1.5 3.37934V8.62066C1.49999 9.02313 1.49999 9.35532 1.5221 9.62591C1.54506 9.90696 1.59434 10.1653 1.71799 10.408C1.90973 10.7843 2.2157 11.0903 2.59202 11.282C2.83469 11.4057 3.09304 11.4549 3.37409 11.4779C3.64468 11.5 3.97686 11.5 4.37934 11.5H7.62066C8.02314 11.5 8.35532 11.5 8.62591 11.4779C8.90696 11.4549 9.16531 11.4057 9.40798 11.282C9.78431 11.0903 10.0903 10.7843 10.282 10.408C10.4057 10.1653 10.4549 9.90696 10.4779 9.62591C10.5 9.35532 10.5 9.02314 10.5 8.62066V4.99997C10.5 4.9922 10.5 4.98832 10.4994 4.97641C10.4878 4.72949 10.2707 4.51236 10.0238 4.50057C10.0119 4.50001 10.0054 4.50001 9.99225 4.50001L7.78404 4.50001C7.65786 4.50002 7.53496 4.50004 7.43089 4.49153C7.31659 4.48219 7.18172 4.46016 7.04601 4.39101C6.85785 4.29514 6.70487 4.14216 6.609 3.954C6.53985 3.81828 6.51781 3.68342 6.50848 3.56912C6.49997 3.46504 6.49999 3.34215 6.5 3.21596L6.5 1.00779ZM4 6.5C3.72386 6.5 3.5 6.72386 3.5 7C3.5 7.27614 3.72386 7.5 4 7.5H8C8.27614 7.5 8.5 7.27614 8.5 7C8.5 6.72386 8.27614 6.5 8 6.5H4ZM4 8.5C3.72386 8.5 3.5 8.72386 3.5 9C3.5 9.27614 3.72386 9.5 4 9.5H7C7.27614 9.5 7.5 9.27614 7.5 9C7.5 8.72386 7.27614 8.5 7 8.5H4Z" fill="#98A2B3"/>
|
||||
<path d="M9.45398 3.5C9.60079 3.5 9.67419 3.5 9.73432 3.46314C9.81925 3.41107 9.87002 3.28842 9.84674 3.19157C9.83025 3.12299 9.78238 3.07516 9.68665 2.97952L8.02049 1.31336C7.92484 1.21762 7.87701 1.16975 7.80843 1.15326C7.71158 1.12998 7.58893 1.18075 7.53687 1.26567C7.5 1.3258 7.5 1.39921 7.5 1.54602L7.5 3.09998C7.5 3.23999 7.5 3.30999 7.52725 3.36347C7.55122 3.41051 7.58946 3.44876 7.6365 3.47272C7.68998 3.49997 7.75998 3.49997 7.9 3.49998L9.45398 3.5Z" fill="#98A2B3"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
3
web/app/(commonLayout)/apps/assets/completion.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.25 11.875V9.6875C16.25 8.1342 14.9908 6.875 13.4375 6.875H12.1875C11.6697 6.875 11.25 6.45527 11.25 5.9375V4.6875C11.25 3.1342 9.9908 1.875 8.4375 1.875H6.875M6.875 12.5H13.125M6.875 15H10M8.75 1.875H4.6875C4.16973 1.875 3.75 2.29473 3.75 2.8125V17.1875C3.75 17.7053 4.16973 18.125 4.6875 18.125H15.3125C15.8303 18.125 16.25 17.7053 16.25 17.1875V9.375C16.25 5.23286 12.8921 1.875 8.75 1.875Z" stroke="#1F2A37" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 595 B |
3
web/app/(commonLayout)/apps/assets/delete.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.6665 7.33333V11.3333M9.33317 7.33333V11.3333M2.6665 4.66667H13.3332M12.6665 4.66667L12.0885 12.7613C12.0646 13.0977 11.914 13.4125 11.6672 13.6424C11.4205 13.8722 11.0957 14 10.7585 14H5.24117C4.90393 14 4.57922 13.8722 4.33243 13.6424C4.08564 13.4125 3.93511 13.0977 3.91117 12.7613L3.33317 4.66667H12.6665ZM9.99984 4.66667V2.66667C9.99984 2.48986 9.9296 2.32029 9.80457 2.19526C9.67955 2.07024 9.50998 2 9.33317 2H6.6665C6.48969 2 6.32012 2.07024 6.1951 2.19526C6.07008 2.32029 5.99984 2.48986 5.99984 2.66667V4.66667H9.99984Z" stroke="#1F2A37" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 711 B |
3
web/app/(commonLayout)/apps/assets/discord.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.0101 4.50191C20.3529 3.74154 18.5759 3.18133 16.7179 2.86048C16.6841 2.85428 16.6503 2.86976 16.6328 2.90071C16.4043 3.30719 16.1511 3.83748 15.9738 4.25429C13.9754 3.95511 11.9873 3.95511 10.0298 4.25429C9.85253 3.82822 9.59019 3.30719 9.36062 2.90071C9.34319 2.87079 9.30939 2.85532 9.27555 2.86048C7.41857 3.18031 5.64152 3.74051 3.98335 4.50191C3.96899 4.5081 3.95669 4.51843 3.94852 4.53183C0.577841 9.56755 -0.345529 14.4795 0.107445 19.3306C0.109495 19.3543 0.122817 19.377 0.141265 19.3914C2.36514 21.0246 4.51935 22.0161 6.63355 22.6732C6.66739 22.6836 6.70324 22.6712 6.72477 22.6433C7.22489 21.9604 7.6707 21.2402 8.05293 20.4829C8.07549 20.4386 8.05396 20.386 8.00785 20.3684C7.30073 20.1002 6.6274 19.7731 5.97971 19.4017C5.92848 19.3718 5.92437 19.2985 5.9715 19.2635C6.1078 19.1613 6.24414 19.0551 6.37428 18.9478C6.39783 18.9282 6.43064 18.924 6.45833 18.9364C10.7134 20.8791 15.32 20.8791 19.5249 18.9364C19.5525 18.923 19.5854 18.9272 19.6099 18.9467C19.7401 19.054 19.8764 19.1613 20.0137 19.2635C20.0609 19.2985 20.0578 19.3718 20.0066 19.4017C19.3589 19.7804 18.6855 20.1002 17.9774 20.3674C17.9313 20.3849 17.9108 20.4386 17.9333 20.4829C18.3238 21.2392 18.7696 21.9593 19.2605 22.6423C19.281 22.6712 19.3179 22.6836 19.3517 22.6732C21.4761 22.0161 23.6303 21.0246 25.8542 19.3914C25.8737 19.377 25.886 19.3553 25.8881 19.3316C26.4302 13.7232 24.98 8.85156 22.0439 4.53286C22.0367 4.51843 22.0245 4.5081 22.0101 4.50191ZM8.68836 16.3768C7.40729 16.3768 6.35173 15.2007 6.35173 13.7563C6.35173 12.3119 7.38682 11.1358 8.68836 11.1358C10.0001 11.1358 11.0455 12.3222 11.025 13.7563C11.025 15.2007 9.98986 16.3768 8.68836 16.3768ZM17.3276 16.3768C16.0466 16.3768 14.991 15.2007 14.991 13.7563C14.991 12.3119 16.0261 11.1358 17.3276 11.1358C18.6394 11.1358 19.6847 12.3222 19.6643 13.7563C19.6643 15.2007 18.6394 16.3768 17.3276 16.3768Z" fill="#5865F2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
17
web/app/(commonLayout)/apps/assets/github.svg
Normal file
@@ -0,0 +1,17 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_131_1011)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.0003 0.5C9.15149 0.501478 6.39613 1.51046 4.22687 3.34652C2.05761 5.18259 0.615903 7.72601 0.159545 10.522C-0.296814 13.318 0.261927 16.1842 1.73587 18.6082C3.20981 21.0321 5.50284 22.8558 8.20493 23.753C8.80105 23.8636 9.0256 23.4941 9.0256 23.18C9.0256 22.8658 9.01367 21.955 9.0097 20.9592C5.6714 21.6804 4.96599 19.5505 4.96599 19.5505C4.42152 18.1674 3.63464 17.8039 3.63464 17.8039C2.54571 17.065 3.71611 17.0788 3.71611 17.0788C4.92227 17.1637 5.55616 18.3097 5.55616 18.3097C6.62521 20.1333 8.36389 19.6058 9.04745 19.2976C9.15475 18.5251 9.46673 17.9995 9.8105 17.7012C7.14383 17.4008 4.34204 16.3774 4.34204 11.8054C4.32551 10.6197 4.76802 9.47305 5.57801 8.60268C5.45481 8.30236 5.04348 7.08923 5.69524 5.44143C5.69524 5.44143 6.7027 5.12135 8.9958 6.66444C10.9627 6.12962 13.0379 6.12962 15.0047 6.66444C17.2958 5.12135 18.3013 5.44143 18.3013 5.44143C18.9551 7.08528 18.5437 8.29841 18.4205 8.60268C19.2331 9.47319 19.6765 10.6218 19.6585 11.8094C19.6585 16.3912 16.8507 17.4008 14.1801 17.6952C14.6093 18.0667 14.9928 18.7918 14.9928 19.9061C14.9928 21.5026 14.9789 22.7868 14.9789 23.18C14.9789 23.4981 15.1955 23.8695 15.8035 23.753C18.5059 22.8557 20.7992 21.0317 22.2731 18.6073C23.747 16.183 24.3055 13.3163 23.8486 10.5201C23.3917 7.7238 21.9493 5.18035 19.7793 3.34461C17.6093 1.50886 14.8533 0.500541 12.0042 0.5H12.0003Z" fill="#191717"/>
|
||||
<path d="M4.54444 17.6321C4.5186 17.6914 4.42322 17.7092 4.34573 17.6677C4.26823 17.6262 4.21061 17.5491 4.23843 17.4879C4.26625 17.4266 4.35964 17.4108 4.43714 17.4523C4.51463 17.4938 4.57424 17.5729 4.54444 17.6321Z" fill="#191717"/>
|
||||
<path d="M5.03123 18.1714C4.99008 18.192 4.943 18.1978 4.89805 18.1877C4.8531 18.1776 4.81308 18.1523 4.78483 18.1161C4.70734 18.0331 4.69143 17.9185 4.75104 17.8671C4.81066 17.8157 4.91797 17.8395 4.99546 17.9224C5.07296 18.0054 5.09084 18.12 5.03123 18.1714Z" fill="#191717"/>
|
||||
<path d="M5.50425 18.857C5.43072 18.9084 5.30553 18.857 5.23598 18.7543C5.21675 18.7359 5.20146 18.7138 5.19101 18.6893C5.18056 18.6649 5.17517 18.6386 5.17517 18.612C5.17517 18.5855 5.18056 18.5592 5.19101 18.5347C5.20146 18.5103 5.21675 18.4882 5.23598 18.4698C5.3095 18.4204 5.4347 18.4698 5.50425 18.5705C5.57379 18.6713 5.57578 18.8057 5.50425 18.857V18.857Z" fill="#191717"/>
|
||||
<path d="M6.14612 19.5207C6.08054 19.5939 5.94741 19.5741 5.83812 19.4753C5.72883 19.3765 5.70299 19.2422 5.76857 19.171C5.83414 19.0999 5.96727 19.1197 6.08054 19.2165C6.1938 19.3133 6.21566 19.4496 6.14612 19.5207V19.5207Z" fill="#191717"/>
|
||||
<path d="M7.04617 19.9081C7.01637 20.001 6.88124 20.0425 6.74612 20.003C6.611 19.9635 6.52158 19.8528 6.54741 19.758C6.57325 19.6631 6.71036 19.6197 6.84747 19.6631C6.98457 19.7066 7.07201 19.8113 7.04617 19.9081Z" fill="#191717"/>
|
||||
<path d="M8.02783 19.9752C8.02783 20.072 7.91656 20.155 7.77349 20.1569C7.63042 20.1589 7.51318 20.0799 7.51318 19.9831C7.51318 19.8863 7.62445 19.8033 7.76752 19.8013C7.91059 19.7993 8.02783 19.8764 8.02783 19.9752Z" fill="#191717"/>
|
||||
<path d="M8.9419 19.8232C8.95978 19.92 8.86042 20.0207 8.71735 20.0445C8.57428 20.0682 8.4491 20.0109 8.43121 19.916C8.41333 19.8212 8.51666 19.7185 8.65576 19.6928C8.79485 19.6671 8.92401 19.7264 8.9419 19.8232Z" fill="#191717"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_131_1011">
|
||||
<rect width="24" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
3
web/app/(commonLayout)/apps/assets/link-gray.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="13" height="14" viewBox="0 0 13 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.41663 3.75033H3.24996C2.96264 3.75033 2.68709 3.86446 2.48393 4.06763C2.28076 4.27079 2.16663 4.54634 2.16663 4.83366V10.2503C2.16663 10.5376 2.28076 10.8132 2.48393 11.0164C2.68709 11.2195 2.96264 11.3337 3.24996 11.3337H8.66663C8.95394 11.3337 9.22949 11.2195 9.43266 11.0164C9.63582 10.8132 9.74996 10.5376 9.74996 10.2503V8.08366M7.58329 2.66699H10.8333M10.8333 2.66699V5.91699M10.8333 2.66699L5.41663 8.08366" stroke="#9CA3AF" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 596 B |
3
web/app/(commonLayout)/apps/assets/link.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="13" height="14" viewBox="0 0 13 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.41663 3.75008H3.24996C2.96264 3.75008 2.68709 3.86422 2.48393 4.06738C2.28076 4.27055 2.16663 4.5461 2.16663 4.83341V10.2501C2.16663 10.5374 2.28076 10.8129 2.48393 11.0161C2.68709 11.2193 2.96264 11.3334 3.24996 11.3334H8.66663C8.95394 11.3334 9.22949 11.2193 9.43266 11.0161C9.63582 10.8129 9.74996 10.5374 9.74996 10.2501V8.08341M7.58329 2.66675H10.8333M10.8333 2.66675V5.91675M10.8333 2.66675L5.41663 8.08341" stroke="#1C64F2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 595 B |
3
web/app/(commonLayout)/apps/assets/right-arrow.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7 2.5L10.5 6M10.5 6L7 9.5M10.5 6H1.5" stroke="#1C64F2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 217 B |
36
web/app/(commonLayout)/apps/page.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import classNames from 'classnames'
|
||||
import style from '../list.module.css'
|
||||
import Apps from './Apps'
|
||||
import { getLocaleOnServer } from '@/i18n/server'
|
||||
import { useTranslation } from '@/i18n/i18next-serverside-config'
|
||||
|
||||
const AppList = async () => {
|
||||
const locale = getLocaleOnServer()
|
||||
const { t } = await useTranslation(locale, 'app')
|
||||
|
||||
return (
|
||||
<div className='flex flex-col overflow-auto bg-gray-100 shrink-0 grow'>
|
||||
<Apps />
|
||||
<footer className='px-12 py-6 grow-0 shrink-0'>
|
||||
<h3 className='text-xl font-semibold leading-tight text-gradient'>{t('join')}</h3>
|
||||
<p className='mt-1 text-sm font-normal leading-tight text-gray-700'>{t('communityIntro')}</p>
|
||||
{/*<p className='mt-3 text-sm'>*/}
|
||||
{/* <a className='inline-flex items-center gap-1 link' target='_blank' href={`https://docs.dify.ai${locale === 'en' ? '' : '/v/zh-hans'}/community/product-roadmap`}>*/}
|
||||
{/* {t('roadmap')}*/}
|
||||
{/* <span className={style.linkIcon} />*/}
|
||||
{/* </a>*/}
|
||||
{/*</p>*/}
|
||||
<div className='flex items-center gap-2 mt-3'>
|
||||
<a className={style.socialMediaLink} target='_blank' href='https://github.com/langgenius'><span className={classNames(style.socialMediaIcon, style.githubIcon)} /></a>
|
||||
<a className={style.socialMediaLink} target='_blank' href='https://discord.gg/AhzKf7dNgk'><span className={classNames(style.socialMediaIcon, style.discordIcon)} /></a>
|
||||
</div>
|
||||
</footer>
|
||||
</div >
|
||||
)
|
||||
}
|
||||
|
||||
export const metadata = {
|
||||
title: 'Apps - Dify',
|
||||
}
|
||||
|
||||
export default AppList
|
||||