From 4b58b549c64c1376b07ecfbb55a4b623fb953257 Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 31 May 2024 14:44:26 +0800 Subject: [PATCH 01/31] chore: notion source to common component --- .../data-source-notion/index.tsx | 149 ++++++------------ .../data-source-notion/operate/index.tsx | 14 +- .../data-source-page/panel/config-item.tsx | 78 +++++++++ .../data-source-page/panel/index.tsx | 113 +++++++++++++ .../style.module.css | 0 .../data-source-page/panel/types.ts | 4 + 6 files changed, 251 insertions(+), 107 deletions(-) create mode 100644 web/app/components/header/account-setting/data-source-page/panel/config-item.tsx create mode 100644 web/app/components/header/account-setting/data-source-page/panel/index.tsx rename web/app/components/header/account-setting/data-source-page/{data-source-notion => panel}/style.module.css (100%) create mode 100644 web/app/components/header/account-setting/data-source-page/panel/types.ts diff --git a/web/app/components/header/account-setting/data-source-page/data-source-notion/index.tsx b/web/app/components/header/account-setting/data-source-page/data-source-notion/index.tsx index 8f72421451eae5..4929b2b403fcb3 100644 --- a/web/app/components/header/account-setting/data-source-page/data-source-notion/index.tsx +++ b/web/app/components/header/account-setting/data-source-page/data-source-notion/index.tsx @@ -1,23 +1,34 @@ -import { useEffect, useState } from 'react' +'use client' +import type { FC } from 'react' +import React, { useEffect, useState } from 'react' import useSWR from 'swr' -import { useTranslation } from 'react-i18next' -import { PlusIcon } from '@heroicons/react/24/solid' -import cn from 'classnames' -import Indicator from '../../../indicator' -import Operate from './operate' -import s from './style.module.css' -import NotionIcon from '@/app/components/base/notion-icon' +import Panel from '../panel' +import { DataSourceType } from '../panel/types' import type { DataSourceNotion as TDataSourceNotion } from '@/models/common' import { useAppContext } from '@/context/app-context' import { fetchNotionConnection } from '@/service/common' +import NotionIcon from '@/app/components/base/notion-icon' -type DataSourceNotionProps = { +const Icon: FC<{ + src: string + name: string + className: string +}> = ({ src, name, className }) => { + return ( + + ) +} +type Props = { workspaces: TDataSourceNotion[] } -const DataSourceNotion = ({ + +const DataSourceNotion: FC = ({ workspaces, -}: DataSourceNotionProps) => { - const { t } = useTranslation() +}) => { const { isCurrentWorkspaceManager } = useAppContext() const [canConnectNotion, setCanConnectNotion] = useState(false) const { data } = useSWR(canConnectNotion ? '/oauth/data-source/notion' : null, fetchNotionConnection) @@ -42,95 +53,31 @@ const DataSourceNotion = ({ if (data?.data) window.location.href = data.data }, [data]) - return ( -
-
-
-
-
- {t('common.dataSource.notion.title')} -
- { - !connected && ( -
- {t('common.dataSource.notion.description')} -
- ) - } -
- { - connected - ? ( -
- {t('common.dataSource.connect')} -
- ) - : ( -
- - {t('common.dataSource.notion.addWorkspace')} -
- ) - } -
- { - connected && ( -
-
- {t('common.dataSource.notion.connectedWorkspace')} -
-
-
- ) - } - { - connected && ( -
- { - workspaces.map(workspace => ( -
- -
{workspace.source_info.workspace_name}
- { - workspace.is_bound - ? - : - } -
- { - workspace.is_bound - ? t('common.dataSource.notion.connected') - : t('common.dataSource.notion.disconnected') - } -
-
- -
- )) - } -
- ) - } -
+ ({ + id: workspace.id, + logo: ({ className }: { className: string }) => ( + ), + name: workspace.source_info.workspace_name, + isActive: workspace.is_bound, + notionConfig: { + total: workspace.source_info.total || 0, + }, + }))} + onRemove={() => { }} // handled in operation/index.tsx + notionActions={{ + onChangeAuthorizedPage: handleAuthAgain, + }} + /> ) } - -export default DataSourceNotion +export default React.memo(DataSourceNotion) diff --git a/web/app/components/header/account-setting/data-source-page/data-source-notion/operate/index.tsx b/web/app/components/header/account-setting/data-source-page/data-source-notion/operate/index.tsx index e115034ff733aa..7b20e5e0a61e13 100644 --- a/web/app/components/header/account-setting/data-source-page/data-source-notion/operate/index.tsx +++ b/web/app/components/header/account-setting/data-source-page/data-source-notion/operate/index.tsx @@ -6,17 +6,19 @@ import { EllipsisHorizontalIcon } from '@heroicons/react/24/solid' import { Menu, Transition } from '@headlessui/react' import { syncDataSourceNotion, updateDataSourceNotionAction } from '@/service/common' import Toast from '@/app/components/base/toast' -import type { DataSourceNotion } from '@/models/common' import { FilePlus02 } from '@/app/components/base/icons/src/vender/line/files' import { RefreshCw05 } from '@/app/components/base/icons/src/vender/line/arrows' import { Trash03 } from '@/app/components/base/icons/src/vender/line/general' type OperateProps = { - workspace: DataSourceNotion + payload: { + id: string + total: number + } onAuthAgain: () => void } export default function Operate({ - workspace, + payload, onAuthAgain, }: OperateProps) { const itemClassName = ` @@ -37,11 +39,11 @@ export default function Operate({ mutate({ url: 'data-source/integrates' }) } const handleSync = async () => { - await syncDataSourceNotion({ url: `/oauth/data-source/notion/${workspace.id}/sync` }) + await syncDataSourceNotion({ url: `/oauth/data-source/notion/${payload.id}/sync` }) updateIntegrates() } const handleRemove = async () => { - await updateDataSourceNotionAction({ url: `/data-source/integrates/${workspace.id}/disable` }) + await updateDataSourceNotionAction({ url: `/data-source/integrates/${payload.id}/disable` }) updateIntegrates() } @@ -79,7 +81,7 @@ export default function Operate({
{t('common.dataSource.notion.changeAuthorizedPages')}
- {workspace.source_info.total} {t('common.dataSource.notion.pagesAuthorized')} + {payload.total} {t('common.dataSource.notion.pagesAuthorized')}
diff --git a/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx b/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx new file mode 100644 index 00000000000000..a776455432410d --- /dev/null +++ b/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx @@ -0,0 +1,78 @@ +'use client' +import type { FC } from 'react' +import React from 'react' +import { useTranslation } from 'react-i18next' +import cn from 'classnames' +import Indicator from '../../../indicator' +import Operate from '../data-source-notion/operate' +import { DataSourceType } from './types' +import s from './style.module.css' +import { Trash03 } from '@/app/components/base/icons/src/vender/line/general' + +export type ConfigItemType = { + id: string + logo: any + name: string + isActive: boolean + notionConfig?: { + total: number + } +} + +type Props = { + type: DataSourceType + payload: ConfigItemType + onRemove: () => void + notionActions?: { + onChangeAuthorizedPage: () => void + } +} + +const ConfigItem: FC = ({ + type, + payload, + onRemove, + notionActions, +}) => { + const { t } = useTranslation() + const isNotion = type === DataSourceType.notion + const isWebsite = type === DataSourceType.website + const onChangeAuthorizedPage = notionActions?.onChangeAuthorizedPage || function () { } + + return ( +
+ +
{payload.name}
+ { + payload.isActive + ? + : + } +
+ { + payload.isActive + ? t('common.dataSource.notion.connected') + : t('common.dataSource.notion.disconnected') + } +
+
+ {isNotion && ( + + )} + + { + isWebsite && ( +
+ +
+ ) + } + +
+ ) +} +export default React.memo(ConfigItem) diff --git a/web/app/components/header/account-setting/data-source-page/panel/index.tsx b/web/app/components/header/account-setting/data-source-page/panel/index.tsx new file mode 100644 index 00000000000000..37d87773670672 --- /dev/null +++ b/web/app/components/header/account-setting/data-source-page/panel/index.tsx @@ -0,0 +1,113 @@ +'use client' +import type { FC } from 'react' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { PlusIcon } from '@heroicons/react/24/solid' +import cn from 'classnames' +import type { ConfigItemType } from './config-item' +import ConfigItem from './config-item' + +import s from './style.module.css' +import type { DataSourceType } from './types' + +type Props = { + type: DataSourceType + isConfigured: boolean + onConfigure: () => void + readonly: boolean + isSupportList?: boolean + configuredList: ConfigItemType[] + onRemove: () => void + notionActions?: { + onChangeAuthorizedPage: () => void + } +} + +const Panel: FC = ({ + type, + isConfigured, + onConfigure, + readonly, + configuredList, + isSupportList, + onRemove, + notionActions, +}) => { + const { t } = useTranslation() + + return ( +
+
+
+
+
+ {t(`common.dataSource.${type}.title`)} +
+ { + !isConfigured && ( +
+ {t(`common.dataSource.${type}.description`)} +
+ ) + } +
+ { + isConfigured + ? ( +
+ {t('common.dataSource.connect')} +
+ ) + : ( + <> + {isSupportList &&
+ + {t('common.dataSource.notion.addWorkspace')} +
} + + ) + } +
+ { + configuredList && ( +
+
+ {t('common.dataSource.notion.connectedWorkspace')} +
+
+
+ ) + } + { + isConfigured && ( +
+ { + configuredList.map(item => ( + + )) + } +
+ ) + } +
+ ) +} +export default React.memo(Panel) diff --git a/web/app/components/header/account-setting/data-source-page/data-source-notion/style.module.css b/web/app/components/header/account-setting/data-source-page/panel/style.module.css similarity index 100% rename from web/app/components/header/account-setting/data-source-page/data-source-notion/style.module.css rename to web/app/components/header/account-setting/data-source-page/panel/style.module.css diff --git a/web/app/components/header/account-setting/data-source-page/panel/types.ts b/web/app/components/header/account-setting/data-source-page/panel/types.ts new file mode 100644 index 00000000000000..345bc10f8121dc --- /dev/null +++ b/web/app/components/header/account-setting/data-source-page/panel/types.ts @@ -0,0 +1,4 @@ +export enum DataSourceType { + notion = 'notion', + website = 'website', +} From ddfac3f4aab9cb8106b5731354ae606f7e040397 Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 31 May 2024 17:35:28 +0800 Subject: [PATCH 02/31] feat: website panel --- web/app/(commonLayout)/apps/Apps.tsx | 16 +++- .../data-source-notion/index.tsx | 1 + .../data-source-website/index.tsx | 80 +++++++++++++++++++ .../data-source-page/index.tsx | 2 + .../data-source-page/panel/index.tsx | 4 +- .../data-source-page/panel/style.module.css | 5 ++ web/i18n/en-US/common.ts | 4 + web/i18n/zh-Hans/common.ts | 4 + web/models/common.ts | 26 ++++++ web/service/common.ts | 5 ++ 10 files changed, 141 insertions(+), 6 deletions(-) create mode 100644 web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx diff --git a/web/app/(commonLayout)/apps/Apps.tsx b/web/app/(commonLayout)/apps/Apps.tsx index 744bb9c9d70981..f4bac7e44bbf7f 100644 --- a/web/app/(commonLayout)/apps/Apps.tsx +++ b/web/app/(commonLayout)/apps/Apps.tsx @@ -24,6 +24,7 @@ import SearchInput from '@/app/components/base/search-input' import { useStore as useTagStore } from '@/app/components/base/tag-management/store' import TagManagementModal from '@/app/components/base/tag-management' import TagFilter from '@/app/components/base/tag-management/filter' +import { useModalContext } from '@/context/modal-context' const getKey = ( pageIndex: number, @@ -49,6 +50,13 @@ const getKey = ( } const Apps = () => { + const { setShowAccountSettingModal } = useModalContext() + useEffect(() => { + setShowAccountSettingModal({ + payload: 'data-source', + }) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) const { t } = useTranslation() const { isCurrentWorkspaceManager } = useAppContext() const showTagManagementModal = useTagStore(s => s.showTagManagementModal) @@ -73,10 +81,10 @@ const Apps = () => { const anchorRef = useRef(null) const options = [ - { value: 'all', text: t('app.types.all'), icon: }, - { value: 'chat', text: t('app.types.chatbot'), icon: }, - { value: 'agent-chat', text: t('app.types.agent'), icon: }, - { value: 'workflow', text: t('app.types.workflow'), icon: }, + { value: 'all', text: t('app.types.all'), icon: }, + { value: 'chat', text: t('app.types.chatbot'), icon: }, + { value: 'agent-chat', text: t('app.types.agent'), icon: }, + { value: 'workflow', text: t('app.types.workflow'), icon: }, ] useEffect(() => { diff --git a/web/app/components/header/account-setting/data-source-page/data-source-notion/index.tsx b/web/app/components/header/account-setting/data-source-page/data-source-notion/index.tsx index 4929b2b403fcb3..f5541999a492bc 100644 --- a/web/app/components/header/account-setting/data-source-page/data-source-notion/index.tsx +++ b/web/app/components/header/account-setting/data-source-page/data-source-notion/index.tsx @@ -59,6 +59,7 @@ const DataSourceNotion: FC = ({ isConfigured={connected} onConfigure={handleConnectNotion} readonly={!isCurrentWorkspaceManager} + isSupportList configuredList={workspaces.map(workspace => ({ id: workspace.id, logo: ({ className }: { className: string }) => ( diff --git a/web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx b/web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx new file mode 100644 index 00000000000000..81b5f91a0fa317 --- /dev/null +++ b/web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx @@ -0,0 +1,80 @@ +'use client' +import type { FC } from 'react' +import React, { useCallback, useEffect, useState } from 'react' +import { useBoolean } from 'ahooks' +import Panel from '../panel' +import { DataSourceType } from '../panel/types' +import { fetchWebsiteDataSource } from '@/service/common' +import type { + DataSourceWebsiteItem, +} from '@/models/common' +import { useAppContext } from '@/context/app-context' + +import { + DataSourceCategory, + WebsiteProvider, +} from '@/models/common' + +type Props = {} + +const isUseMock = false +const mockList: DataSourceWebsiteItem[] = [ + { + id: '1', + category: DataSourceCategory.website, + provider: WebsiteProvider.fireCrawl, + credentials: { + auth_type: 'bearer', + config: { + base_url: 'https://xxx', + api_key: '123456', + }, + }, + created_at: 1627584000, + updated_at: 1627584000, + }, +] + +const DataSourceWebsite: FC = () => { + const { isCurrentWorkspaceManager } = useAppContext() + const [list, setList] = useState(isUseMock ? mockList : []) + useEffect(() => { + (async () => { + const { data } = await fetchWebsiteDataSource() + const list = data.settings.filter(item => item.provider === WebsiteProvider.fireCrawl) + + setList(list) + })() + }, []) + + const [isShowConfig, { + setTrue: showConfig, + setFalse: hideConfig, + }] = useBoolean(false) + + const handleRemove = useCallback(() => { + + }, []) + + console.log(list) + + return ( + 0} + onConfigure={showConfig} + readonly={!isCurrentWorkspaceManager} + configuredList={list.map(item => ({ + id: item.id, + logo: ({ className }: { className: string }) => ( +
L
+ ), + name: 'FireCrawl', + isActive: true, + }))} + onRemove={handleRemove} + + /> + ) +} +export default React.memo(DataSourceWebsite) diff --git a/web/app/components/header/account-setting/data-source-page/index.tsx b/web/app/components/header/account-setting/data-source-page/index.tsx index 761d9cbfe3ddef..ede83152b223e1 100644 --- a/web/app/components/header/account-setting/data-source-page/index.tsx +++ b/web/app/components/header/account-setting/data-source-page/index.tsx @@ -1,6 +1,7 @@ import useSWR from 'swr' import { useTranslation } from 'react-i18next' import DataSourceNotion from './data-source-notion' +import DataSourceWebsite from './data-source-website' import { fetchDataSource } from '@/service/common' export default function DataSourcePage() { @@ -12,6 +13,7 @@ export default function DataSourcePage() {
{t('common.dataSource.add')}
+
) } diff --git a/web/app/components/header/account-setting/data-source-page/panel/index.tsx b/web/app/components/header/account-setting/data-source-page/panel/index.tsx index 37d87773670672..fc6657681138a7 100644 --- a/web/app/components/header/account-setting/data-source-page/panel/index.tsx +++ b/web/app/components/header/account-setting/data-source-page/panel/index.tsx @@ -38,7 +38,7 @@ const Panel: FC = ({ return (
-
+
{t(`common.dataSource.${type}.title`)} @@ -82,7 +82,7 @@ const Panel: FC = ({ }
{ - configuredList && ( + isConfigured && (
{t('common.dataSource.notion.connectedWorkspace')} diff --git a/web/app/components/header/account-setting/data-source-page/panel/style.module.css b/web/app/components/header/account-setting/data-source-page/panel/style.module.css index ede323072dad24..a11d4758fbaec8 100644 --- a/web/app/components/header/account-setting/data-source-page/panel/style.module.css +++ b/web/app/components/header/account-setting/data-source-page/panel/style.module.css @@ -3,6 +3,11 @@ background-size: 20px 20px; } +.website-icon { + background: #ffffff url(../../../../datasets/create/assets/web.svg) center center no-repeat; + background-size: 20px 20px; +} + .workspace-item { box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); } diff --git a/web/i18n/en-US/common.ts b/web/i18n/en-US/common.ts index 586ca28df96a51..d0d7398772e5a6 100644 --- a/web/i18n/en-US/common.ts +++ b/web/i18n/en-US/common.ts @@ -357,6 +357,10 @@ const translation = { preview: 'PREVIEW', }, }, + website: { + title: 'Website', + description: 'Import content from websites using web crawler.', + }, }, plugin: { serpapi: { diff --git a/web/i18n/zh-Hans/common.ts b/web/i18n/zh-Hans/common.ts index 0e6feb7cf562c0..d7fc1ab3873685 100644 --- a/web/i18n/zh-Hans/common.ts +++ b/web/i18n/zh-Hans/common.ts @@ -356,6 +356,10 @@ const translation = { addPages: '添加页面', preview: '预览', }, + website: { + title: '网站', + description: '使用网络爬虫从网站导入内容。', + }, }, }, plugin: { diff --git a/web/models/common.ts b/web/models/common.ts index d5ebdc3faa3e10..a8322e2478206f 100644 --- a/web/models/common.ts +++ b/web/models/common.ts @@ -171,6 +171,32 @@ export type DataSourceNotion = { source_info: DataSourceNotionWorkspace } +export enum DataSourceCategory { + website = 'website', +} +export enum WebsiteProvider { + fireCrawl = 'firecrawl', +} + +export type WebsiteCredentials = { + auth_type: 'bearer' + config: { + base_url: string + api_key: string + } +} +export type DataSourceWebsiteItem = { + id: string + category: DataSourceCategory.website + provider: WebsiteProvider + credentials: WebsiteCredentials + created_at: number + updated_at: number +} +export type DataSourceWebsite = { + settings: DataSourceWebsiteItem[] +} + export type GithubRepo = { stargazers_count: number } diff --git a/web/service/common.ts b/web/service/common.ts index d7f7c6c8bcc0b8..8c645b225eb466 100644 --- a/web/service/common.ts +++ b/web/service/common.ts @@ -6,6 +6,7 @@ import type { CodeBasedExtension, CommonResponse, DataSourceNotion, + DataSourceWebsite, FileUploadConfigResponse, ICurrentWorkspace, IWorkspace, @@ -143,6 +144,10 @@ export const updateDataSourceNotionAction: Fetcher(url) } +export const fetchWebsiteDataSource = () => { + return get<{ data: DataSourceWebsite }>('/api-key-auth/data-source') +} + export const fetchPluginProviders: Fetcher = (url) => { return get(url) } From 8f1a662c4abfcf5ed6b57faf56e77c864459922d Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 31 May 2024 18:25:17 +0800 Subject: [PATCH 03/31] feat: with fire crawl label --- .../account-setting/data-source-page/panel/index.tsx | 12 +++++++++--- web/i18n/en-US/common.ts | 1 + web/i18n/zh-Hans/common.ts | 1 + 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/web/app/components/header/account-setting/data-source-page/panel/index.tsx b/web/app/components/header/account-setting/data-source-page/panel/index.tsx index fc6657681138a7..6d6fc144abdb69 100644 --- a/web/app/components/header/account-setting/data-source-page/panel/index.tsx +++ b/web/app/components/header/account-setting/data-source-page/panel/index.tsx @@ -8,7 +8,7 @@ import type { ConfigItemType } from './config-item' import ConfigItem from './config-item' import s from './style.module.css' -import type { DataSourceType } from './types' +import { DataSourceType } from './types' type Props = { type: DataSourceType @@ -34,14 +34,20 @@ const Panel: FC = ({ notionActions, }) => { const { t } = useTranslation() + const isWebsite = type === DataSourceType.website return (
-
- {t(`common.dataSource.${type}.title`)} +
+
{t(`common.dataSource.${type}.title`)}
+ {isWebsite && ( +
+ {t('common.dataSource.website.with')} 🔥 FireCrawl +
+ )}
{ !isConfigured && ( diff --git a/web/i18n/en-US/common.ts b/web/i18n/en-US/common.ts index d0d7398772e5a6..06b7961773b807 100644 --- a/web/i18n/en-US/common.ts +++ b/web/i18n/en-US/common.ts @@ -360,6 +360,7 @@ const translation = { website: { title: 'Website', description: 'Import content from websites using web crawler.', + with: 'With', }, }, plugin: { diff --git a/web/i18n/zh-Hans/common.ts b/web/i18n/zh-Hans/common.ts index d7fc1ab3873685..af67c57e13117c 100644 --- a/web/i18n/zh-Hans/common.ts +++ b/web/i18n/zh-Hans/common.ts @@ -359,6 +359,7 @@ const translation = { website: { title: '网站', description: '使用网络爬虫从网站导入内容。', + with: '使用', }, }, }, From bde44fd2a26a6382997ea95fe838a22dfc8ef890 Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 31 May 2024 18:35:29 +0800 Subject: [PATCH 04/31] fix: not show configure and i18n problem --- .../data-source-page/panel/index.tsx | 71 ++++++++++++------- web/i18n/en-US/common.ts | 1 + web/i18n/zh-Hans/common.ts | 11 +-- 3 files changed, 52 insertions(+), 31 deletions(-) diff --git a/web/app/components/header/account-setting/data-source-page/panel/index.tsx b/web/app/components/header/account-setting/data-source-page/panel/index.tsx index 6d6fc144abdb69..75bef952f8ede0 100644 --- a/web/app/components/header/account-setting/data-source-page/panel/index.tsx +++ b/web/app/components/header/account-setting/data-source-page/panel/index.tsx @@ -34,6 +34,7 @@ const Panel: FC = ({ notionActions, }) => { const { t } = useTranslation() + const isNotion = type === DataSourceType.notion const isWebsite = type === DataSourceType.website return ( @@ -57,35 +58,53 @@ const Panel: FC = ({ ) }
- { - isConfigured - ? ( -
+ { + isConfigured + ? ( +
- {t('common.dataSource.connect')} -
- ) - : ( - <> - {isSupportList &&
+ {t('common.dataSource.configure')} +
+ ) + : ( + <> + {isSupportList &&
- - {t('common.dataSource.notion.addWorkspace')} -
} - - ) - } + } + onClick={onConfigure} + > + + {t('common.dataSource.notion.addWorkspace')} +
} + + ) + } + + )} + + {isWebsite && !isConfigured && ( +
+ {t('common.dataSource.configure')} +
+ )} +
{ isConfigured && ( diff --git a/web/i18n/en-US/common.ts b/web/i18n/en-US/common.ts index 06b7961773b807..6984b89072607c 100644 --- a/web/i18n/en-US/common.ts +++ b/web/i18n/en-US/common.ts @@ -338,6 +338,7 @@ const translation = { dataSource: { add: 'Add a data source', connect: 'Connect', + configure: 'Configure', notion: { title: 'Notion', description: 'Using Notion as a data source for the Knowledge.', diff --git a/web/i18n/zh-Hans/common.ts b/web/i18n/zh-Hans/common.ts index af67c57e13117c..64d0a367bc41a6 100644 --- a/web/i18n/zh-Hans/common.ts +++ b/web/i18n/zh-Hans/common.ts @@ -338,6 +338,7 @@ const translation = { dataSource: { add: '添加数据源', connect: '绑定', + configure: '配置', notion: { title: 'Notion', description: '使用 Notion 作为知识库的数据源。', @@ -356,11 +357,11 @@ const translation = { addPages: '添加页面', preview: '预览', }, - website: { - title: '网站', - description: '使用网络爬虫从网站导入内容。', - with: '使用', - }, + }, + website: { + title: '网站', + description: '使用网络爬虫从网站导入内容。', + with: '使用', }, }, plugin: { From 04857a29f86ceb239d88bce9a00ee6e3e771eb07 Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 31 May 2024 18:41:45 +0800 Subject: [PATCH 05/31] feat: firecrawl item --- .../data-source-page/data-source-website/index.tsx | 5 +++-- .../account-setting/data-source-page/panel/config-item.tsx | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx b/web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx index 81b5f91a0fa317..c572f5d3b0dd9e 100644 --- a/web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx +++ b/web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx @@ -2,6 +2,7 @@ import type { FC } from 'react' import React, { useCallback, useEffect, useState } from 'react' import { useBoolean } from 'ahooks' +import cn from 'classnames' import Panel from '../panel' import { DataSourceType } from '../panel/types' import { fetchWebsiteDataSource } from '@/service/common' @@ -17,7 +18,7 @@ import { type Props = {} -const isUseMock = false +const isUseMock = true const mockList: DataSourceWebsiteItem[] = [ { id: '1', @@ -67,7 +68,7 @@ const DataSourceWebsite: FC = () => { configuredList={list.map(item => ({ id: item.id, logo: ({ className }: { className: string }) => ( -
L
+
🔥
), name: 'FireCrawl', isActive: true, diff --git a/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx b/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx index a776455432410d..2e6e0c64c63958 100644 --- a/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx +++ b/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx @@ -41,7 +41,7 @@ const ConfigItem: FC = ({ return (
- +
{payload.name}
{ payload.isActive From 10e0f43e416c0dd7b6d9daddf66a15d41e1f45a4 Mon Sep 17 00:00:00 2001 From: Joel Date: Tue, 4 Jun 2024 11:46:03 +0800 Subject: [PATCH 06/31] chore: crawel text --- .../data-source-page/panel/config-item.tsx | 10 +++++----- .../account-setting/data-source-page/panel/index.tsx | 2 +- web/i18n/en-US/common.ts | 3 +++ web/i18n/zh-Hans/common.ts | 3 +++ 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx b/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx index 2e6e0c64c63958..376c4aea7b3318 100644 --- a/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx +++ b/web/app/components/header/account-setting/data-source-page/panel/config-item.tsx @@ -48,11 +48,11 @@ const ConfigItem: FC = ({ ? : } -
+
{ payload.isActive - ? t('common.dataSource.notion.connected') - : t('common.dataSource.notion.disconnected') + ? t(isNotion ? 'common.dataSource.notion.connected' : 'common.dataSource.website.active') + : t(isNotion ? 'common.dataSource.notion.disconnected' : 'common.dataSource.website.inactive') }
@@ -66,8 +66,8 @@ const ConfigItem: FC = ({ { isWebsite && ( -
- +
+
) } diff --git a/web/app/components/header/account-setting/data-source-page/panel/index.tsx b/web/app/components/header/account-setting/data-source-page/panel/index.tsx index 75bef952f8ede0..b0f6f4ad13ac95 100644 --- a/web/app/components/header/account-setting/data-source-page/panel/index.tsx +++ b/web/app/components/header/account-setting/data-source-page/panel/index.tsx @@ -110,7 +110,7 @@ const Panel: FC = ({ isConfigured && (
- {t('common.dataSource.notion.connectedWorkspace')} + {isNotion ? t('common.dataSource.notion.connectedWorkspace') : t('common.dataSource.website.configuredCrawlers')}
diff --git a/web/i18n/en-US/common.ts b/web/i18n/en-US/common.ts index 6984b89072607c..c0a64cc0b6e56c 100644 --- a/web/i18n/en-US/common.ts +++ b/web/i18n/en-US/common.ts @@ -362,6 +362,9 @@ const translation = { title: 'Website', description: 'Import content from websites using web crawler.', with: 'With', + configuredCrawlers: 'Configured crawlers', + active: 'Active', + inactive: 'Inactive', }, }, plugin: { diff --git a/web/i18n/zh-Hans/common.ts b/web/i18n/zh-Hans/common.ts index 64d0a367bc41a6..c3e82cb7aaa488 100644 --- a/web/i18n/zh-Hans/common.ts +++ b/web/i18n/zh-Hans/common.ts @@ -362,6 +362,9 @@ const translation = { title: '网站', description: '使用网络爬虫从网站导入内容。', with: '使用', + configuredCrawlers: '已配置的爬虫', + active: '可用', + inactive: '不可用', }, }, plugin: { From df97c6b291ddecebb3e42eb41fbf19f251ddce8c Mon Sep 17 00:00:00 2001 From: Joel Date: Tue, 4 Jun 2024 16:48:38 +0800 Subject: [PATCH 07/31] feat: firecrawl no data --- .../assets/vender/line/others/icon-3-dots.svg | 5 +++ .../src/vender/line/others/Icon3Dots.json | 39 +++++++++++++++++++ .../src/vender/line/others/Icon3Dots.tsx | 16 ++++++++ .../icons/src/vender/line/others/index.ts | 1 + web/app/components/datasets/create/index.tsx | 2 +- .../datasets/create/step-one/index.tsx | 9 +++-- .../create/website/import-firecrawl.tsx | 15 +++++++ .../datasets/create/website/index.tsx | 18 +++++++++ .../datasets/create/website/no-data.tsx | 36 +++++++++++++++++ web/i18n/en-US/dataset-creation.ts | 5 +++ web/i18n/zh-Hans/dataset-creation.ts | 5 +++ 11 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 web/app/components/base/icons/assets/vender/line/others/icon-3-dots.svg create mode 100644 web/app/components/base/icons/src/vender/line/others/Icon3Dots.json create mode 100644 web/app/components/base/icons/src/vender/line/others/Icon3Dots.tsx create mode 100644 web/app/components/datasets/create/website/import-firecrawl.tsx create mode 100644 web/app/components/datasets/create/website/index.tsx create mode 100644 web/app/components/datasets/create/website/no-data.tsx diff --git a/web/app/components/base/icons/assets/vender/line/others/icon-3-dots.svg b/web/app/components/base/icons/assets/vender/line/others/icon-3-dots.svg new file mode 100644 index 00000000000000..bba42851f61829 --- /dev/null +++ b/web/app/components/base/icons/assets/vender/line/others/icon-3-dots.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web/app/components/base/icons/src/vender/line/others/Icon3Dots.json b/web/app/components/base/icons/src/vender/line/others/Icon3Dots.json new file mode 100644 index 00000000000000..0942222f39e0a4 --- /dev/null +++ b/web/app/components/base/icons/src/vender/line/others/Icon3Dots.json @@ -0,0 +1,39 @@ +{ + "icon": { + "type": "element", + "isRootNode": true, + "name": "svg", + "attributes": { + "width": "16", + "height": "16", + "viewBox": "0 0 16 16", + "fill": "none", + "xmlns": "http://www.w3.org/2000/svg" + }, + "children": [ + { + "type": "element", + "name": "g", + "attributes": { + "id": "Icon-3-dots" + }, + "children": [ + { + "type": "element", + "name": "path", + "attributes": { + "id": "Icon", + "d": "M5 6.5V5M8.93934 7.56066L10 6.5M10.0103 11.5H11.5103", + "stroke": "currentColor", + "stroke-width": "2", + "stroke-linecap": "round", + "stroke-linejoin": "round" + }, + "children": [] + } + ] + } + ] + }, + "name": "Icon3Dots" +} \ No newline at end of file diff --git a/web/app/components/base/icons/src/vender/line/others/Icon3Dots.tsx b/web/app/components/base/icons/src/vender/line/others/Icon3Dots.tsx new file mode 100644 index 00000000000000..1f9eb767a8f2fb --- /dev/null +++ b/web/app/components/base/icons/src/vender/line/others/Icon3Dots.tsx @@ -0,0 +1,16 @@ +// GENERATE BY script +// DON NOT EDIT IT MANUALLY + +import * as React from 'react' +import data from './Icon3Dots.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 = 'Icon3Dots' + +export default Icon diff --git a/web/app/components/base/icons/src/vender/line/others/index.ts b/web/app/components/base/icons/src/vender/line/others/index.ts index 648792f22b40f0..554f14b55bd650 100644 --- a/web/app/components/base/icons/src/vender/line/others/index.ts +++ b/web/app/components/base/icons/src/vender/line/others/index.ts @@ -3,4 +3,5 @@ export { default as Colors } from './Colors' export { default as DragHandle } from './DragHandle' export { default as Exchange02 } from './Exchange02' export { default as FileCode } from './FileCode' +export { default as Icon3Dots } from './Icon3Dots' export { default as Tools } from './Tools' diff --git a/web/app/components/datasets/create/index.tsx b/web/app/components/datasets/create/index.tsx index ce32f29fc528fd..83d74b55baefd6 100644 --- a/web/app/components/datasets/create/index.tsx +++ b/web/app/components/datasets/create/index.tsx @@ -23,7 +23,7 @@ const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => { const { t } = useTranslation() const { setShowAccountSettingModal } = useModalContext() const [hasConnection, setHasConnection] = useState(true) - const [dataSourceType, setDataSourceType] = useState(DataSourceType.FILE) + const [dataSourceType, setDataSourceType] = useState(DataSourceType.WEB) // TODO: for test. DataSourceType.FILE const [step, setStep] = useState(1) const [indexingTypeCache, setIndexTypeCache] = useState('') const [fileList, setFiles] = useState([]) diff --git a/web/app/components/datasets/create/step-one/index.tsx b/web/app/components/datasets/create/step-one/index.tsx index 66df1dd2083466..7d46d5eb5d0bc1 100644 --- a/web/app/components/datasets/create/step-one/index.tsx +++ b/web/app/components/datasets/create/step-one/index.tsx @@ -6,6 +6,7 @@ import FilePreview from '../file-preview' import FileUploader from '../file-uploader' import NotionPagePreview from '../notion-page-preview' import EmptyDatasetCreationModal from '../empty-dataset-creation-modal' +import Website from '../website' import s from './index.module.css' import type { FileItem } from '@/models/datasets' import type { NotionPage } from '@/models/common' @@ -150,10 +151,9 @@ const StepOne = ({ {t('datasetCreation.stepOne.dataSourceType.notion')}
changeType(DataSourceType.WEB)} + className={cn(s.dataSourceItem, dataSourceType === DataSourceType.WEB ? s.active : s.disabled)} + onClick={() => changeType(DataSourceType.WEB)} > - Coming soon {t('datasetCreation.stepOne.dataSourceType.web')}
@@ -201,6 +201,9 @@ const StepOne = ({ )} )} + {dataSourceType === DataSourceType.WEB && ( + + )} {!datasetId && ( <>
diff --git a/web/app/components/datasets/create/website/import-firecrawl.tsx b/web/app/components/datasets/create/website/import-firecrawl.tsx new file mode 100644 index 00000000000000..7fafaf32bc93da --- /dev/null +++ b/web/app/components/datasets/create/website/import-firecrawl.tsx @@ -0,0 +1,15 @@ +'use client' +import type { FC } from 'react' +import React from 'react' + +type Props = { + +} + +const ImportFireCrawl: FC = () => { + return ( +
+
+ ) +} +export default React.memo(ImportFireCrawl) diff --git a/web/app/components/datasets/create/website/index.tsx b/web/app/components/datasets/create/website/index.tsx new file mode 100644 index 00000000000000..cd82d45f7c588c --- /dev/null +++ b/web/app/components/datasets/create/website/index.tsx @@ -0,0 +1,18 @@ +'use client' +import type { FC } from 'react' +import React, { useCallback } from 'react' +import NoData from './no-data' + +type Props = { + +} + +const WebsitePreview: FC = () => { + const handleOnConfig = useCallback(() => { }, []) + return ( +
+ +
+ ) +} +export default React.memo(WebsitePreview) diff --git a/web/app/components/datasets/create/website/no-data.tsx b/web/app/components/datasets/create/website/no-data.tsx new file mode 100644 index 00000000000000..35eb35f7095dac --- /dev/null +++ b/web/app/components/datasets/create/website/no-data.tsx @@ -0,0 +1,36 @@ +'use client' +import type { FC } from 'react' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { Icon3Dots } from '@/app/components/base/icons/src/vender/line/others' +import Button from '@/app/components/base/button' + +const I18N_PREFIX = 'datasetCreation.stepOne.website' + +type Props = { + onConfig: () => void +} + +const NoData: FC = ({ + onConfig, +}) => { + const { t } = useTranslation() + + return ( +
+
+ 🔥 +
+
+ {t(`${I18N_PREFIX}.fireCrawlNotConfigured`)} +
+ {t(`${I18N_PREFIX}.fireCrawlNotConfiguredDescription`)} +
+
+ +
+ ) +} +export default React.memo(NoData) diff --git a/web/i18n/en-US/dataset-creation.ts b/web/i18n/en-US/dataset-creation.ts index 8923170f7f4539..9c7f44f1d0472c 100644 --- a/web/i18n/en-US/dataset-creation.ts +++ b/web/i18n/en-US/dataset-creation.ts @@ -50,6 +50,11 @@ const translation = { confirmButton: 'Create', failed: 'Creation failed', }, + website: { + fireCrawlNotConfigured: 'Firecrawl is not configured', + fireCrawlNotConfiguredDescription: 'Configure Firecrawl with API key to use it.', + configure: 'Configure', + }, }, stepTwo: { segmentation: 'Chunk settings', diff --git a/web/i18n/zh-Hans/dataset-creation.ts b/web/i18n/zh-Hans/dataset-creation.ts index d36850dc3dc833..208312f01f971d 100644 --- a/web/i18n/zh-Hans/dataset-creation.ts +++ b/web/i18n/zh-Hans/dataset-creation.ts @@ -50,6 +50,11 @@ const translation = { confirmButton: '创建', failed: '创建失败', }, + website: { + fireCrawlNotConfigured: 'Firecrawl 未配置', + fireCrawlNotConfiguredDescription: '请配置 Firecrawl 的 API 密钥以使用它。', + configure: 'Configure', + }, }, stepTwo: { segmentation: '分段设置', From 5aef25b5fe01fab8fa7064fe51ab12773be93aea Mon Sep 17 00:00:00 2001 From: Joel Date: Tue, 4 Jun 2024 16:53:25 +0800 Subject: [PATCH 08/31] feat: handle show config firecrawl --- web/app/components/datasets/create/website/index.tsx | 11 ++++++++++- .../data-source-page/data-source-website/index.tsx | 2 -- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/web/app/components/datasets/create/website/index.tsx b/web/app/components/datasets/create/website/index.tsx index cd82d45f7c588c..96af30faf3ab3b 100644 --- a/web/app/components/datasets/create/website/index.tsx +++ b/web/app/components/datasets/create/website/index.tsx @@ -2,13 +2,22 @@ import type { FC } from 'react' import React, { useCallback } from 'react' import NoData from './no-data' +import { useModalContext } from '@/context/modal-context' type Props = { } const WebsitePreview: FC = () => { - const handleOnConfig = useCallback(() => { }, []) + const { setShowAccountSettingModal } = useModalContext() + + const handleOnConfig = useCallback(() => { + setShowAccountSettingModal({ + payload: 'data-source', + }) + }, [setShowAccountSettingModal]) + + // TODO: on Hide return (
diff --git a/web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx b/web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx index c572f5d3b0dd9e..489af9842cbc03 100644 --- a/web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx +++ b/web/app/components/header/account-setting/data-source-page/data-source-website/index.tsx @@ -57,8 +57,6 @@ const DataSourceWebsite: FC = () => { }, []) - console.log(list) - return ( Date: Tue, 4 Jun 2024 18:30:27 +0800 Subject: [PATCH 09/31] feat: firecrawl header --- .../create/website/firecrawl/base/field.tsx | 0 .../create/website/firecrawl/base/input.tsx | 25 +++++++++++ .../website/firecrawl/base/url-input.tsx | 41 ++++++++++++++++++ .../create/website/firecrawl/header.tsx | 42 +++++++++++++++++++ .../create/website/firecrawl/index.tsx | 32 ++++++++++++++ .../website/firecrawl/options-error.tsx | 0 .../create/website/firecrawl/options.tsx | 0 .../create/website/import-firecrawl.tsx | 15 ------- .../datasets/create/website/index.tsx | 19 +++++++-- web/i18n/en-US/dataset-creation.ts | 4 ++ web/i18n/zh-Hans/dataset-creation.ts | 6 ++- 11 files changed, 165 insertions(+), 19 deletions(-) create mode 100644 web/app/components/datasets/create/website/firecrawl/base/field.tsx create mode 100644 web/app/components/datasets/create/website/firecrawl/base/input.tsx create mode 100644 web/app/components/datasets/create/website/firecrawl/base/url-input.tsx create mode 100644 web/app/components/datasets/create/website/firecrawl/header.tsx create mode 100644 web/app/components/datasets/create/website/firecrawl/index.tsx create mode 100644 web/app/components/datasets/create/website/firecrawl/options-error.tsx create mode 100644 web/app/components/datasets/create/website/firecrawl/options.tsx delete mode 100644 web/app/components/datasets/create/website/import-firecrawl.tsx diff --git a/web/app/components/datasets/create/website/firecrawl/base/field.tsx b/web/app/components/datasets/create/website/firecrawl/base/field.tsx new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/web/app/components/datasets/create/website/firecrawl/base/input.tsx b/web/app/components/datasets/create/website/firecrawl/base/input.tsx new file mode 100644 index 00000000000000..0d3337848d41b0 --- /dev/null +++ b/web/app/components/datasets/create/website/firecrawl/base/input.tsx @@ -0,0 +1,25 @@ +'use client' +import type { FC } from 'react' +import React from 'react' + +type Props = { + value: string + onChange: (value: string) => void + placeholder?: string +} + +const Input: FC = ({ + value, + onChange, + placeholder = '', +}) => { + return ( + onChange(e.target.value)} + className='flex h-9 w-full py-1 px-2 rounded-lg text-xs leading-normal bg-gray-100 caret-primary-600 hover:bg-gray-100 focus:ring-1 focus:ring-inset focus:ring-gray-200 focus-visible:outline-none focus:bg-gray-50 placeholder:text-gray-400' + placeholder={placeholder} + /> + ) +} +export default React.memo(Input) diff --git a/web/app/components/datasets/create/website/firecrawl/base/url-input.tsx b/web/app/components/datasets/create/website/firecrawl/base/url-input.tsx new file mode 100644 index 00000000000000..915db90d5c5a77 --- /dev/null +++ b/web/app/components/datasets/create/website/firecrawl/base/url-input.tsx @@ -0,0 +1,41 @@ +'use client' +import type { FC } from 'react' +import React, { useCallback, useState } from 'react' +import { useTranslation } from 'react-i18next' +import Input from './input' +import Button from '@/app/components/base/button' + +const I18N_PREFIX = 'datasetCreation.stepOne.website' + +type Props = { + onRun: (url: string) => void +} + +const UrlInput: FC = ({ + onRun, +}) => { + const { t } = useTranslation() + const [url, setUrl] = useState('') + + const handleOnRun = useCallback(() => { + onRun(url) + }, [onRun, url]) + + return ( +
+ + +
+ ) +} +export default React.memo(UrlInput) diff --git a/web/app/components/datasets/create/website/firecrawl/header.tsx b/web/app/components/datasets/create/website/firecrawl/header.tsx new file mode 100644 index 00000000000000..d89cfce46a55ce --- /dev/null +++ b/web/app/components/datasets/create/website/firecrawl/header.tsx @@ -0,0 +1,42 @@ +'use client' +import type { FC } from 'react' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' +import { BookOpen01 } from '@/app/components/base/icons/src/vender/line/education' + +const I18N_PREFIX = 'datasetCreation.stepOne.website' + +type Props = { + onSetting: () => void +} + +const Header: FC = ({ + onSetting, +}) => { + const { t } = useTranslation() + + return ( +
+
+
{t(`${I18N_PREFIX}.firecrawlTitle`)}
+
+
+ +
+
+ + + {t(`${I18N_PREFIX}.firecrawlDoc`)} + +
+ ) +} +export default React.memo(Header) diff --git a/web/app/components/datasets/create/website/firecrawl/index.tsx b/web/app/components/datasets/create/website/firecrawl/index.tsx new file mode 100644 index 00000000000000..85dd5fbc7d6b00 --- /dev/null +++ b/web/app/components/datasets/create/website/firecrawl/index.tsx @@ -0,0 +1,32 @@ +'use client' +import type { FC } from 'react' +import React, { useCallback } from 'react' +import Header from './header' +import UrlInput from './base/url-input' +import { useModalContext } from '@/context/modal-context' + +type Props = { + +} + +const FireCrawl: FC = () => { + const { setShowAccountSettingModal } = useModalContext() + const handleSetting = useCallback(() => { + setShowAccountSettingModal({ + payload: 'data-source', + }) + }, [setShowAccountSettingModal]) + + const handleRun = useCallback((url: string) => { + console.log(url) + }, []) + return ( +
+
+
+ +
+
+ ) +} +export default React.memo(FireCrawl) diff --git a/web/app/components/datasets/create/website/firecrawl/options-error.tsx b/web/app/components/datasets/create/website/firecrawl/options-error.tsx new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/web/app/components/datasets/create/website/firecrawl/options.tsx b/web/app/components/datasets/create/website/firecrawl/options.tsx new file mode 100644 index 00000000000000..e69de29bb2d1d6 diff --git a/web/app/components/datasets/create/website/import-firecrawl.tsx b/web/app/components/datasets/create/website/import-firecrawl.tsx deleted file mode 100644 index 7fafaf32bc93da..00000000000000 --- a/web/app/components/datasets/create/website/import-firecrawl.tsx +++ /dev/null @@ -1,15 +0,0 @@ -'use client' -import type { FC } from 'react' -import React from 'react' - -type Props = { - -} - -const ImportFireCrawl: FC = () => { - return ( -
-
- ) -} -export default React.memo(ImportFireCrawl) diff --git a/web/app/components/datasets/create/website/index.tsx b/web/app/components/datasets/create/website/index.tsx index 96af30faf3ab3b..b85d986f798418 100644 --- a/web/app/components/datasets/create/website/index.tsx +++ b/web/app/components/datasets/create/website/index.tsx @@ -1,7 +1,8 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' +import React, { useCallback, useState } from 'react' import NoData from './no-data' +import Firecrawl from './firecrawl' import { useModalContext } from '@/context/modal-context' type Props = { @@ -10,6 +11,8 @@ type Props = { const WebsitePreview: FC = () => { const { setShowAccountSettingModal } = useModalContext() + const [isLoaded, setIsLoaded] = useState(false) + const [isConfigured, setIsConfigured] = useState(true) const handleOnConfig = useCallback(() => { setShowAccountSettingModal({ @@ -17,10 +20,20 @@ const WebsitePreview: FC = () => { }) }, [setShowAccountSettingModal]) - // TODO: on Hide + // TODO: on Hide account setting modal + + if (isLoaded) + return null + return (
- + {isConfigured + ? ( + + ) + : ( + + )}
) } diff --git a/web/i18n/en-US/dataset-creation.ts b/web/i18n/en-US/dataset-creation.ts index 9c7f44f1d0472c..c3506485d5d8b4 100644 --- a/web/i18n/en-US/dataset-creation.ts +++ b/web/i18n/en-US/dataset-creation.ts @@ -54,6 +54,10 @@ const translation = { fireCrawlNotConfigured: 'Firecrawl is not configured', fireCrawlNotConfiguredDescription: 'Configure Firecrawl with API key to use it.', configure: 'Configure', + run: 'Run', + firecrawlTitle: 'Extract web content with 🔥Firecrawl', + firecrawlDoc: 'Firecrawl docs', + firecrawlDocLink: '', }, }, stepTwo: { diff --git a/web/i18n/zh-Hans/dataset-creation.ts b/web/i18n/zh-Hans/dataset-creation.ts index 208312f01f971d..7956c98931db90 100644 --- a/web/i18n/zh-Hans/dataset-creation.ts +++ b/web/i18n/zh-Hans/dataset-creation.ts @@ -53,7 +53,11 @@ const translation = { website: { fireCrawlNotConfigured: 'Firecrawl 未配置', fireCrawlNotConfiguredDescription: '请配置 Firecrawl 的 API 密钥以使用它。', - configure: 'Configure', + configure: '配置', + run: '运行', + firecrawlTitle: '使用 🔥Firecrawl 提取网页内容', + firecrawlDoc: 'Firecrawl 文档', + firecrawlDocLink: '', }, }, stepTwo: { From 93113766a1ad0a416304f1ba966986c97574a3a2 Mon Sep 17 00:00:00 2001 From: Joel Date: Tue, 4 Jun 2024 22:48:20 +0800 Subject: [PATCH 10/31] just add --- .../website/firecrawl/base/options-wrap.tsx | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 web/app/components/datasets/create/website/firecrawl/base/options-wrap.tsx diff --git a/web/app/components/datasets/create/website/firecrawl/base/options-wrap.tsx b/web/app/components/datasets/create/website/firecrawl/base/options-wrap.tsx new file mode 100644 index 00000000000000..9f1fe32b7f2742 --- /dev/null +++ b/web/app/components/datasets/create/website/firecrawl/base/options-wrap.tsx @@ -0,0 +1,37 @@ +'use client' +import { useBoolean } from 'ahooks' +import type { FC } from 'react' +import React from 'react' +import { Settings04 } from '@/app/components/base/icons/src/vender/line/general' + +type Props = { + children: React.ReactNode +} + +const OptionsWrap: FC = ({ + children, +}) => { + const [fold, { + setTrue: foldTrue, + setFalse: foldFalse, + toggle: foldToggle, + }] = useBoolean(false) + return ( +
+
+
+ +
Options
+
+ +
+ {!fold && ( +
+ {children} +
+ )} + +
+ ) +} +export default React.memo(OptionsWrap) From baad0a7a5f903c57af37e2fa124b616660dbc208 Mon Sep 17 00:00:00 2001 From: Joel Date: Wed, 5 Jun 2024 11:11:07 +0800 Subject: [PATCH 11/31] options temp --- .../create/website/firecrawl/base/options-wrap.tsx | 7 ++++++- .../components/datasets/create/website/firecrawl/index.tsx | 6 +++++- web/i18n/en-US/dataset-creation.ts | 1 + web/i18n/zh-Hans/dataset-creation.ts | 1 + 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/web/app/components/datasets/create/website/firecrawl/base/options-wrap.tsx b/web/app/components/datasets/create/website/firecrawl/base/options-wrap.tsx index 9f1fe32b7f2742..32b4d3c4b5e9b6 100644 --- a/web/app/components/datasets/create/website/firecrawl/base/options-wrap.tsx +++ b/web/app/components/datasets/create/website/firecrawl/base/options-wrap.tsx @@ -2,8 +2,11 @@ import { useBoolean } from 'ahooks' import type { FC } from 'react' import React from 'react' +import { useTranslation } from 'react-i18next' import { Settings04 } from '@/app/components/base/icons/src/vender/line/general' +const I18N_PREFIX = 'datasetCreation.stepOne.website' + type Props = { children: React.ReactNode } @@ -11,6 +14,8 @@ type Props = { const OptionsWrap: FC = ({ children, }) => { + const { t } = useTranslation() + const [fold, { setTrue: foldTrue, setFalse: foldFalse, @@ -21,7 +26,7 @@ const OptionsWrap: FC = ({
-
Options
+
{t(`${I18N_PREFIX}.options`)}
diff --git a/web/app/components/datasets/create/website/firecrawl/index.tsx b/web/app/components/datasets/create/website/firecrawl/index.tsx index 85dd5fbc7d6b00..d813ae6e94f2dd 100644 --- a/web/app/components/datasets/create/website/firecrawl/index.tsx +++ b/web/app/components/datasets/create/website/firecrawl/index.tsx @@ -3,8 +3,8 @@ import type { FC } from 'react' import React, { useCallback } from 'react' import Header from './header' import UrlInput from './base/url-input' +import OptionsWrap from './base/options-wrap' import { useModalContext } from '@/context/modal-context' - type Props = { } @@ -25,6 +25,10 @@ const FireCrawl: FC = () => {
+ + +
contents
+
) diff --git a/web/i18n/en-US/dataset-creation.ts b/web/i18n/en-US/dataset-creation.ts index c3506485d5d8b4..9bb86765520f59 100644 --- a/web/i18n/en-US/dataset-creation.ts +++ b/web/i18n/en-US/dataset-creation.ts @@ -58,6 +58,7 @@ const translation = { firecrawlTitle: 'Extract web content with 🔥Firecrawl', firecrawlDoc: 'Firecrawl docs', firecrawlDocLink: '', + options: 'Options', }, }, stepTwo: { diff --git a/web/i18n/zh-Hans/dataset-creation.ts b/web/i18n/zh-Hans/dataset-creation.ts index 7956c98931db90..9c4793c0a393b4 100644 --- a/web/i18n/zh-Hans/dataset-creation.ts +++ b/web/i18n/zh-Hans/dataset-creation.ts @@ -58,6 +58,7 @@ const translation = { firecrawlTitle: '使用 🔥Firecrawl 提取网页内容', firecrawlDoc: 'Firecrawl 文档', firecrawlDocLink: '', + options: '选项', }, }, stepTwo: { From 5a0bfb69a37ea13b5d5fc35ad75f05d1863510af Mon Sep 17 00:00:00 2001 From: Joel Date: Wed, 5 Jun 2024 14:32:14 +0800 Subject: [PATCH 12/31] feat: options main --- .../firecrawl/base/checkbox-with-label.tsx | 27 +++++++ .../create/website/firecrawl/base/field.tsx | 41 +++++++++++ .../create/website/firecrawl/base/input.tsx | 19 +++-- .../website/firecrawl/base/options-wrap.tsx | 26 +++++-- .../create/website/firecrawl/index.tsx | 19 +++-- .../create/website/firecrawl/options.tsx | 71 +++++++++++++++++++ web/models/datasets.ts | 9 +++ 7 files changed, 197 insertions(+), 15 deletions(-) create mode 100644 web/app/components/datasets/create/website/firecrawl/base/checkbox-with-label.tsx diff --git a/web/app/components/datasets/create/website/firecrawl/base/checkbox-with-label.tsx b/web/app/components/datasets/create/website/firecrawl/base/checkbox-with-label.tsx new file mode 100644 index 00000000000000..f25e45724512e7 --- /dev/null +++ b/web/app/components/datasets/create/website/firecrawl/base/checkbox-with-label.tsx @@ -0,0 +1,27 @@ +'use client' +import type { FC } from 'react' +import React from 'react' +import cn from 'classnames' +import Checkbox from '@/app/components/base/checkbox' + +type Props = { + className?: string + isChecked: boolean + onChange: (isChecked: boolean) => void + label: string +} + +const CheckboxWithLabel: FC = ({ + className = '', + isChecked, + onChange, + label, +}) => { + return ( + + ) +} +export default React.memo(CheckboxWithLabel) diff --git a/web/app/components/datasets/create/website/firecrawl/base/field.tsx b/web/app/components/datasets/create/website/firecrawl/base/field.tsx index e69de29bb2d1d6..d9fb18aa48c420 100644 --- a/web/app/components/datasets/create/website/firecrawl/base/field.tsx +++ b/web/app/components/datasets/create/website/firecrawl/base/field.tsx @@ -0,0 +1,41 @@ +'use client' +import type { FC } from 'react' +import React from 'react' +import cn from 'classnames' +import Input from './input' + +type Props = { + className?: string + label: string + value: string | number + onChange: (value: string | number) => void + isRequired?: boolean + placeholder?: string + isNumber?: boolean +} + +const Field: FC = ({ + className, + label, + value, + onChange, + isRequired = false, + placeholder = '', + isNumber = false, +}) => { + return ( +
+
+ {label} + {isRequired && *} +
+ +
+ ) +} +export default React.memo(Field) diff --git a/web/app/components/datasets/create/website/firecrawl/base/input.tsx b/web/app/components/datasets/create/website/firecrawl/base/input.tsx index 0d3337848d41b0..a042b1460a78ba 100644 --- a/web/app/components/datasets/create/website/firecrawl/base/input.tsx +++ b/web/app/components/datasets/create/website/firecrawl/base/input.tsx @@ -1,22 +1,33 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import React, { useCallback } from 'react' type Props = { - value: string - onChange: (value: string) => void + value: string | number + onChange: (value: string | number) => void placeholder?: string + isNumber?: boolean } const Input: FC = ({ value, onChange, placeholder = '', + isNumber = false, }) => { + const handleChange = useCallback((e: React.ChangeEvent) => { + const value = e.target.value + if (isNumber) { + onChange(parseFloat(value)) + return + } + onChange(value) + }, [isNumber, onChange]) return ( onChange(e.target.value)} + onChange={handleChange} className='flex h-9 w-full py-1 px-2 rounded-lg text-xs leading-normal bg-gray-100 caret-primary-600 hover:bg-gray-100 focus:ring-1 focus:ring-inset focus:ring-gray-200 focus-visible:outline-none focus:bg-gray-50 placeholder:text-gray-400' placeholder={placeholder} /> diff --git a/web/app/components/datasets/create/website/firecrawl/base/options-wrap.tsx b/web/app/components/datasets/create/website/firecrawl/base/options-wrap.tsx index 32b4d3c4b5e9b6..ff66cb0e614ca0 100644 --- a/web/app/components/datasets/create/website/firecrawl/base/options-wrap.tsx +++ b/web/app/components/datasets/create/website/firecrawl/base/options-wrap.tsx @@ -3,36 +3,48 @@ import { useBoolean } from 'ahooks' import type { FC } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' +import cn from 'classnames' import { Settings04 } from '@/app/components/base/icons/src/vender/line/general' - +import { ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows' const I18N_PREFIX = 'datasetCreation.stepOne.website' type Props = { + className?: string children: React.ReactNode + errorMsg?: string } const OptionsWrap: FC = ({ + className = '', children, + errorMsg, }) => { const { t } = useTranslation() const [fold, { - setTrue: foldTrue, - setFalse: foldFalse, toggle: foldToggle, }] = useBoolean(false) return ( -
-
+
+
{t(`${I18N_PREFIX}.options`)}
- +
{!fold && (
- {children} + {!errorMsg + ? children + : ( +
+ {errorMsg} +
+ )}
)} diff --git a/web/app/components/datasets/create/website/firecrawl/index.tsx b/web/app/components/datasets/create/website/firecrawl/index.tsx index d813ae6e94f2dd..1746a1a2450bec 100644 --- a/web/app/components/datasets/create/website/firecrawl/index.tsx +++ b/web/app/components/datasets/create/website/firecrawl/index.tsx @@ -1,14 +1,23 @@ 'use client' import type { FC } from 'react' -import React, { useCallback } from 'react' +import React, { useCallback, useState } from 'react' import Header from './header' import UrlInput from './base/url-input' import OptionsWrap from './base/options-wrap' +import Options from './options' import { useModalContext } from '@/context/modal-context' +import type { CrawlOptions } from '@/models/datasets' type Props = { } - +const DEFAULT_CRAWL_OPTIONS: CrawlOptions = { + crawl_sub_pages: true, + only_main_content: true, + includes: '', + excludes: '', + limit: 100, + max_depth: 3, +} const FireCrawl: FC = () => { const { setShowAccountSettingModal } = useModalContext() const handleSetting = useCallback(() => { @@ -17,6 +26,8 @@ const FireCrawl: FC = () => { }) }, [setShowAccountSettingModal]) + const [crawlOptions, setCrawlOptions] = useState(DEFAULT_CRAWL_OPTIONS) + const handleRun = useCallback((url: string) => { console.log(url) }, []) @@ -26,8 +37,8 @@ const FireCrawl: FC = () => {
- -
contents
+ +
diff --git a/web/app/components/datasets/create/website/firecrawl/options.tsx b/web/app/components/datasets/create/website/firecrawl/options.tsx index e69de29bb2d1d6..3062ba2d70772e 100644 --- a/web/app/components/datasets/create/website/firecrawl/options.tsx +++ b/web/app/components/datasets/create/website/firecrawl/options.tsx @@ -0,0 +1,71 @@ +'use client' +import type { FC } from 'react' +import React, { useCallback } from 'react' +import cn from 'classnames' +import CheckboxWithLabel from './base/checkbox-with-label' +import Field from './base/field' +import type { CrawlOptions } from '@/models/datasets' + +type Props = { + className?: string + payload: CrawlOptions + onChange: (payload: CrawlOptions) => void +} + +const Options: FC = ({ + className = '', + payload, + onChange, +}) => { + const handleChange = useCallback((key: keyof CrawlOptions) => { + return (value: any) => { + onChange({ + ...payload, + [key]: value, + }) + } + }, [payload, onChange]) + return ( +
+ +
+ + +
+ +
+ + +
+ +
+ ) +} +export default React.memo(Options) diff --git a/web/models/datasets.ts b/web/models/datasets.ts index 585c0473d4f31a..8a16b207d13c55 100644 --- a/web/models/datasets.ts +++ b/web/models/datasets.ts @@ -39,6 +39,15 @@ export type CustomFile = File & { created_at?: number } +export type CrawlOptions = { + crawl_sub_pages: boolean + only_main_content: boolean + includes: string + excludes: string + limit: number + max_depth: number +} + export type FileItem = { fileID: string file: CustomFile From b4a108960f11f0fc264422db8b1e245b6e0f6aa2 Mon Sep 17 00:00:00 2001 From: Joel Date: Wed, 5 Jun 2024 14:50:28 +0800 Subject: [PATCH 13/31] feat: option styles --- .../create/website/firecrawl/base/field.tsx | 6 ++--- .../website/firecrawl/base/options-wrap.tsx | 2 +- .../create/website/firecrawl/index.tsx | 3 +-- .../create/website/firecrawl/options.tsx | 26 ++++++++++++++----- web/i18n/en-US/dataset-creation.ts | 6 +++++ web/i18n/zh-Hans/dataset-creation.ts | 6 +++++ 6 files changed, 36 insertions(+), 13 deletions(-) diff --git a/web/app/components/datasets/create/website/firecrawl/base/field.tsx b/web/app/components/datasets/create/website/firecrawl/base/field.tsx index d9fb18aa48c420..12eaefa9b85f03 100644 --- a/web/app/components/datasets/create/website/firecrawl/base/field.tsx +++ b/web/app/components/datasets/create/website/firecrawl/base/field.tsx @@ -25,9 +25,9 @@ const Field: FC = ({ }) => { return (
-
- {label} - {isRequired && *} +
+
{label}
+ {isRequired && *}
= ({ >
-
{t(`${I18N_PREFIX}.options`)}
+
{t(`${I18N_PREFIX}.options`)}
diff --git a/web/app/components/datasets/create/website/firecrawl/index.tsx b/web/app/components/datasets/create/website/firecrawl/index.tsx index 1746a1a2450bec..e394ac51f3aea9 100644 --- a/web/app/components/datasets/create/website/firecrawl/index.tsx +++ b/web/app/components/datasets/create/website/firecrawl/index.tsx @@ -36,8 +36,7 @@ const FireCrawl: FC = () => {
- - +
diff --git a/web/app/components/datasets/create/website/firecrawl/options.tsx b/web/app/components/datasets/create/website/firecrawl/options.tsx index 3062ba2d70772e..b882febd9d3c25 100644 --- a/web/app/components/datasets/create/website/firecrawl/options.tsx +++ b/web/app/components/datasets/create/website/firecrawl/options.tsx @@ -2,10 +2,13 @@ import type { FC } from 'react' import React, { useCallback } from 'react' import cn from 'classnames' +import { useTranslation } from 'react-i18next' import CheckboxWithLabel from './base/checkbox-with-label' import Field from './base/field' import type { CrawlOptions } from '@/models/datasets' +const I18N_PREFIX = 'datasetCreation.stepOne.website' + type Props = { className?: string payload: CrawlOptions @@ -17,6 +20,8 @@ const Options: FC = ({ payload, onChange, }) => { + const { t } = useTranslation() + const handleChange = useCallback((key: keyof CrawlOptions) => { return (value: any) => { onChange({ @@ -27,11 +32,15 @@ const Options: FC = ({ }, [payload, onChange]) return (
- +
= ({ /> = ({
- +
) } diff --git a/web/i18n/en-US/dataset-creation.ts b/web/i18n/en-US/dataset-creation.ts index 9bb86765520f59..56df52e89df810 100644 --- a/web/i18n/en-US/dataset-creation.ts +++ b/web/i18n/en-US/dataset-creation.ts @@ -59,6 +59,12 @@ const translation = { firecrawlDoc: 'Firecrawl docs', firecrawlDocLink: '', options: 'Options', + crawlSubPage: 'Crawl sub-pages', + limit: 'Limit', + maxDepth: 'Max depth', + excludePaths: 'Exclude paths', + includeOnlyPaths: 'Include only paths', + extractOnlyMainContent: 'Extract only main content (no headers, navs, footers, etc.)', }, }, stepTwo: { diff --git a/web/i18n/zh-Hans/dataset-creation.ts b/web/i18n/zh-Hans/dataset-creation.ts index 9c4793c0a393b4..c8ab88147b9906 100644 --- a/web/i18n/zh-Hans/dataset-creation.ts +++ b/web/i18n/zh-Hans/dataset-creation.ts @@ -59,6 +59,12 @@ const translation = { firecrawlDoc: 'Firecrawl 文档', firecrawlDocLink: '', options: '选项', + crawlSubPage: '爬取子页面', + limit: '限制数量', + maxDepth: '最大深度', + excludePaths: '排除路径', + includeOnlyPaths: '仅包含路径', + extractOnlyMainContent: '仅提取主要内容(无标题、导航、页脚等)', }, }, stepTwo: { From 446f24cb9fea7a198b0e9c6ca10f8f040a571e80 Mon Sep 17 00:00:00 2001 From: Joel Date: Wed, 5 Jun 2024 15:43:50 +0800 Subject: [PATCH 14/31] feat: valid crawl options --- .../create/website/firecrawl/base/input.tsx | 24 ++++++- .../website/firecrawl/base/url-input.tsx | 8 ++- .../create/website/firecrawl/index.tsx | 67 +++++++++++++++++-- web/i18n/en-US/common.ts | 4 ++ web/i18n/zh-Hans/common.ts | 4 ++ web/models/datasets.ts | 4 +- 6 files changed, 100 insertions(+), 11 deletions(-) diff --git a/web/app/components/datasets/create/website/firecrawl/base/input.tsx b/web/app/components/datasets/create/website/firecrawl/base/input.tsx index a042b1460a78ba..06249f57e7469a 100644 --- a/web/app/components/datasets/create/website/firecrawl/base/input.tsx +++ b/web/app/components/datasets/create/website/firecrawl/base/input.tsx @@ -9,6 +9,8 @@ type Props = { isNumber?: boolean } +const MIN_VALUE = 1 + const Input: FC = ({ value, onChange, @@ -18,14 +20,34 @@ const Input: FC = ({ const handleChange = useCallback((e: React.ChangeEvent) => { const value = e.target.value if (isNumber) { - onChange(parseFloat(value)) + let numberValue = parseInt(value, 10) // integer only + if (isNaN(numberValue)) { + onChange('') + return + } + if (numberValue < MIN_VALUE) + numberValue = MIN_VALUE + + onChange(numberValue) return } onChange(value) }, [isNumber, onChange]) + + const otherOption = (() => { + if (isNumber) { + return { + min: MIN_VALUE, + } + } + return { + + } + })() return ( = ({ onRun, }) => { const { t } = useTranslation() - const [url, setUrl] = useState('') - + const [url, setUrl] = useState('https://docs.dify.ai') // TODO: for test + const handleUrlChange = useCallback((url: string | number) => { + setUrl(url as string) + }, []) const handleOnRun = useCallback(() => { onRun(url) }, [onRun, url]) @@ -25,7 +27,7 @@ const UrlInput: FC = ({
)} diff --git a/web/app/components/datasets/create/website/firecrawl/index.tsx b/web/app/components/datasets/create/website/firecrawl/index.tsx index cca2cdf06d0c4d..271c2d1064dbfa 100644 --- a/web/app/components/datasets/create/website/firecrawl/index.tsx +++ b/web/app/components/datasets/create/website/firecrawl/index.tsx @@ -2,6 +2,7 @@ import type { FC } from 'react' import React, { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' +import cn from 'classnames' import Header from './header' import UrlInput from './base/url-input' import OptionsWrap from './base/options-wrap' @@ -13,6 +14,8 @@ import Toast from '@/app/components/base/toast' const ERROR_I18N_PREFIX = 'common.errorMsg' const I18N_PREFIX = 'datasetCreation.stepOne.website' +// const testCrawlErrorMsg = 'Firecrawl currently does not support social media scraping due to policy restrictions. We are actively working on building support for it.' + type Props = { } @@ -65,10 +68,11 @@ const FireCrawl: FC = () => { } }, [crawlOptions, t]) - const [isCrawlFinished, setIsCrawlFinished] = useState(false) + const [isCrawlFinished, setIsCrawlFinished] = useState(true) const [crawlErrorMsg, setCrawlErrorMsg] = useState('') - + const showCrawlError = isCrawlFinished && !!crawlErrorMsg const handleRun = useCallback((url: string) => { + setIsCrawlFinished(false) const { isValid, errorMsg } = checkValid(url) if (!isValid) { Toast.notify({ @@ -85,9 +89,9 @@ const FireCrawl: FC = () => { return (
-
+
- + {!isCrawlFinished ? ( diff --git a/web/app/components/datasets/create/website/firecrawl/options-error.tsx b/web/app/components/datasets/create/website/firecrawl/options-error.tsx deleted file mode 100644 index e69de29bb2d1d6..00000000000000 diff --git a/web/i18n/en-US/dataset-creation.ts b/web/i18n/en-US/dataset-creation.ts index 56df52e89df810..b059b6f0d1e885 100644 --- a/web/i18n/en-US/dataset-creation.ts +++ b/web/i18n/en-US/dataset-creation.ts @@ -65,6 +65,7 @@ const translation = { excludePaths: 'Exclude paths', includeOnlyPaths: 'Include only paths', extractOnlyMainContent: 'Extract only main content (no headers, navs, footers, etc.)', + exceptionErrorTitle: 'An exception occurred while running Firecrawl job:', }, }, stepTwo: { diff --git a/web/i18n/zh-Hans/dataset-creation.ts b/web/i18n/zh-Hans/dataset-creation.ts index c8ab88147b9906..23df0a9d537732 100644 --- a/web/i18n/zh-Hans/dataset-creation.ts +++ b/web/i18n/zh-Hans/dataset-creation.ts @@ -65,6 +65,8 @@ const translation = { excludePaths: '排除路径', includeOnlyPaths: '仅包含路径', extractOnlyMainContent: '仅提取主要内容(无标题、导航、页脚等)', + exceptionErrorTitle: '运行 Firecrawl 时发生异常:', + }, }, stepTwo: { From a278e922f9fb77f40805f57df9b2fec6fca15035 Mon Sep 17 00:00:00 2001 From: Joel Date: Wed, 5 Jun 2024 16:55:31 +0800 Subject: [PATCH 16/31] feat: crawl options struct --- .../website/firecrawl/crawled-result-item.tsx | 32 ++++++++++ .../website/firecrawl/crawled-result.tsx | 58 +++++++++++++++++++ .../create/website/firecrawl/index.tsx | 13 ++++- .../website/firecrawl/mock-crawl-result.ts | 24 ++++++++ web/models/datasets.ts | 7 +++ 5 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 web/app/components/datasets/create/website/firecrawl/crawled-result-item.tsx create mode 100644 web/app/components/datasets/create/website/firecrawl/crawled-result.tsx create mode 100644 web/app/components/datasets/create/website/firecrawl/mock-crawl-result.ts diff --git a/web/app/components/datasets/create/website/firecrawl/crawled-result-item.tsx b/web/app/components/datasets/create/website/firecrawl/crawled-result-item.tsx new file mode 100644 index 00000000000000..6e3735186f19a6 --- /dev/null +++ b/web/app/components/datasets/create/website/firecrawl/crawled-result-item.tsx @@ -0,0 +1,32 @@ +'use client' +import type { FC } from 'react' +import React, { useCallback } from 'react' +import type { CrawlResultItem as CrawlResultItemType } from '@/models/datasets' +import Checkbox from '@/app/components/base/checkbox' + +type Props = { + payload: CrawlResultItemType + isChecked: boolean + onCheckChange: (checked: boolean) => void +} + +const CrawledResultItem: FC = ({ + payload, + isChecked, + onCheckChange, +}) => { + const handleCheckChange = useCallback(() => { + onCheckChange(!isChecked) + }, [isChecked, onCheckChange]) + return ( +
+
+ +
{payload.title}
+
Preview
+
+
{payload.source_url}
+
+ ) +} +export default React.memo(CrawledResultItem) diff --git a/web/app/components/datasets/create/website/firecrawl/crawled-result.tsx b/web/app/components/datasets/create/website/firecrawl/crawled-result.tsx new file mode 100644 index 00000000000000..9df4b2b521679c --- /dev/null +++ b/web/app/components/datasets/create/website/firecrawl/crawled-result.tsx @@ -0,0 +1,58 @@ +'use client' +import type { FC } from 'react' +import React, { useCallback } from 'react' +import CheckboxWithLabel from './base/checkbox-with-label' +import CrawledResultItem from './crawled-result-item' +import type { CrawlResultItem } from '@/models/datasets' + +type Props = { + list: CrawlResultItem[] + checkedList: CrawlResultItem[] + onSelectedChange: (selected: CrawlResultItem[]) => void +} + +const CrawledResult: FC = ({ + list, + checkedList, + onSelectedChange, +}) => { + const isCheckAll = checkedList.length === list.length + + const handleCheckedAll = useCallback(() => { + if (!isCheckAll) + onSelectedChange(list) + + else + onSelectedChange([]) + }, [isCheckAll, list, onSelectedChange]) + + const handleItemCheckChange = useCallback((item: CrawlResultItem) => { + return (checked: boolean) => { + if (checked) + onSelectedChange([...checkedList, item]) + + else + onSelectedChange(checkedList.filter(checkedItem => checkedItem.source_url !== item.source_url)) + } + }, [checkedList, onSelectedChange]) + + return ( +
+
+ +
Scraped 10 pages in total within 12.4 seconds
+
+
+ {list.map(item => ( + checkedItem.source_url === item.source_url)} + onCheckChange={handleItemCheckChange(item)} + /> + ))} +
+
+ ) +} +export default React.memo(CrawledResult) diff --git a/web/app/components/datasets/create/website/firecrawl/index.tsx b/web/app/components/datasets/create/website/firecrawl/index.tsx index 271c2d1064dbfa..fe640f1d25bea9 100644 --- a/web/app/components/datasets/create/website/firecrawl/index.tsx +++ b/web/app/components/datasets/create/website/firecrawl/index.tsx @@ -7,8 +7,10 @@ import Header from './header' import UrlInput from './base/url-input' import OptionsWrap from './base/options-wrap' import Options from './options' +import mockCrawlResult from './mock-crawl-result' +import CrawledResult from './crawled-result' import { useModalContext } from '@/context/modal-context' -import type { CrawlOptions } from '@/models/datasets' +import type { CrawlOptions, CrawlResultItem } from '@/models/datasets' import Toast from '@/app/components/base/toast' const ERROR_I18N_PREFIX = 'common.errorMsg' @@ -69,6 +71,9 @@ const FireCrawl: FC = () => { }, [crawlOptions, t]) const [isCrawlFinished, setIsCrawlFinished] = useState(true) + const [crawlResult, setCrawlResult] = useState(mockCrawlResult) + const [checkedCrawlResult, setCheckedCrawlResult] = useState([]) + const [crawlErrorMsg, setCrawlErrorMsg] = useState('') const showCrawlError = isCrawlFinished && !!crawlErrorMsg const handleRun = useCallback((url: string) => { @@ -97,7 +102,11 @@ const FireCrawl: FC = () => { ) : ( -
Result list
+ )}
diff --git a/web/app/components/datasets/create/website/firecrawl/mock-crawl-result.ts b/web/app/components/datasets/create/website/firecrawl/mock-crawl-result.ts new file mode 100644 index 00000000000000..8fd5e6636f1617 --- /dev/null +++ b/web/app/components/datasets/create/website/firecrawl/mock-crawl-result.ts @@ -0,0 +1,24 @@ +import type { CrawlResultItem } from '@/models/datasets' + +const result: CrawlResultItem[] = [ + { + title: 'Start the frontend Docker container separately', + markdown: 'Markdown 1', + description: 'Description 1', + source_url: 'https://example.com/1', + }, + { + title: 'Advanced Tool Integration', + markdown: 'Markdown 2', + description: 'Description 2', + source_url: 'https://example.com/2', + }, + { + title: 'Local Source Code Start | English | Dify', + markdown: 'Markdown 3', + description: 'Description 3', + source_url: 'https://example.com/3', + }, +] + +export default result diff --git a/web/models/datasets.ts b/web/models/datasets.ts index d3b752aaf576af..1edb63748faadd 100644 --- a/web/models/datasets.ts +++ b/web/models/datasets.ts @@ -48,6 +48,13 @@ export type CrawlOptions = { max_depth: number | string } +export type CrawlResultItem = { + title: string + markdown: string + description: string + source_url: string +} + export type FileItem = { fileID: string file: CustomFile From eff80c8d27a3d6b12c141bb69bd477c7aa6e4dee Mon Sep 17 00:00:00 2001 From: Joel Date: Wed, 5 Jun 2024 18:20:29 +0800 Subject: [PATCH 17/31] feat: scrawl loading --- .../icons/assets/public/other/row-struct.svg | 5 ++ .../icons/src/public/other/RowStruct.json | 56 ++++++++++++++++++ .../base/icons/src/public/other/RowStruct.tsx | 16 +++++ .../base/icons/src/public/other/index.ts | 1 + .../website/firecrawl/base/options-wrap.tsx | 6 +- .../website/firecrawl/base/url-input.tsx | 9 ++- .../create/website/firecrawl/crawling.tsx | 37 ++++++++++++ .../create/website/firecrawl/index.tsx | 59 +++++++++++++------ web/i18n/en-US/dataset-creation.ts | 1 + web/i18n/zh-Hans/dataset-creation.ts | 2 +- 10 files changed, 168 insertions(+), 24 deletions(-) create mode 100644 web/app/components/base/icons/assets/public/other/row-struct.svg create mode 100644 web/app/components/base/icons/src/public/other/RowStruct.json create mode 100644 web/app/components/base/icons/src/public/other/RowStruct.tsx create mode 100644 web/app/components/datasets/create/website/firecrawl/crawling.tsx diff --git a/web/app/components/base/icons/assets/public/other/row-struct.svg b/web/app/components/base/icons/assets/public/other/row-struct.svg new file mode 100644 index 00000000000000..ba275ffeec9079 --- /dev/null +++ b/web/app/components/base/icons/assets/public/other/row-struct.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web/app/components/base/icons/src/public/other/RowStruct.json b/web/app/components/base/icons/src/public/other/RowStruct.json new file mode 100644 index 00000000000000..0d1ef43f4f5d5d --- /dev/null +++ b/web/app/components/base/icons/src/public/other/RowStruct.json @@ -0,0 +1,56 @@ +{ + "icon": { + "type": "element", + "isRootNode": true, + "name": "svg", + "attributes": { + "width": "624", + "height": "48", + "viewBox": "0 0 624 48", + "fill": "none", + "xmlns": "http://www.w3.org/2000/svg" + }, + "children": [ + { + "type": "element", + "name": "rect", + "attributes": { + "x": "8", + "y": "7", + "width": "16", + "height": "16", + "rx": "5", + "fill": "#F2F4F7" + }, + "children": [] + }, + { + "type": "element", + "name": "rect", + "attributes": { + "x": "32", + "y": "10", + "width": "233", + "height": "10", + "rx": "3", + "fill": "#EAECF0" + }, + "children": [] + }, + { + "type": "element", + "name": "rect", + "attributes": { + "x": "32", + "y": "31", + "width": "345", + "height": "6", + "rx": "3", + "fill": "#F2F4F7" + }, + "children": [] + } + ] + }, + "name": "RowStruct" +} \ No newline at end of file diff --git a/web/app/components/base/icons/src/public/other/RowStruct.tsx b/web/app/components/base/icons/src/public/other/RowStruct.tsx new file mode 100644 index 00000000000000..ef5ab8c62df79a --- /dev/null +++ b/web/app/components/base/icons/src/public/other/RowStruct.tsx @@ -0,0 +1,16 @@ +// GENERATE BY script +// DON NOT EDIT IT MANUALLY + +import * as React from 'react' +import data from './RowStruct.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 = 'RowStruct' + +export default Icon diff --git a/web/app/components/base/icons/src/public/other/index.ts b/web/app/components/base/icons/src/public/other/index.ts index adf723edb7db55..257ba59b0d0131 100644 --- a/web/app/components/base/icons/src/public/other/index.ts +++ b/web/app/components/base/icons/src/public/other/index.ts @@ -1,2 +1,3 @@ export { default as Icon3Dots } from './Icon3Dots' export { default as DefaultToolIcon } from './DefaultToolIcon' +export { default as RowStruct } from './RowStruct' diff --git a/web/app/components/datasets/create/website/firecrawl/base/options-wrap.tsx b/web/app/components/datasets/create/website/firecrawl/base/options-wrap.tsx index 8a27012ebb5227..af427249aa8291 100644 --- a/web/app/components/datasets/create/website/firecrawl/base/options-wrap.tsx +++ b/web/app/components/datasets/create/website/firecrawl/base/options-wrap.tsx @@ -12,6 +12,7 @@ const I18N_PREFIX = 'datasetCreation.stepOne.website' type Props = { className?: string children: React.ReactNode + isFilledFull?: boolean errorMsg?: string } @@ -19,6 +20,7 @@ const OptionsWrap: FC = ({ className = '', children, errorMsg, + isFilledFull = false, }) => { const { t } = useTranslation() @@ -38,11 +40,11 @@ const OptionsWrap: FC = ({
{!fold && ( -
+
{!errorMsg ? children : ( - + )}
)} diff --git a/web/app/components/datasets/create/website/firecrawl/base/url-input.tsx b/web/app/components/datasets/create/website/firecrawl/base/url-input.tsx index 1b572eb68e6f41..90acc769be6ce8 100644 --- a/web/app/components/datasets/create/website/firecrawl/base/url-input.tsx +++ b/web/app/components/datasets/create/website/firecrawl/base/url-input.tsx @@ -8,10 +8,12 @@ import Button from '@/app/components/base/button' const I18N_PREFIX = 'datasetCreation.stepOne.website' type Props = { + isRunning: boolean onRun: (url: string) => void } const UrlInput: FC = ({ + isRunning, onRun, }) => { const { t } = useTranslation() @@ -20,8 +22,10 @@ const UrlInput: FC = ({ setUrl(url as string) }, []) const handleOnRun = useCallback(() => { + if (isRunning) + return onRun(url) - }, [onRun, url]) + }, [isRunning, onRun, url]) return (
@@ -34,8 +38,9 @@ const UrlInput: FC = ({ type='primary' onClick={handleOnRun} className='ml-2 !h-8 text-[13px] font-medium' + loading={isRunning} > - {t(`${I18N_PREFIX}.run`)} + {!isRunning ? t(`${I18N_PREFIX}.run`) : ''}
) diff --git a/web/app/components/datasets/create/website/firecrawl/crawling.tsx b/web/app/components/datasets/create/website/firecrawl/crawling.tsx new file mode 100644 index 00000000000000..97b2b01d2eeb61 --- /dev/null +++ b/web/app/components/datasets/create/website/firecrawl/crawling.tsx @@ -0,0 +1,37 @@ +'use client' +import type { FC } from 'react' +import React from 'react' +import cn from 'classnames' +import { useTranslation } from 'react-i18next' +import { RowStruct } from '@/app/components/base/icons/src/public/other' + +type Props = { + className?: string + crawledNum: number + totalNum: number +} + +const Crawling: FC = ({ + className = '', + crawledNum, + totalNum, +}) => { + const { t } = useTranslation() + + return ( +
+
+ {t('datasetCreation.stepOne.website.totalPageScraped')} {crawledNum}/{totalNum} +
+ +
+ {['', '', '', ''].map((item, index) => ( +
+ +
+ ))} +
+
+ ) +} +export default React.memo(Crawling) diff --git a/web/app/components/datasets/create/website/firecrawl/index.tsx b/web/app/components/datasets/create/website/firecrawl/index.tsx index fe640f1d25bea9..234ecc4e4b629e 100644 --- a/web/app/components/datasets/create/website/firecrawl/index.tsx +++ b/web/app/components/datasets/create/website/firecrawl/index.tsx @@ -9,9 +9,11 @@ import OptionsWrap from './base/options-wrap' import Options from './options' import mockCrawlResult from './mock-crawl-result' import CrawledResult from './crawled-result' +import Crawling from './crawling' import { useModalContext } from '@/context/modal-context' import type { CrawlOptions, CrawlResultItem } from '@/models/datasets' import Toast from '@/app/components/base/toast' +import { sleep } from '@/utils' const ERROR_I18N_PREFIX = 'common.errorMsg' const I18N_PREFIX = 'datasetCreation.stepOne.website' @@ -30,8 +32,15 @@ const DEFAULT_CRAWL_OPTIONS: CrawlOptions = { max_depth: 2, } +enum Step { + init = 'init', + running = 'running', + finished = 'finished', +} + const FireCrawl: FC = () => { const { t } = useTranslation() + const [step, setStep] = useState(Step.running) const { setShowAccountSettingModal } = useModalContext() const handleSetting = useCallback(() => { @@ -70,14 +79,15 @@ const FireCrawl: FC = () => { } }, [crawlOptions, t]) - const [isCrawlFinished, setIsCrawlFinished] = useState(true) + const isInit = step === Step.init + const isCrawlFinished = step === Step.finished + const isRunning = step === Step.running const [crawlResult, setCrawlResult] = useState(mockCrawlResult) const [checkedCrawlResult, setCheckedCrawlResult] = useState([]) const [crawlErrorMsg, setCrawlErrorMsg] = useState('') - const showCrawlError = isCrawlFinished && !!crawlErrorMsg - const handleRun = useCallback((url: string) => { - setIsCrawlFinished(false) + const showCrawlError = step === Step.finished && !!crawlErrorMsg + const handleRun = useCallback(async (url: string) => { const { isValid, errorMsg } = checkValid(url) if (!isValid) { Toast.notify({ @@ -86,28 +96,39 @@ const FireCrawl: FC = () => { }) return } + setStep(Step.running) // TODO: crawl - setIsCrawlFinished(true) + await sleep(2000) + setCrawlResult(mockCrawlResult) // TODO: + + setStep(Step.finished) setCrawlErrorMsg('') }, [checkValid]) return (
-
- - - {!isCrawlFinished - ? ( - - ) - : ( - - )} +
+ + + {isInit && } + {isRunning + && } + {isCrawlFinished && ( + + )}
diff --git a/web/i18n/en-US/dataset-creation.ts b/web/i18n/en-US/dataset-creation.ts index b059b6f0d1e885..87b1e57862f812 100644 --- a/web/i18n/en-US/dataset-creation.ts +++ b/web/i18n/en-US/dataset-creation.ts @@ -66,6 +66,7 @@ const translation = { includeOnlyPaths: 'Include only paths', extractOnlyMainContent: 'Extract only main content (no headers, navs, footers, etc.)', exceptionErrorTitle: 'An exception occurred while running Firecrawl job:', + totalPageScraped: 'Total pages scraped:', }, }, stepTwo: { diff --git a/web/i18n/zh-Hans/dataset-creation.ts b/web/i18n/zh-Hans/dataset-creation.ts index 23df0a9d537732..a0be0f0cc100a2 100644 --- a/web/i18n/zh-Hans/dataset-creation.ts +++ b/web/i18n/zh-Hans/dataset-creation.ts @@ -66,7 +66,7 @@ const translation = { includeOnlyPaths: '仅包含路径', extractOnlyMainContent: '仅提取主要内容(无标题、导航、页脚等)', exceptionErrorTitle: '运行 Firecrawl 时发生异常:', - + totalPageScraped: '抓取页面总数:', }, }, stepTwo: { From 0448c49c7ac8a8a3f89f0ea71242d05466904cd8 Mon Sep 17 00:00:00 2001 From: Joel Date: Wed, 5 Jun 2024 18:33:40 +0800 Subject: [PATCH 18/31] feat: result --- .../firecrawl/base/checkbox-with-label.tsx | 4 +++- .../website/firecrawl/base/options-wrap.tsx | 2 +- .../website/firecrawl/crawled-result.tsx | 22 ++++++++++++++----- .../create/website/firecrawl/index.tsx | 3 +-- web/i18n/en-US/dataset-creation.ts | 3 +++ web/i18n/zh-Hans/dataset-creation.ts | 3 +++ 6 files changed, 28 insertions(+), 9 deletions(-) diff --git a/web/app/components/datasets/create/website/firecrawl/base/checkbox-with-label.tsx b/web/app/components/datasets/create/website/firecrawl/base/checkbox-with-label.tsx index f25e45724512e7..ed5d2efd511711 100644 --- a/web/app/components/datasets/create/website/firecrawl/base/checkbox-with-label.tsx +++ b/web/app/components/datasets/create/website/firecrawl/base/checkbox-with-label.tsx @@ -9,6 +9,7 @@ type Props = { isChecked: boolean onChange: (isChecked: boolean) => void label: string + labelClassName?: string } const CheckboxWithLabel: FC = ({ @@ -16,11 +17,12 @@ const CheckboxWithLabel: FC = ({ isChecked, onChange, label, + labelClassName, }) => { return ( ) } diff --git a/web/app/components/datasets/create/website/firecrawl/base/options-wrap.tsx b/web/app/components/datasets/create/website/firecrawl/base/options-wrap.tsx index af427249aa8291..61c89fc10745df 100644 --- a/web/app/components/datasets/create/website/firecrawl/base/options-wrap.tsx +++ b/web/app/components/datasets/create/website/firecrawl/base/options-wrap.tsx @@ -44,7 +44,7 @@ const OptionsWrap: FC = ({ {!errorMsg ? children : ( - + )}
)} diff --git a/web/app/components/datasets/create/website/firecrawl/crawled-result.tsx b/web/app/components/datasets/create/website/firecrawl/crawled-result.tsx index 9df4b2b521679c..48cca13b93ad3b 100644 --- a/web/app/components/datasets/create/website/firecrawl/crawled-result.tsx +++ b/web/app/components/datasets/create/website/firecrawl/crawled-result.tsx @@ -1,10 +1,13 @@ 'use client' import type { FC } from 'react' import React, { useCallback } from 'react' +import { useTranslation } from 'react-i18next' import CheckboxWithLabel from './base/checkbox-with-label' import CrawledResultItem from './crawled-result-item' import type { CrawlResultItem } from '@/models/datasets' +const I18N_PREFIX = 'datasetCreation.stepOne.website' + type Props = { list: CrawlResultItem[] checkedList: CrawlResultItem[] @@ -16,6 +19,8 @@ const CrawledResult: FC = ({ checkedList, onSelectedChange, }) => { + const { t } = useTranslation() + const isCheckAll = checkedList.length === list.length const handleCheckedAll = useCallback(() => { @@ -37,12 +42,19 @@ const CrawledResult: FC = ({ }, [checkedList, onSelectedChange]) return ( -
-
- -
Scraped 10 pages in total within 12.4 seconds
+
+
+ +
{t(`${I18N_PREFIX}.scrapTimeInfo`, { + total: list.length, + time: '12.4 seconds', + })}
-
+
{list.map(item => ( = () => { const { t } = useTranslation() - const [step, setStep] = useState(Step.running) + const [step, setStep] = useState(Step.finished) const { setShowAccountSettingModal } = useModalContext() const handleSetting = useCallback(() => { @@ -86,7 +86,6 @@ const FireCrawl: FC = () => { const [checkedCrawlResult, setCheckedCrawlResult] = useState([]) const [crawlErrorMsg, setCrawlErrorMsg] = useState('') - const showCrawlError = step === Step.finished && !!crawlErrorMsg const handleRun = useCallback(async (url: string) => { const { isValid, errorMsg } = checkValid(url) if (!isValid) { diff --git a/web/i18n/en-US/dataset-creation.ts b/web/i18n/en-US/dataset-creation.ts index 87b1e57862f812..50b8669b4b25ce 100644 --- a/web/i18n/en-US/dataset-creation.ts +++ b/web/i18n/en-US/dataset-creation.ts @@ -67,6 +67,9 @@ const translation = { extractOnlyMainContent: 'Extract only main content (no headers, navs, footers, etc.)', exceptionErrorTitle: 'An exception occurred while running Firecrawl job:', totalPageScraped: 'Total pages scraped:', + selectAll: 'Select All', + resetAll: 'Reset All', + scrapTimeInfo: 'Scraped {{total}} pages in total within {{time}}', }, }, stepTwo: { diff --git a/web/i18n/zh-Hans/dataset-creation.ts b/web/i18n/zh-Hans/dataset-creation.ts index a0be0f0cc100a2..e801c44261ce5d 100644 --- a/web/i18n/zh-Hans/dataset-creation.ts +++ b/web/i18n/zh-Hans/dataset-creation.ts @@ -67,6 +67,9 @@ const translation = { extractOnlyMainContent: '仅提取主要内容(无标题、导航、页脚等)', exceptionErrorTitle: '运行 Firecrawl 时发生异常:', totalPageScraped: '抓取页面总数:', + selectAll: '全选', + resetAll: '重置全部', + scrapTimeInfo: '总共在 {{time}} 内抓取了 {{total}} 个页面', }, }, stepTwo: { From c8294ac6e55a51a0a9972fbb3fba3a77022834f5 Mon Sep 17 00:00:00 2001 From: Joel Date: Thu, 6 Jun 2024 11:10:31 +0800 Subject: [PATCH 19/31] feat: crawl result --- .../website/firecrawl/crawled-result-item.tsx | 16 ++++++++++++---- .../create/website/firecrawl/crawled-result.tsx | 11 ++++++++++- web/i18n/en-US/dataset-creation.ts | 1 + web/i18n/zh-Hans/dataset-creation.ts | 1 + 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/web/app/components/datasets/create/website/firecrawl/crawled-result-item.tsx b/web/app/components/datasets/create/website/firecrawl/crawled-result-item.tsx index 6e3735186f19a6..1730314b477924 100644 --- a/web/app/components/datasets/create/website/firecrawl/crawled-result-item.tsx +++ b/web/app/components/datasets/create/website/firecrawl/crawled-result-item.tsx @@ -1,31 +1,39 @@ 'use client' import type { FC } from 'react' import React, { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import cn from 'classnames' import type { CrawlResultItem as CrawlResultItemType } from '@/models/datasets' import Checkbox from '@/app/components/base/checkbox' type Props = { payload: CrawlResultItemType isChecked: boolean + isPreview: boolean onCheckChange: (checked: boolean) => void + onPreview: () => void } const CrawledResultItem: FC = ({ + isPreview, payload, isChecked, onCheckChange, + onPreview, }) => { + const { t } = useTranslation() + const handleCheckChange = useCallback(() => { onCheckChange(!isChecked) }, [isChecked, onCheckChange]) return ( -
+
- +
{payload.title}
-
Preview
+
{t('datasetCreation.stepOne.website.preview')}
-
{payload.source_url}
+
{payload.source_url}
) } diff --git a/web/app/components/datasets/create/website/firecrawl/crawled-result.tsx b/web/app/components/datasets/create/website/firecrawl/crawled-result.tsx index 48cca13b93ad3b..5e92ce45a1bc1a 100644 --- a/web/app/components/datasets/create/website/firecrawl/crawled-result.tsx +++ b/web/app/components/datasets/create/website/firecrawl/crawled-result.tsx @@ -41,6 +41,13 @@ const CrawledResult: FC = ({ } }, [checkedList, onSelectedChange]) + const [previewIndex, setPreviewIndex] = React.useState(-1) + const handlePreview = useCallback((index: number) => { + return () => { + setPreviewIndex(index) + } + }, []) + return (
@@ -55,9 +62,11 @@ const CrawledResult: FC = ({ })}
- {list.map(item => ( + {list.map((item, index) => ( checkedItem.source_url === item.source_url)} onCheckChange={handleItemCheckChange(item)} diff --git a/web/i18n/en-US/dataset-creation.ts b/web/i18n/en-US/dataset-creation.ts index 50b8669b4b25ce..baa32587d92582 100644 --- a/web/i18n/en-US/dataset-creation.ts +++ b/web/i18n/en-US/dataset-creation.ts @@ -70,6 +70,7 @@ const translation = { selectAll: 'Select All', resetAll: 'Reset All', scrapTimeInfo: 'Scraped {{total}} pages in total within {{time}}', + preview: 'Preview', }, }, stepTwo: { diff --git a/web/i18n/zh-Hans/dataset-creation.ts b/web/i18n/zh-Hans/dataset-creation.ts index e801c44261ce5d..c8866fbd3b2d1b 100644 --- a/web/i18n/zh-Hans/dataset-creation.ts +++ b/web/i18n/zh-Hans/dataset-creation.ts @@ -70,6 +70,7 @@ const translation = { selectAll: '全选', resetAll: '重置全部', scrapTimeInfo: '总共在 {{time}} 内抓取了 {{total}} 个页面', + preview: '预览', }, }, stepTwo: { From 74c24ccceddc06015c6d6b0beb025e88620ed299 Mon Sep 17 00:00:00 2001 From: Joel Date: Thu, 6 Jun 2024 14:49:22 +0800 Subject: [PATCH 20/31] feat: handle preview --- .../datasets/create/step-one/index.tsx | 21 +++++++++- .../website/firecrawl/crawled-result.tsx | 5 ++- .../create/website/firecrawl/index.tsx | 8 +++- .../datasets/create/website/index.tsx | 9 ++-- .../datasets/create/website/preview.tsx | 41 +++++++++++++++++++ 5 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 web/app/components/datasets/create/website/preview.tsx diff --git a/web/app/components/datasets/create/step-one/index.tsx b/web/app/components/datasets/create/step-one/index.tsx index 7d46d5eb5d0bc1..dc1fd24ae2f5db 100644 --- a/web/app/components/datasets/create/step-one/index.tsx +++ b/web/app/components/datasets/create/step-one/index.tsx @@ -7,8 +7,9 @@ import FileUploader from '../file-uploader' import NotionPagePreview from '../notion-page-preview' import EmptyDatasetCreationModal from '../empty-dataset-creation-modal' import Website from '../website' +import WebsitePreview from '../website/preview' import s from './index.module.css' -import type { FileItem } from '@/models/datasets' +import type { CrawlResultItem, FileItem } from '@/models/datasets' import type { NotionPage } from '@/models/common' import { DataSourceType } from '@/models/datasets' import Button from '@/app/components/base/button' @@ -66,6 +67,7 @@ const StepOne = ({ const [showModal, setShowModal] = useState(false) const [currentFile, setCurrentFile] = useState() const [currentNotionPage, setCurrentNotionPage] = useState() + const [currentWebsite, setCurrentWebsite] = useState() const { t } = useTranslation() const modalShowHandle = () => setShowModal(true) @@ -86,6 +88,10 @@ const StepOne = ({ setCurrentNotionPage(undefined) } + const hideWebsitePreview = () => { + setCurrentWebsite(undefined) + } + const shouldShowDataSourceTypeList = !datasetId || (datasetId && !dataset?.data_source_type) const { plan, enableBilling } = useProviderContext() @@ -202,7 +208,17 @@ const StepOne = ({ )} {dataSourceType === DataSourceType.WEB && ( - + <> +
+ +
+ {isShowVectorSpaceFull && ( +
+ +
+ )} + + )} {!datasetId && ( <> @@ -215,6 +231,7 @@ const StepOne = ({
{currentFile && } {currentNotionPage && } + {currentWebsite && }
) } diff --git a/web/app/components/datasets/create/website/firecrawl/crawled-result.tsx b/web/app/components/datasets/create/website/firecrawl/crawled-result.tsx index 5e92ce45a1bc1a..bf451e2eaa8a71 100644 --- a/web/app/components/datasets/create/website/firecrawl/crawled-result.tsx +++ b/web/app/components/datasets/create/website/firecrawl/crawled-result.tsx @@ -12,12 +12,14 @@ type Props = { list: CrawlResultItem[] checkedList: CrawlResultItem[] onSelectedChange: (selected: CrawlResultItem[]) => void + onPreview: (payload: CrawlResultItem) => void } const CrawledResult: FC = ({ list, checkedList, onSelectedChange, + onPreview, }) => { const { t } = useTranslation() @@ -45,8 +47,9 @@ const CrawledResult: FC = ({ const handlePreview = useCallback((index: number) => { return () => { setPreviewIndex(index) + onPreview(list[index]) } - }, []) + }, [list, onPreview]) return (
diff --git a/web/app/components/datasets/create/website/firecrawl/index.tsx b/web/app/components/datasets/create/website/firecrawl/index.tsx index 7c27d96803a84e..1007551978d09e 100644 --- a/web/app/components/datasets/create/website/firecrawl/index.tsx +++ b/web/app/components/datasets/create/website/firecrawl/index.tsx @@ -21,8 +21,9 @@ const I18N_PREFIX = 'datasetCreation.stepOne.website' // const testCrawlErrorMsg = 'Firecrawl currently does not support social media scraping due to policy restrictions. We are actively working on building support for it.' type Props = { - + onPreview: (payload: CrawlResultItem) => void } + const DEFAULT_CRAWL_OPTIONS: CrawlOptions = { crawl_sub_pages: true, only_main_content: true, @@ -38,7 +39,9 @@ enum Step { finished = 'finished', } -const FireCrawl: FC = () => { +const FireCrawl: FC = ({ + onPreview, +}) => { const { t } = useTranslation() const [step, setStep] = useState(Step.finished) @@ -126,6 +129,7 @@ const FireCrawl: FC = () => { list={crawlResult} checkedList={checkedCrawlResult} onSelectedChange={setCheckedCrawlResult} + onPreview={onPreview} /> )} diff --git a/web/app/components/datasets/create/website/index.tsx b/web/app/components/datasets/create/website/index.tsx index b85d986f798418..d3f94e314995ca 100644 --- a/web/app/components/datasets/create/website/index.tsx +++ b/web/app/components/datasets/create/website/index.tsx @@ -4,12 +4,15 @@ import React, { useCallback, useState } from 'react' import NoData from './no-data' import Firecrawl from './firecrawl' import { useModalContext } from '@/context/modal-context' +import type { CrawlResultItem } from '@/models/datasets' type Props = { - + onPreview: (payload: CrawlResultItem) => void } -const WebsitePreview: FC = () => { +const WebsitePreview: FC = ({ + onPreview, +}) => { const { setShowAccountSettingModal } = useModalContext() const [isLoaded, setIsLoaded] = useState(false) const [isConfigured, setIsConfigured] = useState(true) @@ -29,7 +32,7 @@ const WebsitePreview: FC = () => {
{isConfigured ? ( - + ) : ( diff --git a/web/app/components/datasets/create/website/preview.tsx b/web/app/components/datasets/create/website/preview.tsx new file mode 100644 index 00000000000000..322ce43b174d5b --- /dev/null +++ b/web/app/components/datasets/create/website/preview.tsx @@ -0,0 +1,41 @@ +'use client' +import React from 'react' +import { useTranslation } from 'react-i18next' +import cn from 'classnames' +import { XMarkIcon } from '@heroicons/react/20/solid' +import s from '../file-preview/index.module.css' +import type { CrawlResultItem } from '@/models/datasets' + +type IProps = { + payload: CrawlResultItem + hidePreview: () => void +} + +const WebsitePreview = ({ + payload, + hidePreview, +}: IProps) => { + const { t } = useTranslation() + + return ( +
+
+
+ {t('datasetCreation.stepOne.pagePreview')} +
+ +
+
+
+ {payload.title} +
+
{payload.source_url}
+
+
+
{payload.markdown}
+
+
+ ) +} + +export default WebsitePreview From efecdccf35beed86353287216fabd118796924c5 Mon Sep 17 00:00:00 2001 From: Joel Date: Thu, 6 Jun 2024 15:01:58 +0800 Subject: [PATCH 21/31] feat: support login by given mail (#4991) --- web/app/layout.tsx | 3 ++- web/app/signin/normalForm.tsx | 8 +++++--- web/config/index.ts | 2 ++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/web/app/layout.tsx b/web/app/layout.tsx index ca665fb360a366..fedf66045adac1 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -39,11 +39,12 @@ const LocaleLayout = ({ data-api-prefix={process.env.NEXT_PUBLIC_API_PREFIX} data-pubic-api-prefix={process.env.NEXT_PUBLIC_PUBLIC_API_PREFIX} data-public-edition={process.env.NEXT_PUBLIC_EDITION} + data-public-support-mail-login={process.env.NEXT_PUBLIC_SUPPORT_MAIL_LOGIN} data-public-sentry-dsn={process.env.NEXT_PUBLIC_SENTRY_DSN} data-public-maintenance-notice={process.env.NEXT_PUBLIC_MAINTENANCE_NOTICE} data-public-site-about={process.env.NEXT_PUBLIC_SITE_ABOUT} > - + {children} diff --git a/web/app/signin/normalForm.tsx b/web/app/signin/normalForm.tsx index aba658b3ce2298..f6abf0d6c0f383 100644 --- a/web/app/signin/normalForm.tsx +++ b/web/app/signin/normalForm.tsx @@ -7,7 +7,7 @@ import useSWR from 'swr' import Link from 'next/link' import Toast from '../components/base/toast' import style from './page.module.css' -import { IS_CE_EDITION, apiPrefix } from '@/config' +import { IS_CE_EDITION, SUPPORT_MAIL_LOGIN, apiPrefix } from '@/config' import Button from '@/app/components/base/button' import { login, oauth } from '@/service/common' import { getPurifyHref } from '@/utils' @@ -62,6 +62,8 @@ function reducer(state: IState, action: IAction) { const NormalForm = () => { const { t } = useTranslation() + const useEmailLogin = IS_CE_EDITION || SUPPORT_MAIL_LOGIN + const router = useRouter() const [state, dispatch] = useReducer(reducer, { @@ -150,7 +152,7 @@ const NormalForm = () => {
- {!IS_CE_EDITION && ( + {!useEmailLogin && (
@@ -194,7 +196,7 @@ const NormalForm = () => { )} { - IS_CE_EDITION && <> + useEmailLogin && <> {/*