diff --git a/web/app/components/base/retry-button/index.tsx b/web/app/components/base/retry-button/index.tsx
deleted file mode 100644
index 689827af7b..0000000000
--- a/web/app/components/base/retry-button/index.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React, { useEffect, useReducer } from 'react'
-import { useTranslation } from 'react-i18next'
-import useSWR from 'swr'
-import s from './style.module.css'
-import classNames from '@/utils/classnames'
-import Divider from '@/app/components/base/divider'
-import { getErrorDocs, retryErrorDocs } from '@/service/datasets'
-import type { IndexingStatusResponse } from '@/models/datasets'
-
-const WarningIcon = () =>
-
-
-type Props = {
- datasetId: string
-}
-type IIndexState = {
- value: string
-}
-type ActionType = 'retry' | 'success' | 'error'
-
-type IAction = {
- type: ActionType
-}
-const indexStateReducer = (state: IIndexState, action: IAction) => {
- const actionMap = {
- retry: 'retry',
- success: 'success',
- error: 'error',
- }
-
- return {
- ...state,
- value: actionMap[action.type] || state.value,
- }
-}
-
-const RetryButton: FC = ({ datasetId }) => {
- const { t } = useTranslation()
- const [indexState, dispatch] = useReducer(indexStateReducer, { value: 'success' })
- const { data: errorDocs } = useSWR({ datasetId }, getErrorDocs)
-
- const onRetryErrorDocs = async () => {
- dispatch({ type: 'retry' })
- const document_ids = errorDocs?.data.map((doc: IndexingStatusResponse) => doc.id) || []
- const res = await retryErrorDocs({ datasetId, document_ids })
- if (res.result === 'success')
- dispatch({ type: 'success' })
- else
- dispatch({ type: 'error' })
- }
-
- useEffect(() => {
- if (errorDocs?.total === 0)
- dispatch({ type: 'success' })
- else
- dispatch({ type: 'error' })
- }, [errorDocs?.total])
-
- if (indexState.value === 'success')
- return null
-
- return (
-
-
-
- {errorDocs?.total} {t('dataset.docsFailedNotice')}
-
-
-
- {t('dataset.retry')}
-
-
- )
-}
-export default RetryButton
diff --git a/web/app/components/base/retry-button/style.module.css b/web/app/components/base/retry-button/style.module.css
deleted file mode 100644
index 99a0947576..0000000000
--- a/web/app/components/base/retry-button/style.module.css
+++ /dev/null
@@ -1,4 +0,0 @@
-.retryBtn {
- @apply inline-flex justify-center items-center content-center h-9 leading-5 rounded-lg px-4 py-2 text-base;
- @apply border-solid border border-gray-200 text-gray-500 hover:bg-white hover:shadow-sm hover:border-gray-300;
-}
diff --git a/web/app/components/datasets/common/document-status-with-action/index-failed.tsx b/web/app/components/datasets/common/document-status-with-action/index-failed.tsx
new file mode 100644
index 0000000000..05ed9cd4b6
--- /dev/null
+++ b/web/app/components/datasets/common/document-status-with-action/index-failed.tsx
@@ -0,0 +1,69 @@
+'use client'
+import type { FC } from 'react'
+import React, { useEffect, useReducer } from 'react'
+import { useTranslation } from 'react-i18next'
+import useSWR from 'swr'
+import StatusWithAction from './status-with-action'
+import { getErrorDocs, retryErrorDocs } from '@/service/datasets'
+import type { IndexingStatusResponse } from '@/models/datasets'
+
+type Props = {
+ datasetId: string
+}
+type IIndexState = {
+ value: string
+}
+type ActionType = 'retry' | 'success' | 'error'
+
+type IAction = {
+ type: ActionType
+}
+const indexStateReducer = (state: IIndexState, action: IAction) => {
+ const actionMap = {
+ retry: 'retry',
+ success: 'success',
+ error: 'error',
+ }
+
+ return {
+ ...state,
+ value: actionMap[action.type] || state.value,
+ }
+}
+
+const RetryButton: FC = ({ datasetId }) => {
+ const { t } = useTranslation()
+ const [indexState, dispatch] = useReducer(indexStateReducer, { value: 'success' })
+ const { data: errorDocs } = useSWR({ datasetId }, getErrorDocs)
+
+ const onRetryErrorDocs = async () => {
+ dispatch({ type: 'retry' })
+ const document_ids = errorDocs?.data.map((doc: IndexingStatusResponse) => doc.id) || []
+ const res = await retryErrorDocs({ datasetId, document_ids })
+ if (res.result === 'success')
+ dispatch({ type: 'success' })
+ else
+ dispatch({ type: 'error' })
+ }
+
+ useEffect(() => {
+ if (errorDocs?.total === 0)
+ dispatch({ type: 'success' })
+ else
+ dispatch({ type: 'error' })
+ }, [errorDocs?.total])
+
+ if (indexState.value === 'success')
+ return null
+
+ return (
+ { }}
+ />
+ )
+}
+export default RetryButton
diff --git a/web/app/components/datasets/common/document-status-with-action/status-with-action.tsx b/web/app/components/datasets/common/document-status-with-action/status-with-action.tsx
new file mode 100644
index 0000000000..7127e12a89
--- /dev/null
+++ b/web/app/components/datasets/common/document-status-with-action/status-with-action.tsx
@@ -0,0 +1,63 @@
+'use client'
+import { RiAlertFill, RiCheckboxCircleFill, RiErrorWarningFill, RiInformation2Fill } from '@remixicon/react'
+import type { FC } from 'react'
+import React from 'react'
+import cn from '@/utils/classnames'
+import Divider from '@/app/components/base/divider'
+
+type Status = 'success' | 'error' | 'warning' | 'info'
+type Props = {
+ type?: Status
+ description: string
+ actionText: string
+ onAction: () => void
+ disabled?: boolean
+}
+
+const IconMap = {
+ success: {
+ Icon: RiCheckboxCircleFill,
+ color: 'text-text-success',
+ },
+ error: {
+ Icon: RiErrorWarningFill,
+ color: 'text-text-destructive',
+ },
+ warning: {
+ Icon: RiAlertFill,
+ color: 'text-text-warning-secondary',
+ },
+ info: {
+ Icon: RiInformation2Fill,
+ color: 'text-text-accent',
+ },
+}
+
+const getIcon = (type: Status) => {
+ return IconMap[type]
+}
+
+const StatusAction: FC = ({
+ type = 'info',
+ description,
+ actionText,
+ onAction,
+ disabled,
+}) => {
+ const { Icon, color } = getIcon(type)
+ return (
+
+
+
+
{description}
+
+
{actionText}
+
+ )
+}
+export default React.memo(StatusAction)
diff --git a/web/app/components/datasets/documents/index.tsx b/web/app/components/datasets/documents/index.tsx
index 1bf82c9fa6..84e7191179 100644
--- a/web/app/components/datasets/documents/index.tsx
+++ b/web/app/components/datasets/documents/index.tsx
@@ -20,7 +20,7 @@ import { NotionPageSelectorModal } from '@/app/components/base/notion-page-selec
import type { NotionPage } from '@/models/common'
import type { CreateDocumentReq } from '@/models/datasets'
import { DataSourceType } from '@/models/datasets'
-import RetryButton from '@/app/components/base/retry-button'
+import IndexFailed from '@/app/components/datasets/common/document-status-with-action/index-failed'
const FolderPlusIcon = ({ className }: React.SVGProps) => {
return