diff --git a/web/app/components/datasets/create-from-pipeline/create-options/create-from-scratch.tsx b/web/app/components/datasets/create-from-pipeline/create-form/index.tsx similarity index 80% rename from web/app/components/datasets/create-from-pipeline/create-options/create-from-scratch.tsx rename to web/app/components/datasets/create-from-pipeline/create-form/index.tsx index 17bcd1253c..f4920fdcc5 100644 --- a/web/app/components/datasets/create-from-pipeline/create-options/create-from-scratch.tsx +++ b/web/app/components/datasets/create-from-pipeline/create-form/index.tsx @@ -1,24 +1,19 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react' import AppIcon from '@/app/components/base/app-icon' import type { AppIconSelection } from '@/app/components/base/app-icon-picker' import AppIconPicker from '@/app/components/base/app-icon-picker' import Input from '@/app/components/base/input' import Textarea from '@/app/components/base/textarea' -import type { AppIconType } from '@/types/app' -import { RiCloseLine } from '@remixicon/react' -import PermissionSelector from '../../settings/permission-selector' -import type { CreateDatasetReq } from '@/models/datasets' -import { ChunkingMode, DatasetPermission } from '@/models/datasets' -import { useMembers } from '@/service/use-common' -import Button from '@/app/components/base/button' -import { useTranslation } from 'react-i18next' -import Toast from '@/app/components/base/toast' -import { useCreatePipelineDataset } from '@/service/knowledge/use-create-dataset' import type { Member } from '@/models/common' - -type CreateFromScratchProps = { - onClose: () => void -} +import { DatasetPermission } from '@/models/datasets' +import { useMembers } from '@/service/use-common' +import type { AppIconType } from '@/types/app' +import React, { useCallback, useEffect, useRef, useState } from 'react' +import { useTranslation } from 'react-i18next' +import PermissionSelector from '../../settings/permission-selector' +import Button from '@/app/components/base/button' +import { RiCloseLine } from '@remixicon/react' +import Toast from '@/app/components/base/toast' +import type { CreateFormData } from '@/models/pipeline' const DEFAULT_APP_ICON: AppIconSelection = { type: 'emoji', @@ -26,9 +21,15 @@ const DEFAULT_APP_ICON: AppIconSelection = { background: '#FFF4ED', } -const CreateFromScratch = ({ +type CreateFormProps = { + onCreate: (payload: CreateFormData) => void + onClose: () => void +} + +const CreateForm = ({ + onCreate, onClose, -}: CreateFromScratchProps) => { +}: CreateFormProps) => { const { t } = useTranslation() const [name, setName] = useState('') const [appIcon, setAppIcon] = useState(DEFAULT_APP_ICON) @@ -75,9 +76,7 @@ const CreateFromScratch = ({ setPermission(value!) }, []) - const { mutateAsync: createEmptyDataset } = useCreatePipelineDataset() - - const handleCreate = useCallback(async () => { + const handleCreate = useCallback(() => { if (!name) { Toast.notify({ type: 'error', @@ -85,34 +84,14 @@ const CreateFromScratch = ({ }) return } - const request: CreateDatasetReq = { + onCreate({ name, + appIcon, description, - icon_info: { - icon_type: appIcon.type, - icon: appIcon.type === 'image' ? appIcon.fileId : appIcon.icon, - icon_background: appIcon.type === 'image' ? undefined : appIcon.background, - icon_url: appIcon.type === 'image' ? appIcon.url : undefined, - }, - doc_form: ChunkingMode.text, permission, - } - // Handle permission - if (request.permission === DatasetPermission.partialMembers) { - const selectedMemberList = selectedMemberIDs.map((id) => { - return { - user_id: id, - role: memberList.find(member => member.id === id)?.role, - } - }) - request.partial_member_list = selectedMemberList - } - await createEmptyDataset(request, { - onSettled: () => { - onClose?.() - }, + selectedMemberIDs, }) - }, [name, permission, appIcon, description, createEmptyDataset, memberList, selectedMemberIDs, onClose]) + }, [name, appIcon, description, permission, selectedMemberIDs, onCreate]) return (
@@ -200,4 +179,4 @@ const CreateFromScratch = ({ ) } -export default React.memo(CreateFromScratch) +export default React.memo(CreateForm) diff --git a/web/app/components/datasets/create-from-pipeline/create-options/create-from-scratch-modal.tsx b/web/app/components/datasets/create-from-pipeline/create-options/create-from-scratch-modal.tsx new file mode 100644 index 0000000000..0dca658639 --- /dev/null +++ b/web/app/components/datasets/create-from-pipeline/create-options/create-from-scratch-modal.tsx @@ -0,0 +1,75 @@ +import React, { useCallback, useEffect, useState } from 'react' +import type { CreateDatasetReq } from '@/models/datasets' +import { ChunkingMode, DatasetPermission } from '@/models/datasets' +import { useMembers } from '@/service/use-common' +import { useCreatePipelineDataset } from '@/service/knowledge/use-create-dataset' +import type { Member } from '@/models/common' +import CreateForm from '../create-form' +import type { CreateFormData } from '@/models/pipeline' +import Modal from '@/app/components/base/modal' + +type CreateFromScratchModalProps = { + show: boolean + onClose: () => void +} + +const CreateFromScratchModal = ({ + show, + onClose, +}: CreateFromScratchModalProps) => { + const [memberList, setMemberList] = useState([]) + const { data: members } = useMembers() + + useEffect(() => { + if (members?.accounts) + setMemberList(members.accounts) + }, [members]) + + const { mutateAsync: createEmptyDataset } = useCreatePipelineDataset() + + const handleCreate = useCallback(async (payload: CreateFormData) => { + const { name, appIcon, description, permission, selectedMemberIDs } = payload + const request: CreateDatasetReq = { + name, + description, + icon_info: { + icon_type: appIcon.type, + icon: appIcon.type === 'image' ? appIcon.fileId : appIcon.icon, + icon_background: appIcon.type === 'image' ? undefined : appIcon.background, + icon_url: appIcon.type === 'image' ? appIcon.url : undefined, + }, + doc_form: ChunkingMode.text, + permission, + } + // Handle permission + if (request.permission === DatasetPermission.partialMembers) { + const selectedMemberList = selectedMemberIDs.map((id) => { + return { + user_id: id, + role: memberList.find(member => member.id === id)?.role, + } + }) + request.partial_member_list = selectedMemberList + } + await createEmptyDataset(request, { + onSettled: () => { + onClose?.() + }, + }) + }, [createEmptyDataset, memberList, onClose]) + + return ( + + + + ) +} + +export default CreateFromScratchModal diff --git a/web/app/components/datasets/create-from-pipeline/create-options/index.tsx b/web/app/components/datasets/create-from-pipeline/create-options/index.tsx index 382ccb67f5..8893e04ca6 100644 --- a/web/app/components/datasets/create-from-pipeline/create-options/index.tsx +++ b/web/app/components/datasets/create-from-pipeline/create-options/index.tsx @@ -1,8 +1,7 @@ import React, { useCallback, useMemo, useState } from 'react' import Item from './item' import { RiAddCircleFill, RiFileUploadLine } from '@remixicon/react' -import Modal from '@/app/components/base/modal' -import CreateFromScratch from './create-from-scratch' +import CreateFromScratchModal from './create-from-scratch-modal' import { useRouter, useSearchParams } from 'next/navigation' import CreateFromDSLModal, { CreateFromDSLModalTab } from './create-from-dsl-modal' import { useProviderContextSelector } from '@/context/provider-context' @@ -62,15 +61,10 @@ const CreateOptions = () => { description={t('datasetPipeline.creation.ImportDSL.description')} onClick={openImportFromDSL} /> - - - + /> { // TODO: remove mock data - const mockData: PipelineTemple[] = [{ + const mockData: PipelineTemplate[] = [{ id: '1', name: 'Pipeline 1', description: 'This is a description of Pipeline 1. When use the general chunking mode, the chunks retrieved and recalled are the same. When use the general chunking mode, the chunks retrieved and recalled are the same.', diff --git a/web/app/components/datasets/create-from-pipeline/list/customized-list.tsx b/web/app/components/datasets/create-from-pipeline/list/customized-list.tsx index df38d0a678..ece772cebb 100644 --- a/web/app/components/datasets/create-from-pipeline/list/customized-list.tsx +++ b/web/app/components/datasets/create-from-pipeline/list/customized-list.tsx @@ -1,10 +1,10 @@ import { ChunkingMode } from '@/models/datasets' import TemplateCard from './template-card' import { usePipelineTemplateList } from '@/service/use-pipeline' -import type { PipelineTemple } from '@/models/pipeline' +import type { PipelineTemplate } from '@/models/pipeline' const CustomizedList = () => { - const mockData: PipelineTemple[] = [{ + const mockData: PipelineTemplate[] = [{ id: '1', name: 'Pipeline 1', description: 'This is a description of Pipeline 1. When use the general chunking mode, the chunks retrieved and recalled are the same. When use the general chunking mode, the chunks retrieved and recalled are the same.', diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/actions.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/actions.tsx new file mode 100644 index 0000000000..f78f7bbae5 --- /dev/null +++ b/web/app/components/datasets/create-from-pipeline/list/template-card/actions.tsx @@ -0,0 +1,70 @@ +import Button from '@/app/components/base/button' +import { RiAddLine, RiArrowRightUpLine, RiMoreFill } from '@remixicon/react' +import React from 'react' +import { useTranslation } from 'react-i18next' +import Operations from './operations' +import CustomPopover from '@/app/components/base/popover' + +type ActionsProps = { + handleApplyTemplate: () => void + handleShowTemplateDetails: () => void + showMoreOperations: boolean + openEditModal: () => void + handleExportDSL: () => void + handleDelete: () => void +} + +const Actions = ({ + handleApplyTemplate, + handleShowTemplateDetails, + showMoreOperations, + openEditModal, + handleExportDSL, + handleDelete, +}: ActionsProps) => { + const { t } = useTranslation() + + return ( +
+ + + { + showMoreOperations && ( + + } + className={'z-20 min-w-[160px]'} + popupClassName={'rounded-xl bg-none shadow-none ring-0 min-w-[160px]'} + position='br' + trigger='click' + btnElement={ + + } + btnClassName='size-8 cursor-pointer justify-center rounded-lg p-0 shadow-xs shadow-shadow-shadow-3' + /> + ) + } +
+ ) +} + +export default React.memo(Actions) diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/apply-template-modal.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/apply-template-modal.tsx new file mode 100644 index 0000000000..0431b08055 --- /dev/null +++ b/web/app/components/datasets/create-from-pipeline/list/template-card/apply-template-modal.tsx @@ -0,0 +1,74 @@ +import Modal from '@/app/components/base/modal' +import CreateForm from '../../create-form' +import { useCallback, useEffect, useState } from 'react' +import type { CreateFormData } from '@/models/pipeline' +import { ChunkingMode, type CreateDatasetReq, DatasetPermission } from '@/models/datasets' +import { useCreatePipelineDataset } from '@/service/knowledge/use-create-dataset' +import type { Member } from '@/models/common' +import { useMembers } from '@/service/use-common' + +type ApplyTemplateModalProps = { + show: boolean + onClose: () => void +} + +const ApplyTemplateModal = ({ + show, + onClose, +}: ApplyTemplateModalProps) => { + const [memberList, setMemberList] = useState([]) + const { data: members } = useMembers() + + useEffect(() => { + if (members?.accounts) + setMemberList(members.accounts) + }, [members]) + + const { mutateAsync: createEmptyDataset } = useCreatePipelineDataset() // todo: yaml content + + const handleCreate = useCallback(async (payload: CreateFormData) => { + const { name, appIcon, description, permission, selectedMemberIDs } = payload + const request: CreateDatasetReq = { + name, + description, + icon_info: { + icon_type: appIcon.type, + icon: appIcon.type === 'image' ? appIcon.fileId : appIcon.icon, + icon_background: appIcon.type === 'image' ? undefined : appIcon.background, + icon_url: appIcon.type === 'image' ? appIcon.url : undefined, + }, + doc_form: ChunkingMode.text, + permission, + } + // Handle permission + if (request.permission === DatasetPermission.partialMembers) { + const selectedMemberList = selectedMemberIDs.map((id) => { + return { + user_id: id, + role: memberList.find(member => member.id === id)?.role, + } + }) + request.partial_member_list = selectedMemberList + } + await createEmptyDataset(request, { + onSettled: () => { + onClose?.() + }, + }) + }, [createEmptyDataset, memberList, onClose]) + + return ( + + + + ) +} + +export default ApplyTemplateModal diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/content.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/content.tsx new file mode 100644 index 0000000000..cf6527c092 --- /dev/null +++ b/web/app/components/datasets/create-from-pipeline/list/template-card/content.tsx @@ -0,0 +1,61 @@ +import AppIcon from '@/app/components/base/app-icon' +import { General } from '@/app/components/base/icons/src/public/knowledge/dataset-card' +import type { ChunkingMode, IconInfo } from '@/models/datasets' +import { DOC_FORM_ICON_WITH_BG, DOC_FORM_TEXT } from '@/models/datasets' +import React from 'react' +import { useTranslation } from 'react-i18next' + +type ContentProps = { + name: string + description: string + iconInfo: IconInfo + docForm: ChunkingMode +} + +const Content = ({ + name, + description, + iconInfo, + docForm, +}: ContentProps) => { + const { t } = useTranslation() + const Icon = DOC_FORM_ICON_WITH_BG[docForm] || General + + return ( + <> +
+
+ +
+ +
+
+
+
+ {name} +
+
+ {t(`dataset.chunkingMode.${DOC_FORM_TEXT[docForm]}`)} +
+
+
+

+ {description} +

+ + ) +} + +export default React.memo(Content) diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/edit-pipeline-info.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/edit-pipeline-info.tsx index 9c8733c755..b58502a8f6 100644 --- a/web/app/components/datasets/create-from-pipeline/list/template-card/edit-pipeline-info.tsx +++ b/web/app/components/datasets/create-from-pipeline/list/template-card/edit-pipeline-info.tsx @@ -8,12 +8,12 @@ import React, { useCallback, useRef, useState } from 'react' import Button from '@/app/components/base/button' import { useTranslation } from 'react-i18next' import Toast from '@/app/components/base/toast' -import type { PipelineTemple } from '@/models/pipeline' +import type { PipelineTemplate } from '@/models/pipeline' import { useUpdatePipelineInfo } from '@/service/use-pipeline' type EditPipelineInfoProps = { onClose: () => void - pipeline: PipelineTemple + pipeline: PipelineTemplate } const EditPipelineInfo = ({ diff --git a/web/app/components/datasets/create-from-pipeline/list/template-card/index.tsx b/web/app/components/datasets/create-from-pipeline/list/template-card/index.tsx index eb6089c3c8..878c81ffa3 100644 --- a/web/app/components/datasets/create-from-pipeline/list/template-card/index.tsx +++ b/web/app/components/datasets/create-from-pipeline/list/template-card/index.tsx @@ -1,15 +1,8 @@ import React, { useCallback, useState } from 'react' -import AppIcon from '@/app/components/base/app-icon' -import { General } from '@/app/components/base/icons/src/public/knowledge/dataset-card' import { useTranslation } from 'react-i18next' -import Button from '@/app/components/base/button' -import { RiAddLine, RiArrowRightUpLine, RiMoreFill } from '@remixicon/react' -import CustomPopover from '@/app/components/base/popover' -import Operations from './operations' import Modal from '@/app/components/base/modal' import EditPipelineInfo from './edit-pipeline-info' -import type { PipelineTemple } from '@/models/pipeline' -import { DOC_FORM_ICON_WITH_BG, DOC_FORM_TEXT } from '@/models/datasets' +import type { PipelineTemplate } from '@/models/pipeline' import Confirm from '@/app/components/base/confirm' import { useDeletePipeline, useExportPipelineDSL, useImportPipelineDSL, usePipelineTemplateById } from '@/service/use-pipeline' import { downloadFile } from '@/utils/format' @@ -18,9 +11,11 @@ import { DSLImportMode } from '@/models/app' import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks' import { useRouter } from 'next/navigation' import Details from './details' +import Content from './content' +import Actions from './actions' type TemplateCardProps = { - pipeline: PipelineTemple + pipeline: PipelineTemplate showMoreOperations?: boolean } @@ -131,81 +126,22 @@ const TemplateCard = ({ }) }, [pipeline.id, deletePipeline]) - const Icon = DOC_FORM_ICON_WITH_BG[pipeline.doc_form] || General - const iconInfo = pipeline.icon_info - return (
-
-
- -
- -
-
-
-
- {pipeline.name} -
-
- {t(`dataset.chunkingMode.${DOC_FORM_TEXT[pipeline.doc_form]}`)} -
-
-
-

- {pipeline.description} -

-
- - - { - showMoreOperations && ( - - } - className={'z-20 min-w-[160px]'} - popupClassName={'rounded-xl bg-none shadow-none ring-0 min-w-[160px]'} - position='br' - trigger='click' - btnElement={ - - } - btnClassName='size-8 cursor-pointer justify-center rounded-lg p-0 shadow-xs shadow-shadow-shadow-3' - /> - ) - } -
+ + {showEditModal && (