From 060a894bd122b4ce88e082efaa1005c9b05947df Mon Sep 17 00:00:00 2001 From: JzoNg Date: Sat, 12 Oct 2024 12:35:56 +0800 Subject: [PATCH] interaction of plugin detail panel --- .../plugins/test/card/actions.ts | 5 + .../(commonLayout)/plugins/test/card/page.tsx | 4 + web/app/components/plugins/card/card-mock.ts | 3 + .../plugins/plugin-detail-panel/index.tsx | 109 ++++++++++++++++++ web/app/components/plugins/provider-card.tsx | 77 +++++++------ web/app/components/plugins/types.ts | 1 + 6 files changed, 162 insertions(+), 37 deletions(-) create mode 100644 web/app/components/plugins/plugin-detail-panel/index.tsx diff --git a/web/app/(commonLayout)/plugins/test/card/actions.ts b/web/app/(commonLayout)/plugins/test/card/actions.ts index 42c335ea87..251d00d272 100644 --- a/web/app/(commonLayout)/plugins/test/card/actions.ts +++ b/web/app/(commonLayout)/plugins/test/card/actions.ts @@ -7,3 +7,8 @@ export async function handleDelete() { // revalidatePath only invalidates the cache when the included path is next visited. revalidatePath('/') } + +export async function fetchPluginDetail(id: string) { + // Fetch plugin detail TODO + return { id } +} diff --git a/web/app/(commonLayout)/plugins/test/card/page.tsx b/web/app/(commonLayout)/plugins/test/card/page.tsx index 7621404dba..81ac3e4ea6 100644 --- a/web/app/(commonLayout)/plugins/test/card/page.tsx +++ b/web/app/(commonLayout)/plugins/test/card/page.tsx @@ -5,6 +5,7 @@ import { extensionDallE, modelGPT4, toolNotion } from '@/app/components/plugins/ import PluginItem from '@/app/components/plugins/plugin-item' import CardMoreInfo from '@/app/components/plugins/card/card-more-info' import ProviderCard from '@/app/components/plugins/provider-card' +import PluginDetailPanel from '@/app/components/plugins/plugin-detail-panel' import { getLocaleOnServer, useTranslation as translate } from '@/i18n/server' import Badge from '@/app/components/base/badge' const PluginList = async () => { @@ -72,6 +73,9 @@ const PluginList = async () => { ))} + ) } diff --git a/web/app/components/plugins/card/card-mock.ts b/web/app/components/plugins/card/card-mock.ts index d411288db7..f653e711dd 100644 --- a/web/app/components/plugins/card/card-mock.ts +++ b/web/app/components/plugins/card/card-mock.ts @@ -1,6 +1,7 @@ import { PluginType } from '../types' export const toolNotion = { + id: 'tool-notion', type: PluginType.tool, org: 'Notion', name: 'notion page search', @@ -18,6 +19,7 @@ export const toolNotion = { } export const extensionDallE = { + id: 'extension-dalle', type: PluginType.extension, org: 'OpenAI', name: 'DALL-E', @@ -36,6 +38,7 @@ export const extensionDallE = { } export const modelGPT4 = { + id: 'model-gpt4', type: PluginType.model, org: 'OpenAI', name: 'GPT-4', diff --git a/web/app/components/plugins/plugin-detail-panel/index.tsx b/web/app/components/plugins/plugin-detail-panel/index.tsx new file mode 100644 index 0000000000..14c47f3b01 --- /dev/null +++ b/web/app/components/plugins/plugin-detail-panel/index.tsx @@ -0,0 +1,109 @@ +'use client' +import React, { useEffect, useState } from 'react' +import type { FC } from 'react' +import { usePathname, useRouter, useSearchParams } from 'next/navigation' +import { RiVerifiedBadgeLine } from '@remixicon/react' +import Badge from '../../base/badge' +import type { Plugin } from '../types' +import Description from '../card/base/description' +import Icon from '../card/base/card-icon' +import Title from '../card/base/title' +import DownloadCount from '../card/base/download-count' +import type { Locale } from '@/i18n' +import { fetchPluginDetail } from '@/app/(commonLayout)/plugins/test/card/actions' +import Drawer from '@/app/components/base/drawer' +import Loading from '@/app/components/base/loading' +import cn from '@/utils/classnames' +import { + // extensionDallE, + // modelGPT4, + toolNotion, +} from '@/app/components/plugins/card/card-mock' + +type Props = { + locale: Locale // The component is used in both client and server side, so we can't get the locale from both side(getLocaleOnServer and useContext) +} + +const PluginDetailPanel: FC = ({ + locale, +}) => { + const searchParams = useSearchParams() + const pluginID = searchParams.get('pluginID') + const router = useRouter() + const pathname = usePathname() + const [loading, setLoading] = useState(true) + const [pluginDetail, setPluginDetail] = useState() + + const getPluginDetail = async (pluginID: string) => { + setLoading(true) + const detail = await fetchPluginDetail(pluginID) + setPluginDetail({ + ...detail, + ...toolNotion, + } as any) + setLoading(false) + } + + const handleClose = () => { + setPluginDetail(undefined) + router.replace(pathname) + } + + useEffect(() => { + if (pluginID) + getPluginDetail(pluginID) + }, [pluginID]) + + if (!pluginID) + return null + + return ( + + {loading && } + {!loading && pluginDetail && ( +
+
+ {/* Header */} +
+ +
+
+ + <RiVerifiedBadgeLine className="shrink-0 ml-0.5 w-4 h-4 text-text-accent" /> + </div> + <div className='mb-1 flex justify-between items-center h-4'> + <div className='flex items-center'> + <div className='text-text-tertiary system-xs-regular'>{pluginDetail.org}</div> + <div className='mx-2 text-text-quaternary system-xs-regular'>·</div> + <DownloadCount downloadCount={pluginDetail.install_count || 0} /> + </div> + </div> + </div> + </div> + <Description className='mt-3' text={pluginDetail.brief[locale]} descriptionLineRows={2}></Description> + <div className='mt-3 flex space-x-0.5'> + {['LLM', 'text embedding', 'speech2text'].map(tag => ( + <Badge key={tag} text={tag} /> + ))} + </div> + </div> + </div> + )} + </Drawer> + ) +} + +export default PluginDetailPanel diff --git a/web/app/components/plugins/provider-card.tsx b/web/app/components/plugins/provider-card.tsx index 4892d9da29..7ee0b55e86 100644 --- a/web/app/components/plugins/provider-card.tsx +++ b/web/app/components/plugins/provider-card.tsx @@ -1,5 +1,6 @@ import React from 'react' import type { FC } from 'react' +import Link from 'next/link' import { RiArrowRightUpLine, RiVerifiedBadgeLine } from '@remixicon/react' import Badge from '../base/badge' import type { Plugin } from './types' @@ -26,47 +27,49 @@ const ProviderCard: FC<Props> = ({ return ( <div className={cn('group relative p-4 pb-3 border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg hover-bg-components-panel-on-panel-item-bg rounded-xl shadow-xs', className)}> - {/* Header */} - <div className="flex"> - <Icon src={payload.icon} /> - <div className="ml-3 w-0 grow"> - <div className="flex items-center h-5"> - <Title title={label[locale]} /> - <RiVerifiedBadgeLine className="shrink-0 ml-0.5 w-4 h-4 text-text-accent" /> - </div> - <div className='mb-1 flex justify-between items-center h-4'> - <div className='flex items-center'> - <div className='text-text-tertiary system-xs-regular'>{org}</div> - <div className='mx-2 text-text-quaternary system-xs-regular'>·</div> - <DownloadCount downloadCount={payload.install_count || 0} /> + <Link href={`/plugins/test/card?pluginID=${payload.id}`}> + {/* Header */} + <div className="flex"> + <Icon src={payload.icon} /> + <div className="ml-3 w-0 grow"> + <div className="flex items-center h-5"> + <Title title={label[locale]} /> + <RiVerifiedBadgeLine className="shrink-0 ml-0.5 w-4 h-4 text-text-accent" /> + </div> + <div className='mb-1 flex justify-between items-center h-4'> + <div className='flex items-center'> + <div className='text-text-tertiary system-xs-regular'>{org}</div> + <div className='mx-2 text-text-quaternary system-xs-regular'>·</div> + <DownloadCount downloadCount={payload.install_count || 0} /> + </div> </div> </div> </div> - </div> - <Description className='mt-3' text={payload.brief[locale]} descriptionLineRows={2}></Description> - <div className='mt-3 flex space-x-0.5'> - {['LLM', 'text embedding', 'speech2text'].map(tag => ( - <Badge key={tag} text={tag} /> - ))} - </div> - <div - className='hidden group-hover:flex items-center gap-2 absolute bottom-0 left-0 right-0 p-4 pt-8' - style={{ background: 'linear-gradient(0deg, #F9FAFB 60.27%, rgba(249, 250, 251, 0.00) 100%)' }} - > - <Button - className='flex-grow' - variant='primary' + <Description className='mt-3' text={payload.brief[locale]} descriptionLineRows={2}></Description> + <div className='mt-3 flex space-x-0.5'> + {['LLM', 'text embedding', 'speech2text'].map(tag => ( + <Badge key={tag} text={tag} /> + ))} + </div> + <div + className='hidden group-hover:flex items-center gap-2 absolute bottom-0 left-0 right-0 p-4 pt-8' + style={{ background: 'linear-gradient(0deg, #F9FAFB 60.27%, rgba(249, 250, 251, 0.00) 100%)' }} > - Install - </Button> - <Button - className='flex-grow' - variant='secondary' - > - Details - <RiArrowRightUpLine className='w-4 h-4' /> - </Button> - </div> + <Button + className='flex-grow' + variant='primary' + > + Install + </Button> + <Button + className='flex-grow' + variant='secondary' + > + Details + <RiArrowRightUpLine className='w-4 h-4' /> + </Button> + </div> + </Link> </div> ) } diff --git a/web/app/components/plugins/types.ts b/web/app/components/plugins/types.ts index 3b8fdd12f2..0c2b467326 100644 --- a/web/app/components/plugins/types.ts +++ b/web/app/components/plugins/types.ts @@ -8,6 +8,7 @@ export enum PluginType { } export type Plugin = { + id: string 'type': PluginType 'org': string 'name': string