From 36800eeaba3084ccd76856e1233c91d4366375f7 Mon Sep 17 00:00:00 2001 From: Joel Date: Wed, 9 Oct 2024 17:50:23 +0800 Subject: [PATCH] feat: base card component --- .../(commonLayout)/plugins/test/card/page.tsx | 10 +- .../assets/vender/plugin/left-corner.svg | 3 + .../files/{Unknown.json => Unknow.json} | 2 +- .../public/files/{Unknown.tsx => Unknow.tsx} | 4 +- .../base/icons/src/public/files/index.ts | 2 +- .../icons/src/vender/plugin/LeftCorner.json | 27 ++++ .../CuteRobot.tsx => plugin/LeftCorner.tsx} | 4 +- .../base/icons/src/vender/plugin/index.ts | 1 + .../{CuteRobot.json => CuteRobote.json} | 2 +- .../vender/solid/communication/CuteRobote.tsx | 16 ++ .../src/vender/solid/communication/index.ts | 2 +- web/app/components/plugins/card-mock.ts | 49 ++++++ web/app/components/plugins/card.tsx | 144 ++++++++++++++++-- web/app/components/plugins/types.ts | 15 +- 14 files changed, 247 insertions(+), 34 deletions(-) create mode 100644 web/app/components/base/icons/assets/vender/plugin/left-corner.svg rename web/app/components/base/icons/src/public/files/{Unknown.json => Unknow.json} (99%) rename web/app/components/base/icons/src/public/files/{Unknown.tsx => Unknow.tsx} (87%) create mode 100644 web/app/components/base/icons/src/vender/plugin/LeftCorner.json rename web/app/components/base/icons/src/vender/{solid/communication/CuteRobot.tsx => plugin/LeftCorner.tsx} (86%) create mode 100644 web/app/components/base/icons/src/vender/plugin/index.ts rename web/app/components/base/icons/src/vender/solid/communication/{CuteRobot.json => CuteRobote.json} (98%) create mode 100644 web/app/components/base/icons/src/vender/solid/communication/CuteRobote.tsx create mode 100644 web/app/components/plugins/card-mock.ts diff --git a/web/app/(commonLayout)/plugins/test/card/page.tsx b/web/app/(commonLayout)/plugins/test/card/page.tsx index 4c849c41ef..ac85164412 100644 --- a/web/app/(commonLayout)/plugins/test/card/page.tsx +++ b/web/app/(commonLayout)/plugins/test/card/page.tsx @@ -1,8 +1,16 @@ import Card from '@/app/components/plugins/card' +import { extensionDallE, modelGPT4, toolNotion } from '@/app/components/plugins/card-mock' + const PluginList = async () => { return ( <> - +
+ + + + + +
) } diff --git a/web/app/components/base/icons/assets/vender/plugin/left-corner.svg b/web/app/components/base/icons/assets/vender/plugin/left-corner.svg new file mode 100644 index 0000000000..9b360e4be7 --- /dev/null +++ b/web/app/components/base/icons/assets/vender/plugin/left-corner.svg @@ -0,0 +1,3 @@ + + + diff --git a/web/app/components/base/icons/src/public/files/Unknown.json b/web/app/components/base/icons/src/public/files/Unknow.json similarity index 99% rename from web/app/components/base/icons/src/public/files/Unknown.json rename to web/app/components/base/icons/src/public/files/Unknow.json index c39df990d0..33067fa96f 100644 --- a/web/app/components/base/icons/src/public/files/Unknown.json +++ b/web/app/components/base/icons/src/public/files/Unknow.json @@ -195,5 +195,5 @@ } ] }, - "name": "Unknown" + "name": "Unknow" } \ No newline at end of file diff --git a/web/app/components/base/icons/src/public/files/Unknown.tsx b/web/app/components/base/icons/src/public/files/Unknow.tsx similarity index 87% rename from web/app/components/base/icons/src/public/files/Unknown.tsx rename to web/app/components/base/icons/src/public/files/Unknow.tsx index de909ed65e..ce84d344bf 100644 --- a/web/app/components/base/icons/src/public/files/Unknown.tsx +++ b/web/app/components/base/icons/src/public/files/Unknow.tsx @@ -2,7 +2,7 @@ // DON NOT EDIT IT MANUALLY import * as React from 'react' -import data from './Unknown.json' +import data from './Unknow.json' import IconBase from '@/app/components/base/icons/IconBase' import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase' @@ -11,6 +11,6 @@ const Icon = React.forwardRef, Omit ) -Icon.displayName = 'Unknown' +Icon.displayName = 'Unknow' export default Icon diff --git a/web/app/components/base/icons/src/public/files/index.ts b/web/app/components/base/icons/src/public/files/index.ts index f38c28cbdb..2814c4ae39 100644 --- a/web/app/components/base/icons/src/public/files/index.ts +++ b/web/app/components/base/icons/src/public/files/index.ts @@ -6,6 +6,6 @@ export { default as Json } from './Json' export { default as Md } from './Md' export { default as Pdf } from './Pdf' export { default as Txt } from './Txt' -export { default as Unknown } from './Unknown' +export { default as Unknow } from './Unknow' export { default as Xlsx } from './Xlsx' export { default as Yaml } from './Yaml' diff --git a/web/app/components/base/icons/src/vender/plugin/LeftCorner.json b/web/app/components/base/icons/src/vender/plugin/LeftCorner.json new file mode 100644 index 0000000000..d4cd0cd0ec --- /dev/null +++ b/web/app/components/base/icons/src/vender/plugin/LeftCorner.json @@ -0,0 +1,27 @@ +{ + "icon": { + "type": "element", + "isRootNode": true, + "name": "svg", + "attributes": { + "width": "13", + "height": "20", + "viewBox": "0 0 13 20", + "fill": "none", + "xmlns": "http://www.w3.org/2000/svg" + }, + "children": [ + { + "type": "element", + "name": "path", + "attributes": { + "id": "Shape", + "d": "M0 0H13V20C9.98017 20 7.26458 18.1615 6.14305 15.3576L0 0Z", + "fill": "currentColor" + }, + "children": [] + } + ] + }, + "name": "LeftCorner" +} \ No newline at end of file diff --git a/web/app/components/base/icons/src/vender/solid/communication/CuteRobot.tsx b/web/app/components/base/icons/src/vender/plugin/LeftCorner.tsx similarity index 86% rename from web/app/components/base/icons/src/vender/solid/communication/CuteRobot.tsx rename to web/app/components/base/icons/src/vender/plugin/LeftCorner.tsx index 49994048b7..dd215b9bf7 100644 --- a/web/app/components/base/icons/src/vender/solid/communication/CuteRobot.tsx +++ b/web/app/components/base/icons/src/vender/plugin/LeftCorner.tsx @@ -2,7 +2,7 @@ // DON NOT EDIT IT MANUALLY import * as React from 'react' -import data from './CuteRobot.json' +import data from './LeftCorner.json' import IconBase from '@/app/components/base/icons/IconBase' import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase' @@ -11,6 +11,6 @@ const Icon = React.forwardRef, Omit ) -Icon.displayName = 'CuteRobot' +Icon.displayName = 'LeftCorner' export default Icon diff --git a/web/app/components/base/icons/src/vender/plugin/index.ts b/web/app/components/base/icons/src/vender/plugin/index.ts new file mode 100644 index 0000000000..6d219fce21 --- /dev/null +++ b/web/app/components/base/icons/src/vender/plugin/index.ts @@ -0,0 +1 @@ +export { default as LeftCorner } from './LeftCorner' diff --git a/web/app/components/base/icons/src/vender/solid/communication/CuteRobot.json b/web/app/components/base/icons/src/vender/solid/communication/CuteRobote.json similarity index 98% rename from web/app/components/base/icons/src/vender/solid/communication/CuteRobot.json rename to web/app/components/base/icons/src/vender/solid/communication/CuteRobote.json index 5b36575f56..389d044a9b 100644 --- a/web/app/components/base/icons/src/vender/solid/communication/CuteRobot.json +++ b/web/app/components/base/icons/src/vender/solid/communication/CuteRobote.json @@ -34,5 +34,5 @@ } ] }, - "name": "CuteRobot" + "name": "CuteRobote" } \ No newline at end of file diff --git a/web/app/components/base/icons/src/vender/solid/communication/CuteRobote.tsx b/web/app/components/base/icons/src/vender/solid/communication/CuteRobote.tsx new file mode 100644 index 0000000000..d416fb5b66 --- /dev/null +++ b/web/app/components/base/icons/src/vender/solid/communication/CuteRobote.tsx @@ -0,0 +1,16 @@ +// GENERATE BY script +// DON NOT EDIT IT MANUALLY + +import * as React from 'react' +import data from './CuteRobote.json' +import IconBase from '@/app/components/base/icons/IconBase' +import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase' + +const Icon = React.forwardRef, Omit>(( + props, + ref, +) => ) + +Icon.displayName = 'CuteRobote' + +export default Icon diff --git a/web/app/components/base/icons/src/vender/solid/communication/index.ts b/web/app/components/base/icons/src/vender/solid/communication/index.ts index 673de27463..854953c116 100644 --- a/web/app/components/base/icons/src/vender/solid/communication/index.ts +++ b/web/app/components/base/icons/src/vender/solid/communication/index.ts @@ -1,6 +1,6 @@ export { default as AiText } from './AiText' export { default as ChatBot } from './ChatBot' -export { default as CuteRobot } from './CuteRobot' +export { default as CuteRobote } from './CuteRobote' export { default as EditList } from './EditList' export { default as MessageDotsCircle } from './MessageDotsCircle' export { default as MessageFast } from './MessageFast' diff --git a/web/app/components/plugins/card-mock.ts b/web/app/components/plugins/card-mock.ts new file mode 100644 index 0000000000..f054afb855 --- /dev/null +++ b/web/app/components/plugins/card-mock.ts @@ -0,0 +1,49 @@ +import { PluginType } from './types' + +export const toolNotion = { + type: PluginType.tool, + org: 'Notion', + name: 'notion page search', + latest_version: '1.0.0', + icon: 'https://via.placeholder.com/150', + label: { + 'en-US': 'Notion Page Search', + 'zh-Hans': 'Notion 页面搜索', + }, + brief: { + 'en-US': 'Description: Search Notion pages and open visited ones faster. No admin access required.', + 'zh-Hans': '搜索 Notion 页面并更快地打开已访问的页面。无需管理员访问权限。', + }, +} + +export const extensionDallE = { + type: PluginType.extension, + org: 'OpenAI', + name: 'DALL-E', + latest_version: '1.0.0', + icon: 'https://via.placeholder.com/150', + label: { + 'en-US': 'DALL-E', + 'zh-Hans': 'DALL-E', + }, + brief: { + 'en-US': 'Description: A simple plugin to use OpenAI DALL-E model.', + 'zh-Hans': '一个使用 OpenAI DALL-E 模型的简单插件。', + }, +} + +export const modelGPT4 = { + type: PluginType.model, + org: 'OpenAI', + name: 'GPT-4', + latest_version: '1.0.0', + icon: 'https://via.placeholder.com/150', + label: { + 'en-US': 'GPT-4', + 'zh-Hans': 'GPT-4', + }, + brief: { + 'en-US': 'Description: A simple plugin to use OpenAI GPT-4 model.', + 'zh-Hans': '一个使用 OpenAI GPT-4 模型的简单插件。', + }, +} diff --git a/web/app/components/plugins/card.tsx b/web/app/components/plugins/card.tsx index b7710a2ba5..30885961ea 100644 --- a/web/app/components/plugins/card.tsx +++ b/web/app/components/plugins/card.tsx @@ -1,22 +1,136 @@ -const Card = () => { +import React, { useMemo } from 'react' +import { RiVerifiedBadgeLine } from '@remixicon/react' +import type { FC } from 'react' +import { LeftCorner } from '../base/icons/src/vender/plugin' +import type { Plugin } from './types' +import { getLocaleOnServer } from '@/i18n/server' +import cn from '@/utils/classnames' + +export const CornerMark = ({ text }: { text: string }) => { return ( -
-
-
-
-
- Notion Page Search -
-
- Notion -
/
- notion-page-search +
+ +
{text}
+
+ ) +} + +export const Icon = ({ + className, + src, + installed = false, +}: { + className?: string + src: string + installed?: boolean +}) => { + return ( +
+
+ ) +} + +export const Title = ({ + title, +}: { + title: string +}) => { + return ( +
+ {title} +
+ ) +} + +export const OrgInfo = ({ + className, + orgName, + packageName, +}: { + className?: string + orgName: string + packageName: string +}) => { + return
+ {orgName} + / + {packageName} +
+} + +type DescriptionProps = { + className?: string + text: string + descriptionLineRows: number +} + +const Description: FC = ({ + className, + text, + descriptionLineRows, +}) => { + const lineClassName = useMemo(() => { + if (descriptionLineRows === 1) + return 'truncate' + else if (descriptionLineRows === 2) + return 'line-clamp-2' + else + return 'line-clamp-3' + }, [descriptionLineRows]) + return ( +
+ {text} +
+ ) +} + +type Props = { + className?: string + payload: Plugin + installed?: boolean + descriptionLineRows?: number + footer?: React.ReactNode +} + +const Card = ({ + className, + payload, + installed, + descriptionLineRows = 2, + footer, +}: Props) => { + const locale = getLocaleOnServer() + + const { type, name, org, label } = payload + return ( +
+ + {/* Header */} +
+ +
+
+ + <RiVerifiedBadgeLine className="shrink-0 ml-0.5 w-4 h-4 text-text-accent" /> </div> + <OrgInfo + className="mt-0.5" + orgName={org} + packageName={name} + /> </div> </div> - <div className='px-4 pt-1 pb-2 system-xs-regular text-text-tertiary'> - Search Notion pages and open visited ones faster. No admin access required. - </div> + <Description + className="mt-3" + text={payload.brief[locale]} + descriptionLineRows={descriptionLineRows} + /> + {footer && <div>{footer}</div>} </div> ) } diff --git a/web/app/components/plugins/types.ts b/web/app/components/plugins/types.ts index 47eb6331c0..f9b1add725 100644 --- a/web/app/components/plugins/types.ts +++ b/web/app/components/plugins/types.ts @@ -1,9 +1,10 @@ import type { CredentialFormSchemaBase } from '../header/account-setting/model-provider-page/declarations' +import type { Locale } from '@/i18n' export enum PluginType { - plugin = 'plugin', + tool = 'tool', model = 'model', - extension = 'Extension', + extension = 'extension', } export type Plugin = { @@ -12,14 +13,8 @@ export type Plugin = { 'name': string 'latest_version': string 'icon': string - 'label': { - 'en_US': string - 'zh_Hans': string - } - 'brief': { - 'en_US': string - 'zh_Hans': string - } + 'label': Record<Locale, string> + 'brief': Record<Locale, string> // Repo readme.md content 'introduction': string 'repository': string