From 52ba180bf4885b2c245aba40f25779b23045d818 Mon Sep 17 00:00:00 2001 From: twwu Date: Thu, 5 Dec 2024 11:11:57 +0800 Subject: [PATCH] feat: add SegmentDetail component for displaying and editing segment information --- .../detail/completed/segment-detail.tsx | 183 ++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 web/app/components/datasets/documents/detail/completed/segment-detail.tsx diff --git a/web/app/components/datasets/documents/detail/completed/segment-detail.tsx b/web/app/components/datasets/documents/detail/completed/segment-detail.tsx new file mode 100644 index 0000000000..64589b910e --- /dev/null +++ b/web/app/components/datasets/documents/detail/completed/segment-detail.tsx @@ -0,0 +1,183 @@ +import React, { type FC, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { + RiCloseLine, + RiEditLine, +} from '@remixicon/react' +import { StatusItem } from '../../list' +import s from './style.module.css' +import { SegmentIndexTag } from '.' +import type { SegmentDetailModel } from '@/models/datasets' +import { useEventEmitterContextContext } from '@/context/event-emitter' +import AutoHeightTextarea from '@/app/components/base/auto-height-textarea/common' +import Switch from '@/app/components/base/switch' +import Button from '@/app/components/base/button' +import TagInput from '@/app/components/base/tag-input' +import cn from '@/utils/classnames' +import { formatNumber } from '@/utils/format' +import Divider from '@/app/components/base/divider' + +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 SegmentDetail: 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} + /> + + )} +
+
+
+ ) +} + +SegmentDetail.displayName = 'SegmentDetail' + +export default React.memo(SegmentDetail)