feat: linked app new ui

This commit is contained in:
Joel 2024-12-10 16:12:57 +08:00
parent 77185d9617
commit dd23f1093b
6 changed files with 100 additions and 65 deletions

View File

@ -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}

View File

@ -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;
}

View File

@ -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>
)
}

View File

@ -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 && (

View 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)

View File

@ -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()}