From 7200dd42d33f909ebfa6b83f092e4b6542202863 Mon Sep 17 00:00:00 2001 From: twwu Date: Thu, 5 Dec 2024 09:49:37 +0800 Subject: [PATCH 1/2] refactor: update batch action component props and change id type in ChildChunkDetail --- .../documents/detail/completed/batch-action.tsx | 11 +++++++---- web/models/datasets.ts | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/web/app/components/datasets/documents/detail/completed/batch-action.tsx b/web/app/components/datasets/documents/detail/completed/batch-action.tsx index 3bed21df96..7a55784e70 100644 --- a/web/app/components/datasets/documents/detail/completed/batch-action.tsx +++ b/web/app/components/datasets/documents/detail/completed/batch-action.tsx @@ -1,9 +1,11 @@ import React, { type FC } from 'react' import { RiCheckboxCircleLine, RiCloseCircleLine, RiDeleteBinLine } from '@remixicon/react' import Divider from '@/app/components/base/divider' +import classNames from '@/utils/classnames' type IBatchActionProps = { - selectedSegmentIds: string[] + className?: string + selectedIds: string[] onBatchEnable: () => Promise onBatchDisable: () => Promise onBatchDelete: () => Promise @@ -11,18 +13,19 @@ type IBatchActionProps = { } const BatchAction: FC = ({ - selectedSegmentIds, + className, + selectedIds, onBatchEnable, onBatchDisable, onBatchDelete, onCancel, }) => { return ( -
+
- {selectedSegmentIds.length} + {selectedIds.length} Selected
diff --git a/web/models/datasets.ts b/web/models/datasets.ts index ed4ab2fb7d..c2bdedaf62 100644 --- a/web/models/datasets.ts +++ b/web/models/datasets.ts @@ -591,7 +591,7 @@ export const DEFAULT_WEIGHTED_SCORE = { export type ChildChunkType = 'automatic' | 'customized' export type ChildChunkDetail = { - id: number + id: string position: number segment_id: string content: string From f8d6d5a6a91f421de6c1b62c672e900004e55a83 Mon Sep 17 00:00:00 2001 From: twwu Date: Thu, 5 Dec 2024 10:12:02 +0800 Subject: [PATCH 2/2] refactor: replace DocumentContext with useDocumentContext for improved context management --- .../documents/detail/completed/index.tsx | 195 +----------------- .../documents/detail/embedding/index.tsx | 4 +- .../datasets/documents/detail/index.tsx | 29 ++- .../documents/detail/metadata/index.tsx | 4 +- 4 files changed, 34 insertions(+), 198 deletions(-) diff --git a/web/app/components/datasets/documents/detail/completed/index.tsx b/web/app/components/datasets/documents/detail/completed/index.tsx index c7978620a3..adba7dbe1c 100644 --- a/web/app/components/datasets/documents/detail/completed/index.tsx +++ b/web/app/components/datasets/documents/detail/completed/index.tsx @@ -1,35 +1,27 @@ 'use client' import type { FC } from 'react' -import React, { memo, useCallback, useEffect, useMemo, useState } from 'react' +import React, { useCallback, useEffect, useMemo, useState } from 'react' import { useDebounceFn } from 'ahooks' import { useTranslation } from 'react-i18next' import { createContext, useContext, useContextSelector } from 'use-context-selector' -import { - RiCloseLine, - RiEditLine, -} from '@remixicon/react' -import { StatusItem } from '../../list' -import { DocumentContext } from '../index' +import { useDocumentContext } from '../index' import { ProcessStatus } from '../segment-add' import s from './style.module.css' import SegmentList from './segment-list' import DisplayToggle from './display-toggle' import BatchAction from './batch-action' +import SegmentDetail from './segment-detail' import cn from '@/utils/classnames' import { formatNumber } from '@/utils/format' import Modal from '@/app/components/base/modal' -import Switch from '@/app/components/base/switch' import Divider from '@/app/components/base/divider' import Input from '@/app/components/base/input' import { ToastContext } from '@/app/components/base/toast' import type { Item } from '@/app/components/base/select' import { SimpleSelect } from '@/app/components/base/select' import { updateSegment } from '@/service/datasets' -import type { ParentMode, ProcessMode, SegmentDetailModel, SegmentUpdater } from '@/models/datasets' -import AutoHeightTextarea from '@/app/components/base/auto-height-textarea/common' -import Button from '@/app/components/base/button' +import type { SegmentDetailModel, SegmentUpdater } from '@/models/datasets' import NewSegmentModal from '@/app/components/datasets/documents/detail/new-segment-modal' -import TagInput from '@/app/components/base/tag-input' import { useEventEmitterContextContext } from '@/context/event-emitter' import Checkbox from '@/app/components/base/checkbox' import { useDeleteSegment, useDisableSegment, useEnableSegment, useSegmentList } from '@/service/knowledge/use-segment' @@ -66,184 +58,12 @@ export const SegmentIndexTag: FC<{ positionId: string | number; className?: stri ) } -type ISegmentDetailProps = { - embeddingAvailable: boolean - segInfo?: Partial & { id: string } - onChangeSwitch?: (enabled: boolean, segId?: string) => Promise - onUpdate: (segmentId: string, q: string, a: string, k: string[]) => void - onCancel: () => void - archived?: boolean - isEditing?: boolean -} -/** - * Show all the contents of the segment - */ -const SegmentDetailComponent: FC = ({ - embeddingAvailable, - segInfo, - archived, - onChangeSwitch, - onUpdate, - onCancel, - isEditing: initialIsEditing, -}) => { - const { t } = useTranslation() - const [isEditing, setIsEditing] = useState(initialIsEditing) - const [question, setQuestion] = useState(segInfo?.content || '') - const [answer, setAnswer] = useState(segInfo?.answer || '') - const [keywords, setKeywords] = useState(segInfo?.keywords || []) - const { eventEmitter } = useEventEmitterContextContext() - const [loading, setLoading] = useState(false) - - eventEmitter?.useSubscription((v) => { - if (v === 'update-segment') - setLoading(true) - else - setLoading(false) - }) - - const handleCancel = () => { - setIsEditing(false) - setQuestion(segInfo?.content || '') - setAnswer(segInfo?.answer || '') - setKeywords(segInfo?.keywords || []) - } - const handleSave = () => { - onUpdate(segInfo?.id || '', question, answer, keywords) - } - - const renderContent = () => { - if (segInfo?.answer) { - return ( - <> -
QUESTION
- setQuestion(e.target.value)} - disabled={!isEditing} - /> -
ANSWER
- setAnswer(e.target.value)} - disabled={!isEditing} - autoFocus - /> - - ) - } - - return ( - setQuestion(e.target.value)} - disabled={!isEditing} - autoFocus - /> - ) - } - - return ( -
-
- {isEditing && ( - <> - - - - )} - {!isEditing && !archived && embeddingAvailable && ( - <> -
-
{t('common.operation.edit')}
- setIsEditing(true)} /> -
-
- - )} -
- -
-
- -
{renderContent()}
-
{t('datasetDocuments.segment.keywords')}
-
- {!segInfo?.keywords?.length - ? '-' - : ( - setKeywords(newKeywords)} - disableAdd={!isEditing} - disableRemove={!isEditing || (keywords.length === 1)} - /> - ) - } -
-
-
-
{formatNumber(segInfo?.word_count as number)} {t('datasetDocuments.segment.characters')} -
{formatNumber(segInfo?.hit_count as number)} {t('datasetDocuments.segment.hitCount')} -
{t('datasetDocuments.segment.vectorHash')}{segInfo?.index_node_hash} -
-
- - {embeddingAvailable && ( - <> - - { - await onChangeSwitch?.(val, segInfo?.id || '') - }} - disabled={archived} - /> - - )} -
-
-
- ) -} -export const SegmentDetail = memo(SegmentDetailComponent) - -export const splitArray = (arr: any[], size = 3) => { - if (!arr || !arr.length) - return [] - const result = [] - for (let i = 0; i < arr.length; i += size) - result.push(arr.slice(i, i + size)) - return result -} - type ICompletedProps = { embeddingAvailable: boolean showNewSegmentModal: boolean onNewSegmentModalChange: (state: boolean) => void importStatus: ProcessStatus | string | undefined archived?: boolean - mode?: ProcessMode - parentMode?: ParentMode // data: Array<{}> // all/part segments } /** @@ -256,12 +76,10 @@ const Completed: FC = ({ onNewSegmentModalChange, importStatus, archived, - mode, - parentMode, }) => { const { t } = useTranslation() const { notify } = useContext(ToastContext) - const { datasetId = '', documentId = '', docForm } = useContext(DocumentContext) + const [datasetId = '', documentId = '', docForm, mode, parentMode] = useDocumentContext(s => [s.datasetId, s.documentId, s.docForm, s.mode, s.parentMode]) // the current segment id and whether to show the modal const [currSegment, setCurrSegment] = useState<{ segInfo?: SegmentDetailModel; showModal: boolean; isEditing?: boolean }>({ showModal: false }) @@ -504,7 +322,8 @@ const Completed: FC = ({ /> {selectedSegmentIds.length > 0 && = ({ detail, stopPosition = 'top', datasetId: d const { t } = useTranslation() const { notify } = useContext(ToastContext) - const { datasetId = '', documentId = '' } = useContext(DocumentContext) + const [datasetId, documentId] = useDocumentContext(s => [s.datasetId, s.documentId]) const localDatasetId = dstId ?? datasetId const localDocumentId = docId ?? documentId diff --git a/web/app/components/datasets/documents/detail/index.tsx b/web/app/components/datasets/documents/detail/index.tsx index ea3a91245f..7ce8d54473 100644 --- a/web/app/components/datasets/documents/detail/index.tsx +++ b/web/app/components/datasets/documents/detail/index.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import React, { useState } from 'react' import useSWR from 'swr' -import { createContext, useContext } from 'use-context-selector' +import { createContext, useContext, useContextSelector } from 'use-context-selector' import { useTranslation } from 'react-i18next' import { useRouter } from 'next/navigation' import { omit } from 'lodash-es' @@ -25,7 +25,20 @@ import type { DocForm, ParentMode, ProcessMode } from '@/models/datasets' import { useDatasetDetailContext } from '@/context/dataset-detail' import FloatRightContainer from '@/app/components/base/float-right-container' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' -export const DocumentContext = createContext<{ datasetId?: string; documentId?: string; docForm: string }>({ docForm: '' }) + +type DocumentContextValue = { + datasetId?: string + documentId?: string + docForm: string + mode?: ProcessMode + parentMode?: ParentMode +} + +export const DocumentContext = createContext({ docForm: '' }) + +export const useDocumentContext = (selector: (value: DocumentContextValue) => any) => { + return useContextSelector(DocumentContext, selector) +} type DocumentTitleProps = { datasetId: string @@ -140,7 +153,13 @@ const DocumentDetail: FC = ({ datasetId, documentId }) => { } return ( - +
@@ -203,7 +222,7 @@ const DocumentDetail: FC = ({ datasetId, documentId }) => {
{isDetailLoading ? - :
+ :
{embedding ? : = ({ datasetId, documentId }) => { onNewSegmentModalChange={setNewSegmentModalVisible} importStatus={importStatus} archived={documentDetail?.archived} - mode={documentDetail?.dataset_process_rule.mode} - parentMode={documentDetail?.dataset_process_rule.rules.parent_mode} /> }
diff --git a/web/app/components/datasets/documents/detail/metadata/index.tsx b/web/app/components/datasets/documents/detail/metadata/index.tsx index 6c1d749a28..659fff6d18 100644 --- a/web/app/components/datasets/documents/detail/metadata/index.tsx +++ b/web/app/components/datasets/documents/detail/metadata/index.tsx @@ -5,7 +5,7 @@ import { PencilIcon } from '@heroicons/react/24/outline' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import { get } from 'lodash-es' -import { DocumentContext } from '../index' +import { useDocumentContext } from '../index' import s from './style.module.css' import cn from '@/utils/classnames' import Input from '@/app/components/base/input' @@ -151,7 +151,7 @@ const Metadata: FC = ({ docDetail, loading, onUpdate }) => { const [saveLoading, setSaveLoading] = useState(false) const { notify } = useContext(ToastContext) - const { datasetId = '', documentId = '' } = useContext(DocumentContext) + const [datasetId, documentId] = useDocumentContext(s => [s.datasetId, s.documentId]) useEffect(() => { if (docDetail?.doc_type) {