feat: linked app new ui
This commit is contained in:
parent
77185d9617
commit
dd23f1093b
@ -20,14 +20,12 @@ import {
|
||||
// CommandLineIcon as CommandLineSolidIcon,
|
||||
DocumentTextIcon as DocumentTextSolidIcon,
|
||||
} from '@heroicons/react/24/solid'
|
||||
import Link from 'next/link'
|
||||
import { RiInformation2Line } from '@remixicon/react'
|
||||
import s from './style.module.css'
|
||||
import classNames from '@/utils/classnames'
|
||||
import { fetchDatasetDetail, fetchDatasetRelatedApps } from '@/service/datasets'
|
||||
import type { RelatedApp, RelatedAppResponse } from '@/models/datasets'
|
||||
import type { RelatedAppResponse } from '@/models/datasets'
|
||||
import AppSideBar from '@/app/components/app-sidebar'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import FloatPopoverContainer from '@/app/components/base/float-popover-container'
|
||||
import DatasetDetailContext from '@/context/dataset-detail'
|
||||
@ -35,57 +33,16 @@ import { DataSourceType } from '@/models/datasets'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import { LanguagesSupported } from '@/i18n/language'
|
||||
import { useStore } from '@/app/components/app/store'
|
||||
import { AiText, ChatBot, CuteRobot } from '@/app/components/base/icons/src/vender/solid/communication'
|
||||
import { Route } from '@/app/components/base/icons/src/vender/solid/mapsAndTravel'
|
||||
import { getLocaleOnClient } from '@/i18n'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import LinkedAppsPanel from '@/app/components/base/linked-apps-panel'
|
||||
|
||||
export type IAppDetailLayoutProps = {
|
||||
children: React.ReactNode
|
||||
params: { datasetId: string }
|
||||
}
|
||||
|
||||
type ILikedItemProps = {
|
||||
type?: 'plugin' | 'app'
|
||||
appStatus?: boolean
|
||||
detail: RelatedApp
|
||||
isMobile: boolean
|
||||
}
|
||||
|
||||
const LikedItem = ({
|
||||
type = 'app',
|
||||
detail,
|
||||
isMobile,
|
||||
}: ILikedItemProps) => {
|
||||
return (
|
||||
<Link className={classNames(s.itemWrapper, 'px-2', isMobile && 'justify-center')} href={`/app/${detail?.id}/overview`}>
|
||||
<div className={classNames(s.iconWrapper, 'mr-0')}>
|
||||
<AppIcon size='tiny' iconType={detail.icon_type} icon={detail.icon} background={detail.icon_background} imageUrl={detail.icon_url} />
|
||||
{type === 'app' && (
|
||||
<span className='absolute bottom-[-2px] right-[-2px] w-3.5 h-3.5 p-0.5 bg-white rounded border-[0.5px] border-[rgba(0,0,0,0.02)] shadow-sm'>
|
||||
{detail.mode === 'advanced-chat' && (
|
||||
<ChatBot className='w-2.5 h-2.5 text-[#1570EF]' />
|
||||
)}
|
||||
{detail.mode === 'agent-chat' && (
|
||||
<CuteRobot className='w-2.5 h-2.5 text-indigo-600' />
|
||||
)}
|
||||
{detail.mode === 'chat' && (
|
||||
<ChatBot className='w-2.5 h-2.5 text-[#1570EF]' />
|
||||
)}
|
||||
{detail.mode === 'completion' && (
|
||||
<AiText className='w-2.5 h-2.5 text-[#0E9384]' />
|
||||
)}
|
||||
{detail.mode === 'workflow' && (
|
||||
<Route className='w-2.5 h-2.5 text-[#f79009]' />
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{!isMobile && <div className={classNames(s.appInfo, 'ml-2')}>{detail?.name || '--'}</div>}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
const TargetIcon = ({ className }: SVGProps<SVGElement>) => {
|
||||
return <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
|
||||
<g clipPath="url(#clip0_4610_6951)">
|
||||
@ -124,23 +81,43 @@ const ExtraInfo = ({ isMobile, relatedApps }: IExtraInfoProps) => {
|
||||
const [isShowTips, { toggle: toggleTips, set: setShowTips }] = useBoolean(!isMobile)
|
||||
const { t } = useTranslation()
|
||||
|
||||
const hasRelatedApps = relatedApps?.data && relatedApps?.data?.length > 0
|
||||
const relatedAppsTotal = relatedApps?.data?.length || 0
|
||||
|
||||
useEffect(() => {
|
||||
setShowTips(!isMobile)
|
||||
}, [isMobile, setShowTips])
|
||||
|
||||
return <div className='w-full flex flex-col items-center'>
|
||||
<Divider className='mt-5' />
|
||||
{(relatedApps?.data && relatedApps?.data?.length > 0) && (
|
||||
return <div>
|
||||
{hasRelatedApps && (
|
||||
<>
|
||||
{!isMobile && <div className='w-full px-2 pb-1 pt-4 uppercase text-xs text-gray-500 font-medium'>{relatedApps?.total || '--'} {t('common.datasetMenus.relatedApp')}</div>}
|
||||
{!isMobile && (
|
||||
<Tooltip
|
||||
position='right'
|
||||
noDecoration
|
||||
needsDelay
|
||||
popupContent={
|
||||
<LinkedAppsPanel
|
||||
relatedApps={relatedApps.data}
|
||||
isMobile={isMobile}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<div className='inline-flex items-center system-xs-medium-uppercase text-text-secondary space-x-1 cursor-pointer'>
|
||||
<span>{relatedAppsTotal || '--'} {t('common.datasetMenus.relatedApp')}</span>
|
||||
<RiInformation2Line className='w-4 h-4' />
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{isMobile && <div className={classNames(s.subTitle, 'flex items-center justify-center !px-0 gap-1')}>
|
||||
{relatedApps?.total || '--'}
|
||||
{relatedAppsTotal || '--'}
|
||||
<PaperClipIcon className='h-4 w-4 text-gray-700' />
|
||||
</div>}
|
||||
{relatedApps?.data?.map((item, index) => (<LikedItem key={index} isMobile={isMobile} detail={item} />))}
|
||||
|
||||
</>
|
||||
)}
|
||||
{!relatedApps?.data?.length && (
|
||||
{!hasRelatedApps && (
|
||||
<FloatPopoverContainer
|
||||
placement='bottom-start'
|
||||
open={isShowTips}
|
||||
|
@ -1,12 +1,3 @@
|
||||
.itemWrapper {
|
||||
@apply flex items-center w-full h-10 rounded-lg hover:bg-gray-50 cursor-pointer;
|
||||
}
|
||||
.appInfo {
|
||||
@apply truncate text-gray-700 text-sm font-normal;
|
||||
}
|
||||
.iconWrapper {
|
||||
@apply relative w-6 h-6 rounded-lg;
|
||||
}
|
||||
.statusPoint {
|
||||
@apply flex justify-center items-center absolute -right-0.5 -bottom-0.5 w-2.5 h-2.5 bg-white rounded;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ type Props = {
|
||||
name: string
|
||||
description: string
|
||||
expand: boolean
|
||||
extraInfo?: React.ReactNode
|
||||
}
|
||||
|
||||
const DatasetInfo: FC<Props> = ({
|
||||
@ -20,6 +21,7 @@ const DatasetInfo: FC<Props> = ({
|
||||
description,
|
||||
isExternal,
|
||||
expand,
|
||||
extraInfo,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
@ -36,6 +38,7 @@ const DatasetInfo: FC<Props> = ({
|
||||
<div className='my-3 system-xs-regular text-text-tertiary'>{description}</div>
|
||||
</div>
|
||||
)}
|
||||
{extraInfo}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ const AppDetailNav = ({ title, desc, isExternal, icon, icon_background, navigati
|
||||
description={desc}
|
||||
isExternal={isExternal}
|
||||
expand={expand}
|
||||
extraInfo={extraInfo && extraInfo(appSidebarExpand)}
|
||||
/>
|
||||
)}
|
||||
{!['app', 'dataset'].includes(iconType) && (
|
||||
@ -99,7 +100,6 @@ const AppDetailNav = ({ title, desc, isExternal, icon, icon_background, navigati
|
||||
<NavLink key={index} mode={appSidebarExpand} iconMap={{ selected: item.selectedIcon, normal: item.icon }} name={item.name} href={item.href} />
|
||||
)
|
||||
})}
|
||||
{extraInfo && extraInfo(appSidebarExpand)}
|
||||
</nav>
|
||||
{
|
||||
!isMobile && (
|
||||
|
62
web/app/components/base/linked-apps-panel/index.tsx
Normal file
62
web/app/components/base/linked-apps-panel/index.tsx
Normal file
@ -0,0 +1,62 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiArrowRightUpLine } from '@remixicon/react'
|
||||
import cn from '@/utils/classnames'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import type { RelatedApp } from '@/models/datasets'
|
||||
|
||||
type ILikedItemProps = {
|
||||
appStatus?: boolean
|
||||
detail: RelatedApp
|
||||
isMobile: boolean
|
||||
}
|
||||
|
||||
const appTypeMap = {
|
||||
'chat': 'Chatbot',
|
||||
'completion': 'Completion',
|
||||
'agent-chat': 'Agent',
|
||||
'advanced-chat': 'Chatflow',
|
||||
'workflow': 'Workflow',
|
||||
}
|
||||
|
||||
const LikedItem = ({
|
||||
detail,
|
||||
isMobile,
|
||||
}: ILikedItemProps) => {
|
||||
return (
|
||||
<Link className={cn('group/link-item flex items-center justify-between w-full h-8 rounded-lg hover:bg-state-base-hover cursor-pointer px-2', isMobile && 'justify-center')} href={`/app/${detail?.id}/overview`}>
|
||||
<div className='flex items-center'>
|
||||
<div className={cn('relative w-6 h-6 rounded-md')}>
|
||||
<AppIcon size='tiny' iconType={detail.icon_type} icon={detail.icon} background={detail.icon_background} imageUrl={detail.icon_url} />
|
||||
</div>
|
||||
{!isMobile && <div className={cn(' ml-2 truncate system-sm-medium text-text-primary')}>{detail?.name || '--'}</div>}
|
||||
</div>
|
||||
<div className='group-hover/link-item:hidden shrink-0 system-2xs-medium-uppercase text-text-tertiary'>{appTypeMap[detail.mode]}</div>
|
||||
<RiArrowRightUpLine className='hidden group-hover/link-item:block w-4 h-4 text-text-tertiary' />
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
type Props = {
|
||||
relatedApps: RelatedApp[]
|
||||
isMobile: boolean
|
||||
}
|
||||
|
||||
const LinkedAppsPanel: FC<Props> = ({
|
||||
relatedApps,
|
||||
isMobile,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div className='p-1 w-[320px] bg-components-panel-bg-blur border-[0.5px] border-components-panel-border shadow-lg rounded-xl backdrop-blur-[5px]'>
|
||||
<div className='mt-1 mb-0.5 pl-2 system-xs-medium-uppercase text-text-tertiary'>{relatedApps.length || '--'} {t('common.datasetMenus.relatedApp')}</div>
|
||||
{relatedApps.map((item, index) => (
|
||||
<LikedItem key={index} detail={item} isMobile={isMobile} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(LinkedAppsPanel)
|
@ -14,6 +14,7 @@ export type TooltipProps = {
|
||||
popupContent?: React.ReactNode
|
||||
children?: React.ReactNode
|
||||
popupClassName?: string
|
||||
noDecoration?: boolean
|
||||
offset?: OffsetOptions
|
||||
needsDelay?: boolean
|
||||
asChild?: boolean
|
||||
@ -27,6 +28,7 @@ const Tooltip: FC<TooltipProps> = ({
|
||||
popupContent,
|
||||
children,
|
||||
popupClassName,
|
||||
noDecoration,
|
||||
offset,
|
||||
asChild = true,
|
||||
needsDelay = false,
|
||||
@ -96,7 +98,7 @@ const Tooltip: FC<TooltipProps> = ({
|
||||
>
|
||||
{popupContent && (<div
|
||||
className={cn(
|
||||
'relative px-3 py-2 text-xs font-normal text-text-secondary bg-components-tooltip-bg rounded-md shadow-lg break-words',
|
||||
!noDecoration && 'relative px-3 py-2 text-xs font-normal text-text-secondary bg-components-tooltip-bg rounded-md shadow-lg break-words',
|
||||
popupClassName,
|
||||
)}
|
||||
onMouseEnter={() => triggerMethod === 'hover' && setHoverPopup()}
|
||||
|
Loading…
Reference in New Issue
Block a user