Commit cd042254 authored by JzoNg's avatar JzoNg

feat: support notion page preview

parent 1ee00cba
......@@ -11,6 +11,9 @@
}
.previewHeader .title {
display: flex;
justify-content: space-between;
align-items: center;
color: #101828;
font-weight: 600;
font-size: 18px;
......
......@@ -2,12 +2,14 @@
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import cn from 'classnames'
import { XMarkIcon } from '@heroicons/react/20/solid'
import s from './index.module.css'
import type { File } from '@/models/datasets'
import { fetchFilePreview } from '@/service/common'
type IProps = {
file?: File
notionPage?: any
hidePreview: () => void
}
......@@ -46,9 +48,9 @@ const FilePreview = ({
<div className={cn(s.previewHeader)}>
<div className={cn(s.title)}>
<span>{t('datasetCreation.stepOne.filePreview')}</span>
{false && (
<div className='flex items-center justify-center w-6 h-6 cursor-pointer' onClick={hidePreview}></div>
)}
<div className='flex items-center justify-center w-6 h-6 cursor-pointer' onClick={hidePreview}>
<XMarkIcon className='h-4 w-4'></XMarkIcon>
</div>
</div>
<div className={cn(s.fileName)}>
<span>{getFileName(file)}</span><span className={cn(s.filetype)}>.{file?.extension}</span>
......
.filePreview {
@apply flex flex-col border-l border-gray-200 shrink-0;
width: 528px;
background-color: #fcfcfd;
}
.previewHeader {
@apply border-b border-gray-200 shrink-0;
margin: 42px 32px 0;
padding-bottom: 16px;
}
.previewHeader .title {
display: flex;
justify-content: space-between;
align-items: center;
color: #101828;
font-weight: 600;
font-size: 18px;
line-height: 28px;
}
.previewHeader .fileName {
display: flex;
align-items: center;
font-weight: 400;
font-size: 12px;
line-height: 18px;
color: #1D2939;
}
.previewHeader .filetype {
color: #667085;
}
.previewContent {
@apply overflow-y-auto grow;
padding: 20px 32px;
font-weight: 400;
font-size: 16px;
line-height: 24px;
color: #344054;
}
.previewContent .loading {
width: 100%;
height: 180px;
background: #f9fafb center no-repeat url(../assets/Loading.svg);
background-size: contain;
}
.fileContent {
white-space: pre-line;
}
\ No newline at end of file
'use client'
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import cn from 'classnames'
import { XMarkIcon } from '@heroicons/react/20/solid'
import s from './index.module.css'
import type { DataSourceNotionPage } from '@/models/common'
import NotionIcon from '@/app/components/base/notion-icon'
import { fetchNotionPagePreview } from '@/service/datasets'
type Page = DataSourceNotionPage & { workspace_id: string }
type IProps = {
currentPage?: Page
hidePreview: () => void
}
const NotionPagePreview = ({
currentPage,
hidePreview,
}: IProps) => {
const { t } = useTranslation()
const [previewContent, setPreviewContent] = useState('')
const [loading, setLoading] = useState(true)
const getPreviewContent = async () => {
if (!currentPage)
return
try {
const res = await fetchNotionPagePreview({
workspaceID: currentPage.workspace_id,
pageID: currentPage.page_id,
})
setPreviewContent(res.content)
setLoading(false)
}
catch {}
}
const getIcon = () => {
let iconSrc
if (currentPage) {
if (currentPage.page_icon && currentPage.page_icon.type === 'url')
iconSrc = currentPage.page_icon.url
if (currentPage.page_icon && currentPage.page_icon.type === 'emoji')
iconSrc = currentPage.page_icon.emoji
}
return iconSrc
}
useEffect(() => {
if (currentPage) {
setLoading(true)
getPreviewContent()
}
}, [currentPage])
return (
<div className={cn(s.filePreview)}>
<div className={cn(s.previewHeader)}>
<div className={cn(s.title)}>
<span>{t('datasetCreation.stepOne.pagePreview')}</span>
<div className='flex items-center justify-center w-6 h-6 cursor-pointer' onClick={hidePreview}>
<XMarkIcon className='h-4 w-4'></XMarkIcon>
</div>
</div>
<div className={cn(s.fileName)}>
<NotionIcon
className='shrink-0 mr-1'
type='page'
src={getIcon()}
/>
{currentPage?.page_name}
</div>
</div>
<div className={cn(s.previewContent)}>
{loading && <div className={cn(s.loading)}/>}
{!loading && (
<div className={cn(s.fileContent)}>{previewContent}</div>
)}
</div>
</div>
)
}
export default NotionPagePreview
......@@ -4,9 +4,11 @@ import { useTranslation } from 'react-i18next'
import cn from 'classnames'
import FilePreview from '../file-preview'
import FileUploader from '../file-uploader'
import NotionPagePreview from '../notion-page-preview'
import EmptyDatasetCreationModal from '../empty-dataset-creation-modal'
import s from './index.module.css'
import type { File } from '@/models/datasets'
import type { DataSourceNotionPage } from '@/models/common'
import { DataSourceType } from '@/models/datasets'
import Button from '@/app/components/base/button'
import { NotionPageSelector } from '@/app/components/base/notion-page-selector'
......@@ -24,6 +26,8 @@ type IStepOneProps = {
changeType: (type: DataSourceType) => void
}
type Page = DataSourceNotionPage & { workspace_id: string }
const StepOne = ({
datasetId,
dataSourceType,
......@@ -34,9 +38,11 @@ const StepOne = ({
file,
updateFile,
notionPages = [],
updateNotionPages,
}: IStepOneProps) => {
const [showModal, setShowModal] = useState(false)
const [showFilePreview, setShowFilePreview] = useState(true)
const [currentNotionPage, setCurrentNotionPage] = useState<Page | undefined>()
const { t } = useTranslation()
const hidePreview = () => setShowFilePreview(false)
......@@ -45,6 +51,14 @@ const StepOne = ({
const modalCloseHandle = () => setShowModal(false)
const updateCurrentPage = (page: Page) => {
setCurrentNotionPage(page)
}
const hideNotionPagePreview = () => {
setCurrentNotionPage(undefined)
}
return (
<div className='flex w-full h-full'>
<div className='grow overflow-y-auto relative'>
......@@ -53,14 +67,20 @@ const StepOne = ({
<div className={s.dataSourceTypeList}>
<div
className={cn(s.dataSourceItem, dataSourceType === DataSourceType.FILE && s.active)}
onClick={() => changeType(DataSourceType.FILE)}
onClick={() => {
changeType(DataSourceType.FILE)
hidePreview()
}}
>
<span className={cn(s.datasetIcon)} />
{t('datasetCreation.stepOne.dataSourceType.file')}
</div>
<div
className={cn(s.dataSourceItem, dataSourceType === DataSourceType.NOTION && s.active)}
onClick={() => changeType(DataSourceType.NOTION)}
onClick={() => {
changeType(DataSourceType.NOTION)
hidePreview()
}}
>
<span className={cn(s.datasetIcon, s.notion)} />
{t('datasetCreation.stepOne.dataSourceType.notion')}
......@@ -85,16 +105,16 @@ const StepOne = ({
{!hasConnection && (
<div className={s.notionConnectionTip}>
<span className={s.notionIcon}/>
<div className={s.title}>Notion is not connected</div>
<div className={s.tip}>To sync with Notion, connection to Notion must be established first.</div>
<Button className='h-8' type='primary' onClick={onSetting}>Go to connect</Button>
<div className={s.title}>{t('datasetCreation.stepOne.notionSyncTitle')}</div>
<div className={s.tip}>{t('datasetCreation.stepOne.notionSyncTip')}</div>
<Button className='h-8' type='primary' onClick={onSetting}>{t('datasetCreation.stepOne.connect')}</Button>
</div>
)}
{hasConnection && (
<>
{/* TODO */}
<div className='mb-8 w-[640px]'>
<NotionPageSelector />
<NotionPageSelector onSelect={updateNotionPages} onPreview={updateCurrentPage} />
</div>
<Button disabled={!notionPages.length} className={s.submitButton} type='primary' onClick={onStepChange}>{t('datasetCreation.stepOne.button')}</Button>
</>
......@@ -111,7 +131,7 @@ const StepOne = ({
<EmptyDatasetCreationModal show={showModal} onHide={modalCloseHandle} />
</div>
{file && showFilePreview && <FilePreview file={file} hidePreview={hidePreview} />}
{/* TODO notion page preview */}
{currentNotionPage && <NotionPagePreview currentPage={currentNotionPage} hidePreview={hideNotionPagePreview} />}
</div>
)
}
......
......@@ -13,6 +13,7 @@ const translation = {
},
stepOne: {
filePreview: 'File Preview',
pagePreview: 'Page Preview',
dataSourceType: {
file: 'Import from text file',
notion: 'Sync from Notion',
......@@ -32,6 +33,9 @@ const translation = {
change: 'Change',
failed: 'Upload failed',
},
notionSyncTitle: 'Notion is not connected',
notionSyncTip: 'To sync with Notion, connection to Notion must be established first.',
connect: 'Go to connect',
button: 'next',
emptyDatasetCreation: 'I want to create an empty dataset',
modal: {
......
......@@ -13,6 +13,7 @@ const translation = {
},
stepOne: {
filePreview: '文件预览',
pagePreview: '页面预览',
dataSourceType: {
file: '导入已有文本',
notion: '同步自 Notion 内容',
......@@ -32,6 +33,9 @@ const translation = {
change: '更改文件',
failed: '上传失败',
},
notionSyncTitle: 'Notion 未绑定',
notionSyncTip: '同步 Notion 内容前,须先绑定 Notion 空间',
connect: '去绑定',
button: '下一步',
emptyDatasetCreation: '创建一个空数据集',
modal: {
......
......@@ -125,3 +125,7 @@ export const fetchTestingRecords: Fetcher<HitTestingRecordsResponse, { datasetId
export const fetchFileIndexingEstimate: Fetcher<FileIndexingEstimateResponse, any> = (body: any) => {
return post('/datasets/indexing-estimate', { body }) as Promise<FileIndexingEstimateResponse>
}
export const fetchNotionPagePreview: Fetcher<{ content: string }, { workspaceID: string; pageID: string }> = ({ workspaceID, pageID }) => {
return get(`notion/workspaces/${workspaceID}/pages/${pageID}/preview`) as Promise<{ content: string }>
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment