feat: fe mobile responsive next (#1609)

This commit is contained in:
Yuhao
2023-11-27 11:47:48 +08:00
committed by GitHub
parent 3cc697832a
commit a9c1c7d239
89 changed files with 768 additions and 485 deletions

View File

@@ -15,10 +15,10 @@
color: #667085;
}
.uploader {
@apply relative box-border flex justify-center items-center mb-2;
@apply relative box-border flex justify-center items-center mb-2 p-3;
flex-direction: column;
max-width: 640px;
height: 80px;
min-height: 80px;
background: #F9FAFB;
border: 1px dashed #EAECF0;
border-radius: 12px;

View File

@@ -234,10 +234,12 @@ const FileUploader = ({
/>
<div className={cn(s.title, titleClassName)}>{t('datasetCreation.stepOne.uploader.title')}</div>
<div ref={dropRef} className={cn(s.uploader, dragging && s.dragging)}>
<div className='flex justify-center items-center h-6 mb-2'>
<div className='flex justify-center items-center min-h-6 mb-2'>
<span className={s.uploadIcon}/>
<span>{t('datasetCreation.stepOne.uploader.button')}</span>
<label className={s.browse} onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.browse')}</label>
<span>
{t('datasetCreation.stepOne.uploader.button')}
<label className={s.browse} onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.browse')}</label>
</span>
</div>
<div className={s.tip}>{t('datasetCreation.stepOne.uploader.tip', { size: fileUploadConfig.file_size_limit })}</div>
{dragging && <div ref={dragRef} className={s.draggingCover}/>}

View File

@@ -103,7 +103,7 @@ const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => {
return (
<div className='flex' style={{ height: 'calc(100vh - 56px)' }}>
<div className="flex flex-col w-56 overflow-y-auto bg-white border-r border-gray-200 shrink-0">
<div className="flex flex-col w-11 sm:w-56 overflow-y-auto bg-white border-r border-gray-200 shrink-0">
<StepsNavBar step={step} datasetId={datasetId} />
</div>
<div className="grow bg-white">

View File

@@ -15,9 +15,6 @@
background-color: #fff;
}
.dataSourceTypeList {
@apply flex items-center mb-8;
}
.dataSourceItem {
@apply box-border relative shrink-0 flex items-center mr-3 p-3 h-14 bg-white rounded-xl cursor-pointer;
border: 0.5px solid #EAECF0;

View File

@@ -106,7 +106,7 @@ const StepOne = ({
<div className={s.form}>
{
shouldShowDataSourceTypeList && (
<div className={s.dataSourceTypeList}>
<div className='flex items-center mb-8 flex-wrap gap-y-4'>
<div
className={cn(
s.dataSourceItem,

View File

@@ -5,6 +5,7 @@ import cn from 'classnames'
import EmbeddingProcess from '../embedding-process'
import s from './index.module.css'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import type { FullDocumentDetail, createDocumentResponse } from '@/models/datasets'
type StepThreeProps = {
@@ -17,9 +18,12 @@ type StepThreeProps = {
const StepThree = ({ datasetId, datasetName, indexingType, creationCache }: StepThreeProps) => {
const { t } = useTranslation()
const media = useBreakpoints()
const isMobile = media === MediaType.mobile
return (
<div className='flex w-full h-full'>
<div className={'h-full w-full overflow-y-scroll px-16'}>
<div className={'h-full w-full overflow-y-scroll px-6 sm:px-16'}>
<div className='max-w-[636px]'>
{!datasetId && (
<>
@@ -46,13 +50,13 @@ const StepThree = ({ datasetId, datasetName, indexingType, creationCache }: Step
/>
</div>
</div>
<div className={cn(s.sideTip)}>
{!isMobile && <div className={cn(s.sideTip)}>
<div className={s.tipCard}>
<span className={s.icon}/>
<div className={s.title}>{t('datasetCreation.stepThree.sideTipTitle')}</div>
<div className={s.content}>{t('datasetCreation.stepThree.sideTipContent')}</div>
</div>
</div>
</div>}
</div>
)
}

View File

@@ -1,5 +1,5 @@
.pageHeader {
@apply px-16;
@apply px-16 flex justify-between items-center;
position: sticky;
top: 0;
left: 0;
@@ -251,7 +251,7 @@
}
.ruleItem {
@apply flex items-center h-7;
@apply flex items-center;
}
.formFooter {
@@ -382,7 +382,7 @@
.previewWrap {
flex-shrink: 0;
width: 524px;
max-width: 524px;
}
.previewHeader {

View File

@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import { useBoolean } from 'ahooks'
import { XMarkIcon } from '@heroicons/react/20/solid'
import { RocketLaunchIcon } from '@heroicons/react/24/outline'
import cn from 'classnames'
import Link from 'next/link'
import { groupBy } from 'lodash-es'
@@ -20,6 +21,7 @@ import {
} from '@/service/datasets'
import Button from '@/app/components/base/button'
import Loading from '@/app/components/base/loading'
import FloatRightContainer from '@/app/components/base/float-right-container'
import RetrievalMethodConfig from '@/app/components/datasets/common/retrieval-method-config'
import EconomicalRetrievalMethodConfig from '@/app/components/datasets/common/economical-retrieval-method-config'
import { type RetrievalConfig } from '@/types/app'
@@ -37,6 +39,8 @@ import I18n from '@/context/i18n'
import { IS_CE_EDITION } from '@/config'
import { RETRIEVE_METHOD } from '@/types/app'
import { useProviderContext } from '@/context/provider-context'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import Tooltip from '@/app/components/base/tooltip'
type ValueOf<T> = T[keyof T]
type StepTwoProps = {
@@ -84,6 +88,9 @@ const StepTwo = ({
const { t } = useTranslation()
const { locale } = useContext(I18n)
const media = useBreakpoints()
const isMobile = media === MediaType.mobile
const { dataset: currentDataset, mutateDatasetRes } = useDatasetDetailContext()
const scrollRef = useRef<HTMLDivElement>(null)
const [scrolled, setScrolled] = useState(false)
@@ -467,7 +474,7 @@ const StepTwo = ({
useEffect(() => {
if (segmentationType === SegmentType.AUTO) {
setAutomaticFileIndexingEstimate(null)
setShowPreview()
!isMobile && setShowPreview()
fetchFileIndexingEstimate()
setPreviewSwitched(false)
}
@@ -493,8 +500,23 @@ const StepTwo = ({
return (
<div className='flex w-full h-full'>
<div ref={scrollRef} className='relative h-full w-full overflow-y-scroll'>
<div className={cn(s.pageHeader, scrolled && s.fixed)}>{t('datasetCreation.steps.two')}</div>
<div className={cn(s.form)}>
<div className={cn(s.pageHeader, scrolled && s.fixed, isMobile && '!px-6')}>
<span>{t('datasetCreation.steps.two')}</span>
{isMobile && (
<Button
className='border-[0.5px] !h-8 hover:outline hover:outline-[0.5px] hover:outline-gray-300 text-gray-700 font-medium bg-white shadow-[0px_1px_2px_0px_rgba(16,24,40,0.05)]'
onClick={setShowPreview}
>
<Tooltip selector='data-preview-toggle'>
<div className="flex flex-row items-center">
<RocketLaunchIcon className="h-4 w-4 mr-1.5 stroke-[1.8px]" />
<span className="text-[13px]">{t('datasetCreation.stepTwo.previewTitleButton')}</span>
</div>
</Tooltip>
</Button>
)}
</div>
<div className={cn(s.form, isMobile && '!px-4')}>
<div className={s.label}>{t('datasetCreation.stepTwo.segmentation')}</div>
<div className='max-w-[640px]'>
<div
@@ -554,7 +576,7 @@ const StepTwo = ({
</div>
</div>
<div className={s.formRow}>
<div className='w-full'>
<div className='w-full flex flex-col gap-1'>
<div className={s.label}>{t('datasetCreation.stepTwo.rules')}</div>
{rules.map(rule => (
<div key={rule.id} className={s.ruleItem}>
@@ -574,7 +596,7 @@ const StepTwo = ({
</div>
<div className={s.label}>{t('datasetCreation.stepTwo.indexMode')}</div>
<div className='max-w-[640px]'>
<div className='flex items-center gap-3'>
<div className='flex items-center gap-3 flex-wrap sm:flex-nowrap'>
{(!hasSetIndexType || (hasSetIndexType && indexingType === IndexingType.QUALIFIED)) && (
<div
className={cn(
@@ -797,68 +819,69 @@ const StepTwo = ({
</div>
</div>
</div>
{(showPreview)
? (
<div ref={previewScrollRef} className={cn(s.previewWrap, 'relativeh-full overflow-y-scroll border-l border-[#F2F4F7]')}>
<div className={cn(s.previewHeader, previewScrolled && `${s.fixed} pb-3`)}>
<div className='flex items-center justify-between px-8'>
<div className='grow flex items-center'>
<div>{t('datasetCreation.stepTwo.previewTitle')}</div>
{docForm === DocForm.QA && !previewSwitched && (
<Button className='ml-2 !h-[26px] !py-[3px] !px-2 !text-xs !font-medium !text-primary-600' onClick={previewSwitch}>{t('datasetCreation.stepTwo.previewButton')}</Button>
)}
</div>
<div className='flex items-center justify-center w-6 h-6 cursor-pointer' onClick={hidePreview}>
<XMarkIcon className='h-4 w-4'></XMarkIcon>
</div>
<FloatRightContainer isMobile={isMobile} isOpen={showPreview} onClose={hidePreview} footer={null}>
{showPreview && <div ref={previewScrollRef} className={cn(s.previewWrap, 'relative h-full overflow-y-scroll border-l border-[#F2F4F7]')}>
<div className={cn(s.previewHeader, previewScrolled && `${s.fixed} pb-3`)}>
<div className='flex items-center justify-between px-8'>
<div className='grow flex items-center'>
<div>{t('datasetCreation.stepTwo.previewTitle')}</div>
{docForm === DocForm.QA && !previewSwitched && (
<Button className='ml-2 !h-[26px] !py-[3px] !px-2 !text-xs !font-medium !text-primary-600' onClick={previewSwitch}>{t('datasetCreation.stepTwo.previewButton')}</Button>
)}
</div>
<div className='flex items-center justify-center w-6 h-6 cursor-pointer' onClick={hidePreview}>
<XMarkIcon className='h-4 w-4'></XMarkIcon>
</div>
{docForm === DocForm.QA && !previewSwitched && (
<div className='px-8 pr-12 text-xs text-gray-500'>
<span>{t('datasetCreation.stepTwo.previewSwitchTipStart')}</span>
<span className='text-amber-600'>{t('datasetCreation.stepTwo.previewSwitchTipEnd')}</span>
</div>
)}
</div>
<div className='my-4 px-8 space-y-4'>
{previewSwitched && docForm === DocForm.QA && fileIndexingEstimate?.qa_preview && (
<>
{fileIndexingEstimate?.qa_preview.map((item, index) => (
<PreviewItem type={PreviewType.QA} key={item.question} qa={item} index={index + 1} />
))}
</>
)}
{(docForm === DocForm.TEXT || !previewSwitched) && fileIndexingEstimate?.preview && (
<>
{fileIndexingEstimate?.preview.map((item, index) => (
<PreviewItem type={PreviewType.TEXT} key={item} content={item} index={index + 1} />
))}
</>
)}
{previewSwitched && docForm === DocForm.QA && !fileIndexingEstimate?.qa_preview && (
<div className='flex items-center justify-center h-[200px]'>
<Loading type='area' />
</div>
)}
{!previewSwitched && !fileIndexingEstimate?.preview && (
<div className='flex items-center justify-center h-[200px]'>
<Loading type='area' />
</div>
)}
{docForm === DocForm.QA && !previewSwitched && (
<div className='px-8 pr-12 text-xs text-gray-500'>
<span>{t('datasetCreation.stepTwo.previewSwitchTipStart')}</span>
<span className='text-amber-600'>{t('datasetCreation.stepTwo.previewSwitchTipEnd')}</span>
</div>
)}
</div>
<div className='my-4 px-8 space-y-4'>
{previewSwitched && docForm === DocForm.QA && fileIndexingEstimate?.qa_preview && (
<>
{fileIndexingEstimate?.qa_preview.map((item, index) => (
<PreviewItem type={PreviewType.QA} key={item.question} qa={item} index={index + 1} />
))}
</>
)}
{(docForm === DocForm.TEXT || !previewSwitched) && fileIndexingEstimate?.preview && (
<>
{fileIndexingEstimate?.preview.map((item, index) => (
<PreviewItem type={PreviewType.TEXT} key={item} content={item} index={index + 1} />
))}
</>
)}
{previewSwitched && docForm === DocForm.QA && !fileIndexingEstimate?.qa_preview && (
<div className='flex items-center justify-center h-[200px]'>
<Loading type='area' />
</div>
)}
{!previewSwitched && !fileIndexingEstimate?.preview && (
<div className='flex items-center justify-center h-[200px]'>
<Loading type='area' />
</div>
)}
</div>
</div>}
{!showPreview && (
<div className={cn(s.sideTip)}>
<div className={s.tipCard}>
<span className={s.icon} />
<div className={s.title}>{t('datasetCreation.stepTwo.sideTipTitle')}</div>
<div className={s.content}>
<p className='mb-3'>{t('datasetCreation.stepTwo.sideTipP1')}</p>
<p className='mb-3'>{t('datasetCreation.stepTwo.sideTipP2')}</p>
<p className='mb-3'>{t('datasetCreation.stepTwo.sideTipP3')}</p>
<p>{t('datasetCreation.stepTwo.sideTipP4')}</p>
</div>
</div>
</div>
)
: (<div className={cn(s.sideTip)}>
<div className={s.tipCard}>
<span className={s.icon} />
<div className={s.title}>{t('datasetCreation.stepTwo.sideTipTitle')}</div>
<div className={s.content}>
<p className='mb-3'>{t('datasetCreation.stepTwo.sideTipP1')}</p>
<p className='mb-3'>{t('datasetCreation.stepTwo.sideTipP2')}</p>
<p className='mb-3'>{t('datasetCreation.stepTwo.sideTipP3')}</p>
<p>{t('datasetCreation.stepTwo.sideTipP4')}</p>
</div>
</div>
</div>)}
)}
</FloatRightContainer>
</div>
)
}

View File

@@ -14,14 +14,15 @@
background-size: 16px;
}
.stepList {
@apply p-4;
@apply p-4 relative;
line-height: 18px;
}
.stepItem {
@apply relative flex justify-items-start pt-3 pr-0 pb-3;
@apply relative flex justify-items-start pt-3 pr-0 pb-3 box-content;
padding-left: 52px;
font-size: 13px;
height: 18px;
}
.stepItem.step1::before {

View File

@@ -3,46 +3,56 @@ import { useTranslation } from 'react-i18next'
import { useRouter } from 'next/navigation'
import cn from 'classnames'
import { useCallback } from 'react'
import s from './index.module.css'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
type IStepsNavBarProps = {
step: number,
datasetId?: string,
step: number
datasetId?: string
}
const STEP_T_MAP: Record<number, string> = {
1: 'datasetCreation.steps.one',
2: 'datasetCreation.steps.two',
3: 'datasetCreation.steps.three',
}
const STEP_LIST = [1, 2, 3]
const StepsNavBar = ({
step,
datasetId,
}: IStepsNavBarProps) => {
const { t } = useTranslation()
const router = useRouter()
const navBackHandle = () => {
if (!datasetId) {
const media = useBreakpoints()
const isMobile = media === MediaType.mobile
const navBackHandle = useCallback(() => {
if (!datasetId)
router.replace('/datasets')
} else {
else
router.replace(`/datasets/${datasetId}/documents`)
}
}
}, [router, datasetId])
return (
<div className='w-full pt-4'>
<div className={s.stepsHeader}>
<div onClick={navBackHandle} className={s.navBack} />
{!datasetId ? t('datasetCreation.steps.header.creation') : t('datasetCreation.steps.header.update')}
<div className={cn(s.stepsHeader, isMobile && '!px-0 justify-center')}>
<div onClick={navBackHandle} className={cn(s.navBack, isMobile && '!mr-0')} />
{!isMobile && (!datasetId ? t('datasetCreation.steps.header.creation') : t('datasetCreation.steps.header.update'))}
</div>
<div className={cn(s.stepList)}>
<div className={cn(s.stepItem, s.step1, step === 1 && s.active, step !== 1 && s.done)}>
<div className={cn(s.stepNum)}>{step === 1 ? 1 : ''}</div>
<div className={cn(s.stepName)}>{t('datasetCreation.steps.one')}</div>
</div>
<div className={cn(s.stepItem, s.step2, step === 2 && s.active, step === 3 && s.done)}>
<div className={cn(s.stepNum)}>{step !== 3 ? 2 : ''}</div>
<div className={cn(s.stepName)}>{t('datasetCreation.steps.two')}</div>
</div>
<div className={cn(s.stepItem, s.step3, step === 3 && s.active)}>
<div className={cn(s.stepNum)}>3</div>
<div className={cn(s.stepName)}>{t('datasetCreation.steps.three')}</div>
</div>
<div className={cn(s.stepList, isMobile && '!p-0')}>
{STEP_LIST.map(item => (
<div
key={item}
className={cn(s.stepItem, s[`step${item}`], step === item && s.active, step > item && s.done, isMobile && 'px-0')}
>
<div className={cn(s.stepNum)}>{item}</div>
<div className={cn(s.stepName)}>{isMobile ? '' : t(STEP_T_MAP[item])}</div>
</div>
))}
</div>
</div>
)