Commit d3713c51 authored by StyleZhang's avatar StyleZhang

feat: setting add notion data source

parent c7cbe3ba
.icon {
background: center center no-repeat;
background-size: cover;
}
\ No newline at end of file
import cn from 'classnames'
type NotionIconProps = {
type?: 'workspace' | 'page'
src: string
name: string
className?: string
}
const NotionIcon = ({
type = 'workspace',
src,
name,
className,
}: NotionIconProps) => {
if (type === 'workspace') {
if (src) {
if (src.includes('https://')) {
return (
<img
alt='workspace icon'
src={src}
className={cn('block object-cover w-5 h-5', className)}
/>
)
}
return (
<div className={cn('flex items-center justify-center w-5 h-5', className)}>{src}</div>
)
}
return (
<div className={cn('flex items-center justify-center w-5 h-5 bg-gray-200 text-xs font-medium text-gray-500 rounded', className)}>{name[0].toLocaleUpperCase()}</div>
)
}
return (
<div></div>
)
}
export default NotionIcon
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 2.5C4.96243 2.5 2.5 4.96243 2.5 8C2.5 11.0376 4.96243 13.5 8 13.5C11.0376 13.5 13.5 11.0376 13.5 8C13.5 4.96243 11.0376 2.5 8 2.5ZM9.85355 6.14645C10.0488 6.34171 10.0488 6.65829 9.85355 6.85355L8.70711 8L9.85355 9.14645C10.0488 9.34171 10.0488 9.65829 9.85355 9.85355C9.65829 10.0488 9.34171 10.0488 9.14645 9.85355L8 8.70711L6.85355 9.85355C6.65829 10.0488 6.34171 10.0488 6.14645 9.85355C5.95118 9.65829 5.95118 9.34171 6.14645 9.14645L7.29289 8L6.14645 6.85355C5.95118 6.65829 5.95118 6.34171 6.14645 6.14645C6.34171 5.95118 6.65829 5.95118 6.85355 6.14645L8 7.29289L9.14645 6.14645C9.34171 5.95118 9.65829 5.95118 9.85355 6.14645Z" fill="#98A2B3"/>
</svg>
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 4.5L6 7.5L9 4.5" stroke="#344054" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.49939 19.1498H13.6897C15.3354 19.1498 16.1891 18.2807 16.1891 16.6273V9.6521C16.1891 8.58313 16.0507 8.09095 15.3816 7.41418L11.3441 3.30749C10.6981 2.65381 10.1675 2.5 9.20618 2.5H5.49939C3.85363 2.5 3 3.36902 3 5.02246V16.6273C3 18.2884 3.85363 19.1498 5.49939 19.1498ZM5.62243 17.6424C4.87646 17.6424 4.50732 17.2502 4.50732 16.5351V5.11475C4.50732 4.40722 4.87646 4.00732 5.62243 4.00732H8.89856V8.22168C8.89856 9.32142 9.44457 9.85205 10.5366 9.85205H14.6818V16.5351C14.6818 17.2502 14.3049 17.6424 13.5589 17.6424H5.62243ZM10.675 8.52929C10.3597 8.52929 10.229 8.39087 10.229 8.07556V4.21496L14.4741 8.52929H10.675Z" fill="#37352F" fill-opacity="0.45"/>
</svg>
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.49939 19.1498H13.6897C15.3354 19.1498 16.1891 18.2807 16.1891 16.6273V9.6521C16.1891 8.58313 16.0507 8.09095 15.3816 7.41418L11.3441 3.30749C10.6981 2.65381 10.1675 2.5 9.20618 2.5H5.49939C3.85363 2.5 3 3.36902 3 5.02246V16.6273C3 18.2884 3.85363 19.1498 5.49939 19.1498ZM5.62243 17.6424C4.87645 17.6424 4.50732 17.2502 4.50732 16.5351V5.11475C4.50732 4.40722 4.87645 4.00732 5.62243 4.00732H8.89856V8.22168C8.89856 9.32142 9.44457 9.85205 10.5366 9.85205H14.6818V16.5351C14.6818 17.2502 14.3049 17.6424 13.5589 17.6424H5.62243ZM10.675 8.52929C10.3597 8.52929 10.229 8.39087 10.229 8.07556V4.21496L14.4741 8.52929H10.675ZM12.3362 11.8746H6.70678C6.41454 11.8746 6.2069 12.09 6.2069 12.3591C6.2069 12.636 6.41454 12.8513 6.70678 12.8513H12.3362C12.613 12.8513 12.8207 12.636 12.8207 12.3591C12.8207 12.09 12.613 11.8746 12.3362 11.8746ZM12.3362 14.4587H6.70678C6.41454 14.4587 6.2069 14.674 6.2069 14.9509C6.2069 15.22 6.41454 15.4276 6.70678 15.4276H12.3362C12.613 15.4276 12.8207 15.22 12.8207 14.9509C12.8207 14.674 12.613 14.4587 12.3362 14.4587Z" fill="#37352F" fill-opacity="0.45"/>
</svg>
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Icon">
<path id="Icon_2" d="M12.25 12.25L10.2084 10.2083M11.6667 6.70833C11.6667 9.44675 9.44675 11.6667 6.70833 11.6667C3.96992 11.6667 1.75 9.44675 1.75 6.70833C1.75 3.96992 3.96992 1.75 6.70833 1.75C9.44675 1.75 11.6667 3.96992 11.6667 6.70833Z" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5943_4745)">
<path d="M6.99984 8.74984C7.96634 8.74984 8.74984 7.96634 8.74984 6.99984C8.74984 6.03334 7.96634 5.24984 6.99984 5.24984C6.03334 5.24984 5.24984 6.03334 5.24984 6.99984C5.24984 7.96634 6.03334 8.74984 6.99984 8.74984Z" stroke="#667085" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.9241 8.59075C10.8535 8.75069 10.8324 8.92812 10.8636 9.10015C10.8948 9.27218 10.9768 9.43092 11.0991 9.5559L11.1309 9.58772C11.2295 9.68622 11.3077 9.80319 11.3611 9.93195C11.4145 10.0607 11.442 10.1987 11.442 10.3381C11.442 10.4775 11.4145 10.6155 11.3611 10.7442C11.3077 10.873 11.2295 10.99 11.1309 11.0885C11.0324 11.1871 10.9154 11.2653 10.7867 11.3187C10.6579 11.3721 10.5199 11.3995 10.3805 11.3995C10.2411 11.3995 10.1031 11.3721 9.97437 11.3187C9.84561 11.2653 9.72864 11.1871 9.63014 11.0885L9.59832 11.0567C9.47334 10.9344 9.3146 10.8524 9.14257 10.8212C8.97055 10.79 8.79312 10.8111 8.63317 10.8817C8.47632 10.9489 8.34256 11.0605 8.24833 11.2028C8.15411 11.345 8.10355 11.5118 8.10287 11.6824V11.7726C8.10287 12.0539 7.99112 12.3236 7.79222 12.5225C7.59332 12.7214 7.32355 12.8332 7.04226 12.8332C6.76097 12.8332 6.4912 12.7214 6.2923 12.5225C6.0934 12.3236 5.98166 12.0539 5.98166 11.7726V11.7248C5.97755 11.5493 5.92073 11.3791 5.81859 11.2363C5.71645 11.0935 5.57371 10.9847 5.40893 10.9241C5.24898 10.8535 5.07155 10.8324 4.89953 10.8636C4.7275 10.8948 4.56876 10.9768 4.44378 11.0991L4.41196 11.1309C4.31346 11.2295 4.19648 11.3077 4.06773 11.3611C3.93897 11.4145 3.80096 11.442 3.66158 11.442C3.5222 11.442 3.38419 11.4145 3.25543 11.3611C3.12668 11.3077 3.0097 11.2295 2.9112 11.1309C2.81259 11.0324 2.73436 10.9154 2.68099 10.7867C2.62761 10.6579 2.60014 10.5199 2.60014 10.3805C2.60014 10.2411 2.62761 10.1031 2.68099 9.97437C2.73436 9.84561 2.81259 9.72864 2.9112 9.63014L2.94302 9.59832C3.06527 9.47334 3.14728 9.3146 3.17848 9.14257C3.20967 8.97055 3.18861 8.79312 3.11802 8.63317C3.0508 8.47632 2.93918 8.34256 2.7969 8.24833C2.65463 8.15411 2.48791 8.10355 2.31726 8.10287H2.22711C1.94582 8.10287 1.67605 7.99112 1.47715 7.79222C1.27825 7.59332 1.1665 7.32355 1.1665 7.04226C1.1665 6.76097 1.27825 6.4912 1.47715 6.2923C1.67605 6.0934 1.94582 5.98166 2.22711 5.98166H2.27484C2.45036 5.97755 2.6206 5.92073 2.7634 5.81859C2.90621 5.71645 3.01499 5.57371 3.07559 5.40893C3.14619 5.24898 3.16724 5.07155 3.13605 4.89953C3.10486 4.7275 3.02285 4.56876 2.90059 4.44378L2.86878 4.41196C2.77017 4.31346 2.69194 4.19648 2.63856 4.06773C2.58519 3.93897 2.55772 3.80096 2.55772 3.66158C2.55772 3.5222 2.58519 3.38419 2.63856 3.25543C2.69194 3.12668 2.77017 3.0097 2.86878 2.9112C2.96728 2.81259 3.08425 2.73436 3.21301 2.68099C3.34176 2.62761 3.47978 2.60014 3.61916 2.60014C3.75854 2.60014 3.89655 2.62761 4.0253 2.68099C4.15406 2.73436 4.27103 2.81259 4.36953 2.9112L4.40135 2.94302C4.52633 3.06527 4.68507 3.14728 4.8571 3.17848C5.02913 3.20967 5.20656 3.18861 5.3665 3.11802H5.40893C5.56578 3.0508 5.69954 2.93918 5.79377 2.7969C5.88799 2.65463 5.93855 2.48791 5.93923 2.31726V2.22711C5.93923 1.94582 6.05097 1.67605 6.24988 1.47715C6.44878 1.27825 6.71855 1.1665 6.99984 1.1665C7.28113 1.1665 7.5509 1.27825 7.7498 1.47715C7.9487 1.67605 8.06044 1.94582 8.06044 2.22711V2.27484C8.06112 2.44548 8.11169 2.6122 8.20591 2.75448C8.30013 2.89675 8.4339 3.00837 8.59075 3.07559C8.75069 3.14619 8.92812 3.16724 9.10015 3.13605C9.27218 3.10486 9.43092 3.02285 9.5559 2.90059L9.58772 2.86878C9.68622 2.77017 9.80319 2.69194 9.93195 2.63856C10.0607 2.58519 10.1987 2.55772 10.3381 2.55772C10.4775 2.55772 10.6155 2.58519 10.7442 2.63856C10.873 2.69194 10.99 2.77017 11.0885 2.86878C11.1871 2.96728 11.2653 3.08425 11.3187 3.21301C11.3721 3.34176 11.3995 3.47978 11.3995 3.61916C11.3995 3.75854 11.3721 3.89655 11.3187 4.0253C11.2653 4.15406 11.1871 4.27103 11.0885 4.36953L11.0567 4.40135C10.9344 4.52633 10.8524 4.68507 10.8212 4.8571C10.79 5.02913 10.8111 5.20656 10.8817 5.3665V5.40893C10.9489 5.56578 11.0605 5.69954 11.2028 5.79377C11.345 5.88799 11.5118 5.93855 11.6824 5.93923H11.7726C12.0539 5.93923 12.3236 6.05097 12.5225 6.24988C12.7214 6.44878 12.8332 6.71855 12.8332 6.99984C12.8332 7.28113 12.7214 7.5509 12.5225 7.7498C12.3236 7.9487 12.0539 8.06044 11.7726 8.06044H11.7248C11.5542 8.06112 11.3875 8.11169 11.2452 8.20591C11.1029 8.30013 10.9913 8.4339 10.9241 8.59075Z" stroke="#667085" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_5943_4745">
<rect width="14" height="14" fill="white"/>
</clipPath>
</defs>
</svg>
.setting-icon {
background: url(./assets/setting.svg) center center no-repeat;
background-size: 14px 14px;
}
\ No newline at end of file
import { useState } from 'react'
import cn from 'classnames'
import s from './base.module.css'
import WorkspaceSelector from './workspace-selector'
import SearchInput from './search-input'
const NotionPageSelector = () => {
const [searchValue, setSearchValue] = useState('')
const handleSearchValueChange = (value: string) => {
setSearchValue(value)
}
return (
<div className='bg-gray-25 border border-gray-200 rounded-xl'>
<div className='flex items-center pl-[10px] pr-2 h-11 bg-white'>
<WorkspaceSelector />
<div className='mx-1 w-[1px] h-3 bg-gray-200' />
<div className={cn(s['setting-icon'], 'w-6 h-6 cursor-pointer')} />
<div className='grow' />
<SearchInput
value={searchValue}
onChange={handleSearchValueChange}
/>
</div>
<div className='p-2'>
<div className='flex items-center px-2 h-7 rounded-md'>
<div className='mr-3 w-4 h-4'></div>
<div className='mr-2 w-5 h-5'></div>
<div className='text-sm font-medium text-gray-700'>sdfsfsfsd</div>
</div>
</div>
</div>
)
}
export default NotionPageSelector
export { default as NotionPageSelectorModal } from './notion-page-selector-modal'
export { default as NotionPageSelector } from './base'
import NotionPageSelector from '../base'
const NotionPageSelectorModal = () => {
return (
<NotionPageSelector />
)
}
export default NotionPageSelectorModal
.search-icon {
background: url(../assets/search.svg) center center;
background-size: 14px 14px;
}
.clear-icon {
background: url(../assets/clear.svg) center center;
background-size: contain;
}
.input-wrapper {
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
}
\ No newline at end of file
import { useCallback } from 'react'
import type { ChangeEvent } from 'react'
import { useTranslation } from 'react-i18next'
import cn from 'classnames'
import s from './index.module.css'
type SearchInputProps = {
value: string
onChange: (v: string) => void
}
const SearchInput = ({
value,
onChange,
}: SearchInputProps) => {
const { t } = useTranslation()
const handleClear = useCallback(() => {
onChange('')
}, [])
return (
<div className={cn(s['input-wrapper'], 'flex items-center px-2 w-50 h-7 border border-gray-300 rounded-md', `${value ? 'bg-white' : 'bg-gray-100'}`)}>
<div className={cn(s['search-icon'], 'mr-[6px] w-4 h-4')} />
<input
className='grow text-[13px] bg-inherit border-0 outline-0 appearance-none'
value={value}
onChange={(e: ChangeEvent<HTMLInputElement>) => onChange(e.target.value)}
placeholder={t('common.dataSource.notion.selector.searchPages') || ''}
/>
{
value && (
<div
className={cn(s['clear-icon'], 'ml-1 w-4 h-4 cursor-pointer')}
onClick={handleClear}
/>
)
}
</div>
)
}
export default SearchInput
.down-arrow {
background: url(../assets/down-arrow.svg) center center no-repeat;
background-size: cover;
}
.popup {
box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03);
}
\ No newline at end of file
'use client'
import { useTranslation } from 'react-i18next'
import { Fragment } from 'react'
import { Menu, Transition } from '@headlessui/react'
import cn from 'classnames'
import s from './index.module.css'
export default function WorkspaceSelector() {
const { t } = useTranslation()
return (
<Menu as="div" className="relative inline-block text-left">
{
({ open }) => (
<>
<Menu.Button className={`flex items-center justify-center h-7 rounded-md hover:bg-gray-50 ${open && 'bg-gray-50'} cursor-pointer`}>
<div className='ml-1 mr-2 w-5 h-5 rounded'></div>
<div className='mr-1 w-[90px] truncate'>Stylezhang's workspace</div>
<div className='mr-1 w-5 h-[18px] bg-primary-50 rounded-lg text-xs font-medium text-primary-600'>4</div>
<div className={cn(s['down-arrow'], 'mr-2 w-3 h-3')} />
</Menu.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items
className={cn(
s.popup,
`absolute left-0 top-8 w-80
origin-top-right rounded-lg bg-white
border-[0.5px] border-gray-200`,
)}
>
<div className="p-1">
<Menu.Item>
<div className='flex items-center px-3 h-9 hover:bg-gray-50 cursor-pointer'>
<div className='mr-2 w-5 h-5 rounded'></div>
<div className='grow mr-2 text-sm text-gray-700'>LangGenius</div>
<div className='text-xs font-medium text-primary-600'>
{4} {t('common.dataSource.notion.selector.pageSelected')}
</div>
</div>
</Menu.Item>
<Menu.Item>
<div className='flex items-center px-3 h-9 hover:bg-gray-50 cursor-pointer'>
<div className='mr-2 w-5 h-5 rounded'></div>
<div className='grow mr-2 text-sm text-gray-700'>LangGenius</div>
<div className='text-xs font-medium text-primary-600'>
{4} {t('common.dataSource.notion.selector.pageSelected')}
</div>
</div>
</Menu.Item>
</div>
</Menu.Items>
</Transition>
</>
)
}
</Menu>
)
}
...@@ -9,9 +9,9 @@ import { Menu, Transition } from '@headlessui/react' ...@@ -9,9 +9,9 @@ import { Menu, Transition } from '@headlessui/react'
import Indicator from '../indicator' import Indicator from '../indicator'
import AccountSetting from '../account-setting' import AccountSetting from '../account-setting'
import AccountAbout from '../account-about' import AccountAbout from '../account-about'
import WorkplaceSelector from './workplace-selector'
import type { LangGeniusVersionResponse, UserProfileResponse } from '@/models/common' import type { LangGeniusVersionResponse, UserProfileResponse } from '@/models/common'
import I18n from '@/context/i18n' import I18n from '@/context/i18n'
import WorkplaceSelector from './workplace-selector'
import Avatar from '@/app/components/base/avatar' import Avatar from '@/app/components/base/avatar'
type IAppSelectorProps = { type IAppSelectorProps = {
......
import { useTranslation } from 'react-i18next'
import Link from 'next/link'
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 { apiPrefix } from '@/config'
import type { DataSourceNotion as TDataSourceNotion } from '@/models/common'
type DataSourceNotionProps = {
workspaces: TDataSourceNotion[]
}
const DataSourceNotion = ({
workspaces,
}: DataSourceNotionProps) => {
const { t } = useTranslation()
return (
<div className='mb-2 border-[0.5px] border-gray-200 bg-gray-50 rounded-xl'>
<div className='flex items-center px-3 py-[9px]'>
<div className={cn(s['notion-icon'], 'w-8 h-8 mr-3 border border-gray-100 rounded-lg')} />
<div className='grow'>
<div className='leading-5 text-sm font-medium text-gray-800'>
{t('common.dataSource.notion.title')}
</div>
{
!workspaces.length && (
<div className='leading-5 text-xs text-gray-500'>
{t('common.dataSource.notion.description')}
</div>
)
}
</div>
{
!workspaces.length
? (
<Link
className='flex items-center ml-3 px-3 h-7 bg-white border border-gray-200 rounded-md text-xs font-medium text-gray-700 cursor-pointer'
href={`${apiPrefix}/oauth/data-source/notion`}>
{t('common.dataSource.connect')}
</Link>
)
: (
<Link
href={`${apiPrefix}/oauth/data-source/notion`}
className='flex items-center px-3 h-7 bg-white border-[0.5px] border-gray-200 text-xs font-medium text-primary-600 rounded-md cursor-pointer'>
<PlusIcon className='w-[14px] h-[14px] mr-[5px]' />
{t('common.dataSource.notion.addWorkspace')}
</Link>
)
}
</div>
<div className='flex items-center px-3 h-[18px]'>
<div className='text-xs font-medium text-gray-500'>
{t('common.dataSource.notion.connectedWorkspace')}
</div>
<div className='grow ml-3 border-t border-t-gray-100' />
</div>
<div className='px-3 pt-2 pb-3'>
{
workspaces.map(workspace => (
<div className={cn(s['workspace-item'], 'flex items-center mb-1 py-1 pr-1 bg-white rounded-lg')} key={workspace.id}>
<NotionIcon
className='ml-3 mr-[6px]'
src={workspace.source_info.workspace_icon || ''}
name={workspace.source_info.workspace_name}
/>
<div className='grow py-[7px] leading-[18px] text-[13px] font-medium text-gray-700'>{workspace.source_info.workspace_name}</div>
{
workspace.is_bound
? <Indicator className='mr-[6px]' />
: <Indicator className='mr-[6px]' color='yellow' />
}
<div className='mr-3 text-xs font-medium'>
{
workspace.is_bound
? t('common.dataSource.notion.connected')
: t('common.dataSource.notion.disconnected')
}
</div>
<div className='mr-2 w-[1px] h-3 bg-gray-100' />
<Operate workspace={workspace} />
</div>
))
}
</div>
</div>
)
}
export default DataSourceNotion
.file-icon {
background: url(../../../../assets/file.svg) center center no-repeat;
background-size: contain;
}
.sync-icon {
background: url(../../../../assets/sync.svg) center center no-repeat;
background-size: contain;
}
.trash-icon {
background: url(../../../../assets/trash.svg) center center no-repeat;
background-size: contain;
}
\ No newline at end of file
'use client'
import { useTranslation } from 'react-i18next'
import { Fragment } from 'react'
import Link from 'next/link'
import { EllipsisHorizontalIcon } from '@heroicons/react/24/solid'
import { Menu, Transition } from '@headlessui/react'
import cn from 'classnames'
import s from './index.module.css'
import { apiPrefix } from '@/config'
import type { DataSourceNotion } from '@/models/common'
type OperateProps = {
workspace: DataSourceNotion
}
export default function Operate({
workspace,
}: OperateProps) {
const itemClassName = `
flex px-3 py-2 hover:bg-gray-50 text-sm text-gray-700
cursor-pointer
`
const itemIconClassName = `
mr-2 mt-[2px] w-4 h-4
`
const { t } = useTranslation()
return (
<Menu as="div" className="relative inline-block text-left">
{
({ open }) => (
<>
<Menu.Button className={`flex items-center justify-center w-8 h-8 rounded-lg hover:bg-gray-100 ${open && 'bg-gray-100'}`}>
<EllipsisHorizontalIcon className='w-4 h-4' />
</Menu.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items
className="
absolute right-0 top-9 w-60 max-w-80
divide-y divide-gray-100 origin-top-right rounded-lg bg-white
shadow-[0_10px_15px_-3px_rgba(0,0,0,0.1),0_4px_6px_rgba(0,0,0,0.05)]
"
>
<div className="px-1 py-1">
<Menu.Item>
<Link
className={itemClassName}
href={`${apiPrefix}/oauth/data-source/notion`}>
<div className={cn(s['file-icon'], itemIconClassName)}></div>
<div>
<div className='leading-5'>{t('common.dataSource.notion.changeAuthorizedPages')}</div>
<div className='leading-5 text-xs text-gray-500'>
{workspace.source_info.total} {t('common.dataSource.notion.pagesAuthorized')}
</div>
</div>
</Link>
</Menu.Item>
<Menu.Item>
<div className={itemClassName}>
<div className={cn(s['sync-icon'], itemIconClassName)} />
<div className='leading-5'>{t('common.dataSource.notion.sync')}</div>
</div>
</Menu.Item>
</div>
<Menu.Item>
<div className='p-1'>
<div className={itemClassName}>
<div className={cn(s['trash-icon'], itemIconClassName)} />
<div className='leading-5'>{t('common.dataSource.notion.remove')}</div>
</div>
</div>
</Menu.Item>
</Menu.Items>
</Transition>
</>
)
}
</Menu>
)
}
.notion-icon {
background: #ffffff url(../../../assets/notion.svg) center center no-repeat;
background-size: 20px 20px;
}
.workspace-item {
box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05);
}
.workspace-item:last-of-type {
margin-bottom: 0;
}
\ No newline at end of file
import useSWR from 'swr'
import { useTranslation } from 'react-i18next'
import DataSourceNotion from './data-source-notion'
import { fetchDataSource } from '@/service/common'
export default function DataSourcePage() {
const { t } = useTranslation()
const { data } = useSWR({ url: 'data-source/integrates' }, fetchDataSource)
const notionWorkspaces = data?.data.filter(item => item.provider === 'notion') || []
return (
<div className='mb-8'>
<div className='mb-2 text-sm font-medium text-gray-900'>{t('common.dataSource.add')}</div>
<DataSourceNotion workspaces={notionWorkspaces} />
</div>
)
}
...@@ -2,4 +2,14 @@ ...@@ -2,4 +2,14 @@
max-width: 720px !important; max-width: 720px !important;
padding: 0 !important; padding: 0 !important;
overflow-y: auto; overflow-y: auto;
}
.data-source-icon {
background: url(../assets/data-source.svg) center center no-repeat;
background-size: cover;
}
.data-source-solid-icon {
background: url(../assets/data-source-blue.svg) center center no-repeat;
background-size: cover;
} }
\ No newline at end of file
'use client' 'use client'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useState } from 'react' import { useState } from 'react'
import { AtSymbolIcon, GlobeAltIcon, UserIcon, XMarkIcon, CubeTransparentIcon, UsersIcon } from '@heroicons/react/24/outline' import { AtSymbolIcon, CubeTransparentIcon, GlobeAltIcon, UserIcon, UsersIcon, XMarkIcon } from '@heroicons/react/24/outline'
import { GlobeAltIcon as GlobalAltIconSolid, UserIcon as UserIconSolid, UsersIcon as UsersIconSolid } from '@heroicons/react/24/solid' import { GlobeAltIcon as GlobalAltIconSolid, UserIcon as UserIconSolid, UsersIcon as UsersIconSolid } from '@heroicons/react/24/solid'
import cn from 'classnames'
import AccountPage from './account-page' import AccountPage from './account-page'
import MembersPage from './members-page' import MembersPage from './members-page'
import IntegrationsPage from './Integrations-page' import IntegrationsPage from './Integrations-page'
import LanguagePage from './language-page' import LanguagePage from './language-page'
import ProviderPage from './provider-page' import ProviderPage from './provider-page'
import DataSourcePage from './data-source-page'
import s from './index.module.css' import s from './index.module.css'
import Modal from '@/app/components/base/modal' import Modal from '@/app/components/base/modal'
const iconClassName = ` const iconClassName = `
w-[18px] h-[18px] ml-3 mr-2 w-4 h-4 ml-3 mr-2
` `
type IconProps = {
className?: string
}
const DataSourceIcon = ({ className }: IconProps) => (
<div className={cn(s['data-source-icon'], className)} />
)
const DataSourceSolidIcon = ({ className }: IconProps) => (
<div className={cn(s['data-source-solid-icon'], className)} />
)
type IAccountSettingProps = { type IAccountSettingProps = {
onCancel: () => void onCancel: () => void
activeTab?: string activeTab?: string
...@@ -48,7 +60,7 @@ export default function AccountSetting({ ...@@ -48,7 +60,7 @@ export default function AccountSetting({
icon: <GlobeAltIcon className={iconClassName} />, icon: <GlobeAltIcon className={iconClassName} />,
activeIcon: <GlobalAltIconSolid className={iconClassName} />, activeIcon: <GlobalAltIconSolid className={iconClassName} />,
}, },
] ],
}, },
{ {
key: 'workspace-group', key: 'workspace-group',
...@@ -66,8 +78,14 @@ export default function AccountSetting({ ...@@ -66,8 +78,14 @@ export default function AccountSetting({
icon: <CubeTransparentIcon className={iconClassName} />, icon: <CubeTransparentIcon className={iconClassName} />,
activeIcon: <CubeTransparentIcon className={iconClassName} />, activeIcon: <CubeTransparentIcon className={iconClassName} />,
}, },
] {
} key: 'data-source',
name: t('common.settings.dataSource'),
icon: <DataSourceIcon className={iconClassName} />,
activeIcon: <DataSourceSolidIcon className={iconClassName} />,
},
],
},
] ]
return ( return (
...@@ -126,6 +144,9 @@ export default function AccountSetting({ ...@@ -126,6 +144,9 @@ export default function AccountSetting({
{ {
activeMenu === 'provider' && <ProviderPage /> activeMenu === 'provider' && <ProviderPage />
} }
{
activeMenu === 'data-source' && <DataSourcePage />
}
</div> </div>
</div> </div>
</Modal> </Modal>
......
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.66659 9.98845C10.1231 9.93732 11.4455 9.71981 12.461 9.38077C13.0418 9.18687 13.5593 8.94109 13.9452 8.6353C14.3245 8.33478 14.6666 7.89967 14.6666 7.33341V3.33341C14.6666 2.76545 14.3207 2.33047 13.9411 2.03132C13.5539 1.72624 13.0351 1.48065 12.4534 1.28675C11.283 0.896612 9.70849 0.666748 7.99992 0.666748C6.29135 0.666748 4.71686 0.896612 3.54646 1.28675C2.96474 1.48065 2.44589 1.72624 2.05878 2.03132C1.67918 2.33047 1.33325 2.76545 1.33325 3.33341V7.33341C1.33325 7.89967 1.67538 8.33478 2.05463 8.6353C2.44053 8.94109 2.958 9.18687 3.53881 9.38077C4.55435 9.71981 5.87675 9.93732 7.33325 9.98845V11.4472C6.76498 11.6481 6.31458 12.0985 6.11372 12.6667H1.99992C1.63173 12.6667 1.33325 12.9652 1.33325 13.3334C1.33325 13.7016 1.63173 14.0001 1.99992 14.0001H6.11372C6.38828 14.7769 7.12911 15.3334 7.99992 15.3334C8.87073 15.3334 9.61156 14.7769 9.88612 14.0001H13.9999C14.3681 14.0001 14.6666 13.7016 14.6666 13.3334C14.6666 12.9652 14.3681 12.6667 13.9999 12.6667H9.88612C9.68526 12.0985 9.23486 11.6481 8.66659 11.4472V9.98845ZM2.66659 3.33337C2.66659 3.33337 2.66657 3.32994 2.66862 3.32322C2.67089 3.31579 2.67647 3.30111 2.68997 3.27913C2.71827 3.23303 2.77597 3.16373 2.88408 3.07853C3.1042 2.90506 3.46403 2.71968 3.9681 2.55166C4.96927 2.21793 6.39478 2.00008 7.99992 2.00008C9.60506 2.00008 11.0306 2.21793 12.0317 2.55166C12.5358 2.71968 12.8956 2.90506 13.1158 3.07853C13.2239 3.16373 13.2816 3.23303 13.3099 3.27913C13.3234 3.30111 13.329 3.31579 13.3312 3.32322C13.3333 3.32994 13.3333 3.33337 13.3333 3.33337C13.3333 3.33337 13.3332 3.33722 13.3312 3.34361C13.329 3.35104 13.3234 3.36572 13.3099 3.3877C13.2816 3.4338 13.2239 3.5031 13.1158 3.5883C12.8956 3.76177 12.5358 3.94715 12.0317 4.11517C11.0306 4.4489 9.60506 4.66675 7.99992 4.66675C6.39478 4.66675 4.96927 4.4489 3.9681 4.11517C3.46403 3.94715 3.1042 3.76177 2.88408 3.5883C2.77597 3.5031 2.71827 3.4338 2.68997 3.3877C2.67647 3.36572 2.67089 3.35104 2.66862 3.34361C2.6667 3.33731 2.66659 3.33337 2.66659 3.33337Z" fill="#155EEF"/>
</svg>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.33333 13.3333C9.33333 14.0696 8.73638 14.6666 8 14.6666C7.26362 14.6666 6.66667 14.0696 6.66667 13.3333M9.33333 13.3333C9.33333 12.5969 8.73638 11.9999 8 11.9999M9.33333 13.3333H14M6.66667 13.3333C6.66667 12.5969 7.26362 11.9999 8 11.9999M6.66667 13.3333H2M8 11.9999V9.33325M14 3.33325C14 4.43782 11.3137 5.33325 8 5.33325C4.68629 5.33325 2 4.43782 2 3.33325M14 3.33325C14 2.22868 11.3137 1.33325 8 1.33325C4.68629 1.33325 2 2.22868 2 3.33325M14 3.33325V7.33325C14 8.43992 11.3333 9.33325 8 9.33325M2 3.33325V7.33325C2 8.43992 4.66667 9.33325 8 9.33325" stroke="#344054" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.3333 6.99992V4.53325C13.3333 3.41315 13.3333 2.85309 13.1153 2.42527C12.9236 2.04895 12.6176 1.74299 12.2413 1.55124C11.8135 1.33325 11.2534 1.33325 10.1333 1.33325H5.86666C4.74655 1.33325 4.1865 1.33325 3.75868 1.55124C3.38235 1.74299 3.07639 2.04895 2.88464 2.42527C2.66666 2.85309 2.66666 3.41315 2.66666 4.53325V11.4666C2.66666 12.5867 2.66666 13.1467 2.88464 13.5746C3.07639 13.9509 3.38235 14.2569 3.75868 14.4486C4.1865 14.6666 4.74655 14.6666 5.86666 14.6666H7.99999M9.33332 7.33325H5.33332M6.66666 9.99992H5.33332M10.6667 4.66659H5.33332M12 13.9999V9.99992M9.99999 11.9999H14" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_5364_42310)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.5725 18.2611L1.4229 15.5832C0.905706 14.9389 0.625 14.1466 0.625 13.3312V3.63437C0.625 2.4129 1.60224 1.39936 2.86295 1.31328L12.8326 0.632614C13.5569 0.583164 14.2768 0.775682 14.8717 1.17794L18.3745 3.5462C19.0015 3.97012 19.375 4.66312 19.375 5.40266V16.427C19.375 17.6223 18.4141 18.6121 17.1798 18.688L6.11458 19.3692C5.12958 19.4298 4.17749 19.0148 3.5725 18.2611Z" fill="white"/>
<path d="M7.03006 8.48663V8.35968C7.03006 8.03787 7.28779 7.77098 7.61997 7.7488L10.0396 7.58726L13.3857 12.5146V8.19003L12.5244 8.07522V8.01492C12.5244 7.68933 12.788 7.42068 13.1244 7.40344L15.326 7.29066V7.60749C15.326 7.75622 15.2154 7.88343 15.0638 7.90907L14.534 7.99868V15.0022L13.8691 15.2309C13.3136 15.4219 12.6952 15.2174 12.3772 14.7376L9.12879 9.83568V14.5143L10.1287 14.7056L10.1147 14.7984C10.0711 15.0889 9.82028 15.3086 9.51687 15.3221L7.03006 15.4328C6.99718 15.1204 7.23132 14.8409 7.55431 14.807L7.88143 14.7726V8.53447L7.03006 8.48663Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.9218 1.85418L2.95217 2.53485C2.35499 2.57562 1.89209 3.05572 1.89209 3.63431V13.3311C1.89209 13.8748 2.07923 14.4029 2.42402 14.8325L4.57362 17.5104C4.92117 17.9433 5.46812 18.1817 6.03397 18.1469L17.0991 17.4658C17.6663 17.4309 18.1078 16.9761 18.1078 16.4269V5.4026C18.1078 5.06281 17.9362 4.74441 17.6481 4.54963L14.1453 2.18137C13.7883 1.94002 13.3564 1.82451 12.9218 1.85418ZM3.44654 3.78556C3.30788 3.6829 3.37387 3.46903 3.54806 3.45654L12.9889 2.77938C13.2897 2.75781 13.5886 2.84064 13.8318 3.01299L15.7261 4.35502C15.798 4.40597 15.7642 4.51596 15.6752 4.5208L5.67742 5.06454C5.37485 5.081 5.0762 4.99211 4.83563 4.814L3.44654 3.78556ZM5.20848 6.76913C5.20848 6.44433 5.47088 6.17604 5.80642 6.15777L16.3769 5.5821C16.7039 5.56429 16.9792 5.81577 16.9792 6.13232V15.6782C16.9792 16.0024 16.7177 16.2705 16.3829 16.2895L5.8793 16.8871C5.51537 16.9079 5.20848 16.6282 5.20848 16.2759V6.76913Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_5364_42310">
<rect width="20" height="20" fill="white"/>
</clipPath>
</defs>
</svg>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.69773 13.1783C7.29715 13.8879 9.20212 13.8494 10.8334 12.9075C13.5438 11.3427 14.4724 7.87704 12.9076 5.16672L12.7409 4.87804M3.09233 10.8335C1.52752 8.12314 2.45615 4.65746 5.16647 3.09265C6.7978 2.15081 8.70277 2.11227 10.3022 2.82185M1.66226 10.8892L3.48363 11.3773L3.97166 9.5559M12.0284 6.44393L12.5164 4.62256L14.3378 5.1106" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6 2H10M2 4H14M12.6667 4L12.1991 11.0129C12.129 12.065 12.0939 12.5911 11.8667 12.99C11.6666 13.3412 11.3648 13.6235 11.0011 13.7998C10.588 14 10.0607 14 9.00623 14H6.99377C5.93927 14 5.41202 14 4.99889 13.7998C4.63517 13.6235 4.33339 13.3412 4.13332 12.99C3.90607 12.5911 3.871 12.065 3.80086 11.0129L3.33333 4M6.66667 7V10.3333M9.33333 7V10.3333" stroke="#667085" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
...@@ -87,6 +87,7 @@ const translation = { ...@@ -87,6 +87,7 @@ const translation = {
integrations: 'Integrations', integrations: 'Integrations',
language: 'Language', language: 'Language',
provider: 'Model Provider', provider: 'Model Provider',
dataSource: 'Data Source',
}, },
account: { account: {
avatar: 'Avatar', avatar: 'Avatar',
...@@ -172,6 +173,22 @@ const translation = { ...@@ -172,6 +173,22 @@ const translation = {
back: ' technology.', back: ' technology.',
}, },
}, },
dataSource: {
add: 'Add a data source',
connect: 'Connect',
notion: {
title: 'Notion',
description: 'Using Notion as a data source for the dataset.',
connectedWorkspace: 'Connected workspace',
addWorkspace: 'Add workspace',
connected: 'Connected',
disconnected: 'Disconnected',
changeAuthorizedPages: 'Change authorized pages',
pagesAuthorized: 'Pages authorized',
sync: 'Sync',
remove: 'Remove',
},
},
about: { about: {
changeLog: 'Changlog', changeLog: 'Changlog',
updateNow: 'Update now', updateNow: 'Update now',
......
...@@ -87,6 +87,7 @@ const translation = { ...@@ -87,6 +87,7 @@ const translation = {
integrations: '集成', integrations: '集成',
language: '语言', language: '语言',
provider: '模型供应商', provider: '模型供应商',
dataSource: '数据来源',
}, },
account: { account: {
avatar: '头像', avatar: '头像',
...@@ -173,6 +174,28 @@ const translation = { ...@@ -173,6 +174,28 @@ const translation = {
back: ' 技术进行加密和存储。', back: ' 技术进行加密和存储。',
}, },
}, },
dataSource: {
add: '添加数据源',
connect: '绑定',
notion: {
title: 'Notion',
description: '使用 Notion 作为数据集的数据源。',
connectedWorkspace: '已绑定工作空间',
addWorkspace: '添加工作空间',
connected: '已绑定',
disconnected: '未绑定',
changeAuthorizedPages: '更改授权页面',
pagesAuthorized: '已授权页面',
sync: '同步',
remove: '删除',
selector: {
pageSelected: '已选页面',
searchPages: '搜索页面...',
noSerachResult: '无搜索结果',
addPages: '添加页面',
},
},
},
about: { about: {
changeLog: '更新日志', changeLog: '更新日志',
updateNow: '现在更新', updateNow: '现在更新',
......
...@@ -99,3 +99,20 @@ export type IWorkspace = { ...@@ -99,3 +99,20 @@ export type IWorkspace = {
created_at: number created_at: number
current: boolean current: boolean
} }
export type DataSourceNotion = {
id: string
provider: string
is_bound: boolean
source_info: {
workspace_name: string
workspace_id: string
workspace_icon: string | null
total: number
pages: {
page_icon: string | null
page_id: string
page_name: string
}[]
}
}
import type { Fetcher } from 'swr' import type { Fetcher } from 'swr'
import { get, post, del, put } from './base' import { del, get, post, put } from './base'
import type { import type {
CommonResponse, LangGeniusVersionResponse, OauthResponse, AccountIntegrate, CommonResponse, DataSourceNotion,
TenantInfoResponse, UserProfileOriginResponse, Member, IWorkspace, LangGeniusVersionResponse, Member,
AccountIntegrate, Provider, ProviderAzureToken, IWorkspace OauthResponse, Provider, ProviderAzureToken, TenantInfoResponse,
UserProfileOriginResponse,
} from '@/models/common' } from '@/models/common'
import type { import type {
UpdateOpenAIKeyResponse,
ValidateOpenAIKeyResponse, ValidateOpenAIKeyResponse,
UpdateOpenAIKeyResponse
} from '@/models/app' } from '@/models/app'
export const login: Fetcher<CommonResponse, { url: string; body: Record<string, any> }> = ({ url, body }) => { export const login: Fetcher<CommonResponse, { url: string; body: Record<string, any> }> = ({ url, body }) => {
...@@ -73,7 +74,7 @@ export const updateMemberRole: Fetcher<CommonResponse, { url: string; body: Reco ...@@ -73,7 +74,7 @@ export const updateMemberRole: Fetcher<CommonResponse, { url: string; body: Reco
return put(url, { body }) as Promise<CommonResponse> return put(url, { body }) as Promise<CommonResponse>
} }
export const deleteMemberOrCancelInvitation: Fetcher<CommonResponse, { url: string; }> = ({ url }) => { export const deleteMemberOrCancelInvitation: Fetcher<CommonResponse, { url: string }> = ({ url }) => {
return del(url) as Promise<CommonResponse> return del(url) as Promise<CommonResponse>
} }
...@@ -89,3 +90,6 @@ export const switchWorkspace: Fetcher<CommonResponse & { new_tenant: IWorkspace ...@@ -89,3 +90,6 @@ export const switchWorkspace: Fetcher<CommonResponse & { new_tenant: IWorkspace
return post(url, { body }) as Promise<CommonResponse & { new_tenant: IWorkspace }> return post(url, { body }) as Promise<CommonResponse & { new_tenant: IWorkspace }>
} }
export const fetchDataSource: Fetcher<{ data: DataSourceNotion[] }, { url: string; params: Record<string, any> }> = ({ url, params }) => {
return get(url, { params }) as Promise<{ data: DataSourceNotion[] }>
}
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