mirror of
https://github.com/langgenius/dify.git
synced 2025-12-19 09:17:19 -05:00
refactor: migrate common service toward TanStack Query (#29009)
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
'use client'
|
||||
import { useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useSWR from 'swr'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import Button from '@/app/components/base/button'
|
||||
|
||||
import { invitationCheck } from '@/service/common'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import useDocumentTitle from '@/hooks/use-document-title'
|
||||
import { useInvitationCheck } from '@/service/use-common'
|
||||
|
||||
const ActivateForm = () => {
|
||||
useDocumentTitle('')
|
||||
@@ -26,19 +26,21 @@ const ActivateForm = () => {
|
||||
token,
|
||||
},
|
||||
}
|
||||
const { data: checkRes } = useSWR(checkParams, invitationCheck, {
|
||||
revalidateOnFocus: false,
|
||||
onSuccess(data) {
|
||||
if (data.is_valid) {
|
||||
const params = new URLSearchParams(searchParams)
|
||||
const { email, workspace_id } = data.data
|
||||
params.set('email', encodeURIComponent(email))
|
||||
params.set('workspace_id', encodeURIComponent(workspace_id))
|
||||
params.set('invite_token', encodeURIComponent(token as string))
|
||||
router.replace(`/signin?${params.toString()}`)
|
||||
}
|
||||
},
|
||||
})
|
||||
const { data: checkRes } = useInvitationCheck({
|
||||
...checkParams.params,
|
||||
token: token || undefined,
|
||||
}, true)
|
||||
|
||||
useEffect(() => {
|
||||
if (checkRes?.is_valid) {
|
||||
const params = new URLSearchParams(searchParams)
|
||||
const { email, workspace_id } = checkRes.data
|
||||
params.set('email', encodeURIComponent(email))
|
||||
params.set('workspace_id', encodeURIComponent(workspace_id))
|
||||
params.set('invite_token', encodeURIComponent(token as string))
|
||||
router.replace(`/signin?${params.toString()}`)
|
||||
}
|
||||
}, [checkRes, router, searchParams, token])
|
||||
|
||||
return (
|
||||
<div className={
|
||||
|
||||
@@ -7,8 +7,9 @@ import { ChunkingMode, DataSourceType, DatasetPermission, RerankingModeEnum } fr
|
||||
import { IndexingType } from '@/app/components/datasets/create/step-two'
|
||||
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { updateDatasetSetting } from '@/service/datasets'
|
||||
import { fetchMembers } from '@/service/common'
|
||||
import { useMembers } from '@/service/use-common'
|
||||
import { RETRIEVE_METHOD, type RetrievalConfig } from '@/types/app'
|
||||
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
||||
|
||||
const mockNotify = jest.fn()
|
||||
const mockOnCancel = jest.fn()
|
||||
@@ -41,8 +42,10 @@ jest.mock('@/service/datasets', () => ({
|
||||
updateDatasetSetting: jest.fn(),
|
||||
}))
|
||||
|
||||
jest.mock('@/service/common', () => ({
|
||||
fetchMembers: jest.fn(),
|
||||
jest.mock('@/service/use-common', () => ({
|
||||
__esModule: true,
|
||||
...jest.requireActual('@/service/use-common'),
|
||||
useMembers: jest.fn(),
|
||||
}))
|
||||
|
||||
jest.mock('@/context/app-context', () => ({
|
||||
@@ -103,7 +106,7 @@ jest.mock('@/app/components/datasets/settings/utils', () => ({
|
||||
}))
|
||||
|
||||
const mockUpdateDatasetSetting = updateDatasetSetting as jest.MockedFunction<typeof updateDatasetSetting>
|
||||
const mockFetchMembers = fetchMembers as jest.MockedFunction<typeof fetchMembers>
|
||||
const mockUseMembers = useMembers as jest.MockedFunction<typeof useMembers>
|
||||
|
||||
const createRetrievalConfig = (overrides: Partial<RetrievalConfig> = {}): RetrievalConfig => ({
|
||||
search_method: RETRIEVE_METHOD.semantic,
|
||||
@@ -192,10 +195,43 @@ const renderWithProviders = (dataset: DataSet) => {
|
||||
)
|
||||
}
|
||||
|
||||
const createMemberList = (): DataSet['partial_member_list'] => ([
|
||||
'member-2',
|
||||
])
|
||||
|
||||
const renderSettingsModal = async (dataset: DataSet) => {
|
||||
renderWithProviders(dataset)
|
||||
await waitFor(() => expect(mockUseMembers).toHaveBeenCalled())
|
||||
}
|
||||
|
||||
describe('SettingsModal', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
mockIsWorkspaceDatasetOperator = false
|
||||
mockUseMembers.mockReturnValue({
|
||||
data: {
|
||||
accounts: [
|
||||
{
|
||||
id: 'user-1',
|
||||
name: 'User One',
|
||||
email: 'user@example.com',
|
||||
avatar: 'avatar.png',
|
||||
avatar_url: 'avatar.png',
|
||||
status: 'active',
|
||||
role: 'owner',
|
||||
},
|
||||
{
|
||||
id: 'member-2',
|
||||
name: 'Member Two',
|
||||
email: 'member@example.com',
|
||||
avatar: 'avatar.png',
|
||||
avatar_url: 'avatar.png',
|
||||
status: 'active',
|
||||
role: 'editor',
|
||||
},
|
||||
],
|
||||
},
|
||||
} as ReturnType<typeof useMembers>)
|
||||
mockUseModelList.mockImplementation((type: ModelTypeEnum) => {
|
||||
if (type === ModelTypeEnum.rerank) {
|
||||
return {
|
||||
@@ -213,261 +249,289 @@ describe('SettingsModal', () => {
|
||||
mockUseModelListAndDefaultModelAndCurrentProviderAndModel.mockReturnValue({ defaultModel: null, currentModel: null })
|
||||
mockUseCurrentProviderAndModel.mockReturnValue({ currentProvider: null, currentModel: null })
|
||||
mockCheckShowMultiModalTip.mockReturnValue(false)
|
||||
mockFetchMembers.mockResolvedValue({
|
||||
accounts: [
|
||||
{
|
||||
id: 'user-1',
|
||||
name: 'User One',
|
||||
email: 'user@example.com',
|
||||
avatar: 'avatar.png',
|
||||
avatar_url: 'avatar.png',
|
||||
status: 'active',
|
||||
role: 'owner',
|
||||
},
|
||||
{
|
||||
id: 'member-2',
|
||||
name: 'Member Two',
|
||||
email: 'member@example.com',
|
||||
avatar: 'avatar.png',
|
||||
avatar_url: 'avatar.png',
|
||||
status: 'active',
|
||||
role: 'editor',
|
||||
},
|
||||
],
|
||||
})
|
||||
mockUpdateDatasetSetting.mockResolvedValue(createDataset())
|
||||
})
|
||||
|
||||
it('renders dataset details', async () => {
|
||||
renderWithProviders(createDataset())
|
||||
// Rendering and basic field bindings.
|
||||
describe('Rendering', () => {
|
||||
it('should render dataset details when dataset is provided', async () => {
|
||||
// Arrange
|
||||
const dataset = createDataset()
|
||||
|
||||
await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
|
||||
// Act
|
||||
await renderSettingsModal(dataset)
|
||||
|
||||
expect(screen.getByPlaceholderText('datasetSettings.form.namePlaceholder')).toHaveValue('Test Dataset')
|
||||
expect(screen.getByPlaceholderText('datasetSettings.form.descPlaceholder')).toHaveValue('Description')
|
||||
})
|
||||
|
||||
it('calls onCancel when cancel is clicked', async () => {
|
||||
renderWithProviders(createDataset())
|
||||
|
||||
await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: 'common.operation.cancel' }))
|
||||
|
||||
expect(mockOnCancel).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('shows external knowledge info for external datasets', async () => {
|
||||
const dataset = createDataset({
|
||||
provider: 'external',
|
||||
external_knowledge_info: {
|
||||
external_knowledge_id: 'ext-id-123',
|
||||
external_knowledge_api_id: 'ext-api-id-123',
|
||||
external_knowledge_api_name: 'External Knowledge API',
|
||||
external_knowledge_api_endpoint: 'https://api.external.com',
|
||||
},
|
||||
// Assert
|
||||
expect(screen.getByPlaceholderText('datasetSettings.form.namePlaceholder')).toHaveValue('Test Dataset')
|
||||
expect(screen.getByPlaceholderText('datasetSettings.form.descPlaceholder')).toHaveValue('Description')
|
||||
})
|
||||
|
||||
renderWithProviders(dataset)
|
||||
it('should show external knowledge info when dataset is external', async () => {
|
||||
// Arrange
|
||||
const dataset = createDataset({
|
||||
provider: 'external',
|
||||
external_knowledge_info: {
|
||||
external_knowledge_id: 'ext-id-123',
|
||||
external_knowledge_api_id: 'ext-api-id-123',
|
||||
external_knowledge_api_name: 'External Knowledge API',
|
||||
external_knowledge_api_endpoint: 'https://api.external.com',
|
||||
},
|
||||
})
|
||||
|
||||
await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
|
||||
// Act
|
||||
await renderSettingsModal(dataset)
|
||||
|
||||
expect(screen.getByText('External Knowledge API')).toBeInTheDocument()
|
||||
expect(screen.getByText('https://api.external.com')).toBeInTheDocument()
|
||||
expect(screen.getByText('ext-id-123')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('updates name when user types', async () => {
|
||||
renderWithProviders(createDataset())
|
||||
|
||||
await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
|
||||
|
||||
const nameInput = screen.getByPlaceholderText('datasetSettings.form.namePlaceholder')
|
||||
await userEvent.clear(nameInput)
|
||||
await userEvent.type(nameInput, 'New Dataset Name')
|
||||
|
||||
expect(nameInput).toHaveValue('New Dataset Name')
|
||||
})
|
||||
|
||||
it('updates description when user types', async () => {
|
||||
renderWithProviders(createDataset())
|
||||
|
||||
await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
|
||||
|
||||
const descriptionInput = screen.getByPlaceholderText('datasetSettings.form.descPlaceholder')
|
||||
await userEvent.clear(descriptionInput)
|
||||
await userEvent.type(descriptionInput, 'New description')
|
||||
|
||||
expect(descriptionInput).toHaveValue('New description')
|
||||
})
|
||||
|
||||
it('shows and dismisses retrieval change tip when index method changes', async () => {
|
||||
const dataset = createDataset({ indexing_technique: IndexingType.ECONOMICAL })
|
||||
|
||||
renderWithProviders(dataset)
|
||||
|
||||
await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
|
||||
|
||||
await userEvent.click(screen.getByText('datasetCreation.stepTwo.qualified'))
|
||||
|
||||
expect(await screen.findByText('appDebug.datasetConfig.retrieveChangeTip')).toBeInTheDocument()
|
||||
|
||||
await userEvent.click(screen.getByLabelText('close-retrieval-change-tip'))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('appDebug.datasetConfig.retrieveChangeTip')).not.toBeInTheDocument()
|
||||
// Assert
|
||||
expect(screen.getByText('External Knowledge API')).toBeInTheDocument()
|
||||
expect(screen.getByText('https://api.external.com')).toBeInTheDocument()
|
||||
expect(screen.getByText('ext-id-123')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('requires dataset name before saving', async () => {
|
||||
renderWithProviders(createDataset())
|
||||
// User interactions that update visible state.
|
||||
describe('Interactions', () => {
|
||||
it('should call onCancel when cancel button is clicked', async () => {
|
||||
// Arrange
|
||||
const user = userEvent.setup()
|
||||
|
||||
await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
|
||||
// Act
|
||||
await renderSettingsModal(createDataset())
|
||||
await user.click(screen.getByRole('button', { name: 'common.operation.cancel' }))
|
||||
|
||||
const nameInput = screen.getByPlaceholderText('datasetSettings.form.namePlaceholder')
|
||||
await userEvent.clear(nameInput)
|
||||
await userEvent.click(screen.getByRole('button', { name: 'common.operation.save' }))
|
||||
|
||||
expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({
|
||||
type: 'error',
|
||||
message: 'datasetSettings.form.nameError',
|
||||
}))
|
||||
expect(mockUpdateDatasetSetting).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('requires rerank model when reranking is enabled', async () => {
|
||||
mockUseModelList.mockReturnValue({ data: [] })
|
||||
const dataset = createDataset({}, createRetrievalConfig({
|
||||
reranking_enable: true,
|
||||
reranking_model: {
|
||||
reranking_provider_name: '',
|
||||
reranking_model_name: '',
|
||||
},
|
||||
}))
|
||||
|
||||
renderWithProviders(dataset)
|
||||
|
||||
await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
|
||||
await userEvent.click(screen.getByRole('button', { name: 'common.operation.save' }))
|
||||
|
||||
expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({
|
||||
type: 'error',
|
||||
message: 'appDebug.datasetConfig.rerankModelRequired',
|
||||
}))
|
||||
expect(mockUpdateDatasetSetting).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('saves internal dataset changes', async () => {
|
||||
const rerankRetrieval = createRetrievalConfig({
|
||||
reranking_enable: true,
|
||||
reranking_model: {
|
||||
reranking_provider_name: 'rerank-provider',
|
||||
reranking_model_name: 'rerank-model',
|
||||
},
|
||||
})
|
||||
const dataset = createDataset({
|
||||
retrieval_model: rerankRetrieval,
|
||||
retrieval_model_dict: rerankRetrieval,
|
||||
// Assert
|
||||
expect(mockOnCancel).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
renderWithProviders(dataset)
|
||||
it('should update name input when user types', async () => {
|
||||
// Arrange
|
||||
const user = userEvent.setup()
|
||||
await renderSettingsModal(createDataset())
|
||||
|
||||
await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
|
||||
const nameInput = screen.getByPlaceholderText('datasetSettings.form.namePlaceholder')
|
||||
|
||||
const nameInput = screen.getByPlaceholderText('datasetSettings.form.namePlaceholder')
|
||||
await userEvent.clear(nameInput)
|
||||
await userEvent.type(nameInput, 'Updated Internal Dataset')
|
||||
await userEvent.click(screen.getByRole('button', { name: 'common.operation.save' }))
|
||||
// Act
|
||||
await user.clear(nameInput)
|
||||
await user.type(nameInput, 'New Dataset Name')
|
||||
|
||||
await waitFor(() => expect(mockUpdateDatasetSetting).toHaveBeenCalled())
|
||||
// Assert
|
||||
expect(nameInput).toHaveValue('New Dataset Name')
|
||||
})
|
||||
|
||||
expect(mockUpdateDatasetSetting).toHaveBeenCalledWith(expect.objectContaining({
|
||||
body: expect.objectContaining({
|
||||
name: 'Updated Internal Dataset',
|
||||
permission: DatasetPermission.allTeamMembers,
|
||||
}),
|
||||
}))
|
||||
expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({
|
||||
type: 'success',
|
||||
message: 'common.actionMsg.modifiedSuccessfully',
|
||||
}))
|
||||
expect(mockOnSave).toHaveBeenCalledWith(expect.objectContaining({
|
||||
name: 'Updated Internal Dataset',
|
||||
retrieval_model_dict: expect.objectContaining({
|
||||
it('should update description input when user types', async () => {
|
||||
// Arrange
|
||||
const user = userEvent.setup()
|
||||
await renderSettingsModal(createDataset())
|
||||
|
||||
const descriptionInput = screen.getByPlaceholderText('datasetSettings.form.descPlaceholder')
|
||||
|
||||
// Act
|
||||
await user.clear(descriptionInput)
|
||||
await user.type(descriptionInput, 'New description')
|
||||
|
||||
// Assert
|
||||
expect(descriptionInput).toHaveValue('New description')
|
||||
})
|
||||
|
||||
it('should show and dismiss retrieval change tip when indexing method changes', async () => {
|
||||
// Arrange
|
||||
const user = userEvent.setup()
|
||||
const dataset = createDataset({ indexing_technique: IndexingType.ECONOMICAL })
|
||||
|
||||
// Act
|
||||
await renderSettingsModal(dataset)
|
||||
await user.click(screen.getByText('datasetCreation.stepTwo.qualified'))
|
||||
|
||||
// Assert
|
||||
expect(await screen.findByText('appDebug.datasetConfig.retrieveChangeTip')).toBeInTheDocument()
|
||||
|
||||
// Act
|
||||
await user.click(screen.getByLabelText('close-retrieval-change-tip'))
|
||||
|
||||
// Assert
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('appDebug.datasetConfig.retrieveChangeTip')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('should open account setting modal when embedding model tip is clicked', async () => {
|
||||
// Arrange
|
||||
const user = userEvent.setup()
|
||||
|
||||
// Act
|
||||
await renderSettingsModal(createDataset())
|
||||
await user.click(screen.getByText('datasetSettings.form.embeddingModelTipLink'))
|
||||
|
||||
// Assert
|
||||
expect(mockSetShowAccountSettingModal).toHaveBeenCalledWith({ payload: ACCOUNT_SETTING_TAB.PROVIDER })
|
||||
})
|
||||
})
|
||||
|
||||
// Validation guardrails before saving.
|
||||
describe('Validation', () => {
|
||||
it('should block save when dataset name is empty', async () => {
|
||||
// Arrange
|
||||
const user = userEvent.setup()
|
||||
await renderSettingsModal(createDataset())
|
||||
|
||||
const nameInput = screen.getByPlaceholderText('datasetSettings.form.namePlaceholder')
|
||||
|
||||
// Act
|
||||
await user.clear(nameInput)
|
||||
await user.click(screen.getByRole('button', { name: 'common.operation.save' }))
|
||||
|
||||
// Assert
|
||||
expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({
|
||||
type: 'error',
|
||||
message: 'datasetSettings.form.nameError',
|
||||
}))
|
||||
expect(mockUpdateDatasetSetting).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should block save when reranking is enabled without model', async () => {
|
||||
// Arrange
|
||||
const user = userEvent.setup()
|
||||
mockUseModelList.mockReturnValue({ data: [] })
|
||||
const dataset = createDataset({}, createRetrievalConfig({
|
||||
reranking_enable: true,
|
||||
}),
|
||||
}))
|
||||
reranking_model: {
|
||||
reranking_provider_name: '',
|
||||
reranking_model_name: '',
|
||||
},
|
||||
}))
|
||||
|
||||
// Act
|
||||
await renderSettingsModal(dataset)
|
||||
await user.click(screen.getByRole('button', { name: 'common.operation.save' }))
|
||||
|
||||
// Assert
|
||||
expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({
|
||||
type: 'error',
|
||||
message: 'appDebug.datasetConfig.rerankModelRequired',
|
||||
}))
|
||||
expect(mockUpdateDatasetSetting).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
it('saves external dataset with partial members and updated retrieval params', async () => {
|
||||
const dataset = createDataset({
|
||||
provider: 'external',
|
||||
permission: DatasetPermission.partialMembers,
|
||||
partial_member_list: ['member-2'],
|
||||
external_retrieval_model: {
|
||||
top_k: 5,
|
||||
score_threshold: 0.3,
|
||||
score_threshold_enabled: true,
|
||||
},
|
||||
}, {
|
||||
score_threshold_enabled: true,
|
||||
score_threshold: 0.8,
|
||||
// Save flows and side effects.
|
||||
describe('Save', () => {
|
||||
it('should save internal dataset changes when form is valid', async () => {
|
||||
// Arrange
|
||||
const user = userEvent.setup()
|
||||
const rerankRetrieval = createRetrievalConfig({
|
||||
reranking_enable: true,
|
||||
reranking_model: {
|
||||
reranking_provider_name: 'rerank-provider',
|
||||
reranking_model_name: 'rerank-model',
|
||||
},
|
||||
})
|
||||
const dataset = createDataset({
|
||||
retrieval_model: rerankRetrieval,
|
||||
retrieval_model_dict: rerankRetrieval,
|
||||
})
|
||||
|
||||
// Act
|
||||
await renderSettingsModal(dataset)
|
||||
|
||||
const nameInput = screen.getByPlaceholderText('datasetSettings.form.namePlaceholder')
|
||||
await user.clear(nameInput)
|
||||
await user.type(nameInput, 'Updated Internal Dataset')
|
||||
await user.click(screen.getByRole('button', { name: 'common.operation.save' }))
|
||||
|
||||
// Assert
|
||||
await waitFor(() => expect(mockUpdateDatasetSetting).toHaveBeenCalled())
|
||||
|
||||
expect(mockUpdateDatasetSetting).toHaveBeenCalledWith(expect.objectContaining({
|
||||
body: expect.objectContaining({
|
||||
name: 'Updated Internal Dataset',
|
||||
permission: DatasetPermission.allTeamMembers,
|
||||
}),
|
||||
}))
|
||||
expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({
|
||||
type: 'success',
|
||||
message: 'common.actionMsg.modifiedSuccessfully',
|
||||
}))
|
||||
expect(mockOnSave).toHaveBeenCalledWith(expect.objectContaining({
|
||||
name: 'Updated Internal Dataset',
|
||||
retrieval_model_dict: expect.objectContaining({
|
||||
reranking_enable: true,
|
||||
}),
|
||||
}))
|
||||
})
|
||||
|
||||
renderWithProviders(dataset)
|
||||
|
||||
await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: 'common.operation.save' }))
|
||||
|
||||
await waitFor(() => expect(mockUpdateDatasetSetting).toHaveBeenCalled())
|
||||
|
||||
expect(mockUpdateDatasetSetting).toHaveBeenCalledWith(expect.objectContaining({
|
||||
body: expect.objectContaining({
|
||||
it('should save external dataset changes when partial members configured', async () => {
|
||||
// Arrange
|
||||
const user = userEvent.setup()
|
||||
const dataset = createDataset({
|
||||
provider: 'external',
|
||||
permission: DatasetPermission.partialMembers,
|
||||
external_retrieval_model: expect.objectContaining({
|
||||
partial_member_list: createMemberList(),
|
||||
external_retrieval_model: {
|
||||
top_k: 5,
|
||||
}),
|
||||
partial_member_list: [
|
||||
{
|
||||
user_id: 'member-2',
|
||||
role: 'editor',
|
||||
},
|
||||
],
|
||||
}),
|
||||
}))
|
||||
expect(mockOnSave).toHaveBeenCalledWith(expect.objectContaining({
|
||||
retrieval_model_dict: expect.objectContaining({
|
||||
score_threshold: 0.3,
|
||||
score_threshold_enabled: true,
|
||||
},
|
||||
}, {
|
||||
score_threshold_enabled: true,
|
||||
score_threshold: 0.8,
|
||||
}),
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
it('disables save button while saving', async () => {
|
||||
mockUpdateDatasetSetting.mockImplementation(() => new Promise(resolve => setTimeout(resolve, 100)))
|
||||
// Act
|
||||
await renderSettingsModal(dataset)
|
||||
await user.click(screen.getByRole('button', { name: 'common.operation.save' }))
|
||||
|
||||
renderWithProviders(createDataset())
|
||||
// Assert
|
||||
await waitFor(() => expect(mockUpdateDatasetSetting).toHaveBeenCalled())
|
||||
|
||||
await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
|
||||
expect(mockUpdateDatasetSetting).toHaveBeenCalledWith(expect.objectContaining({
|
||||
body: expect.objectContaining({
|
||||
permission: DatasetPermission.partialMembers,
|
||||
external_retrieval_model: expect.objectContaining({
|
||||
top_k: 5,
|
||||
}),
|
||||
partial_member_list: [
|
||||
{
|
||||
user_id: 'member-2',
|
||||
role: 'editor',
|
||||
},
|
||||
],
|
||||
}),
|
||||
}))
|
||||
expect(mockOnSave).toHaveBeenCalledWith(expect.objectContaining({
|
||||
retrieval_model_dict: expect.objectContaining({
|
||||
score_threshold_enabled: true,
|
||||
score_threshold: 0.8,
|
||||
}),
|
||||
}))
|
||||
})
|
||||
|
||||
const saveButton = screen.getByRole('button', { name: 'common.operation.save' })
|
||||
await userEvent.click(saveButton)
|
||||
it('should disable save button while saving', async () => {
|
||||
// Arrange
|
||||
const user = userEvent.setup()
|
||||
mockUpdateDatasetSetting.mockImplementation(() => new Promise(resolve => setTimeout(resolve, 100)))
|
||||
|
||||
expect(saveButton).toBeDisabled()
|
||||
})
|
||||
// Act
|
||||
await renderSettingsModal(createDataset())
|
||||
|
||||
it('shows error toast when save fails', async () => {
|
||||
mockUpdateDatasetSetting.mockRejectedValue(new Error('API Error'))
|
||||
const saveButton = screen.getByRole('button', { name: 'common.operation.save' })
|
||||
await user.click(saveButton)
|
||||
|
||||
renderWithProviders(createDataset())
|
||||
// Assert
|
||||
expect(saveButton).toBeDisabled()
|
||||
})
|
||||
|
||||
await waitFor(() => expect(mockFetchMembers).toHaveBeenCalled())
|
||||
it('should show error toast when save fails', async () => {
|
||||
// Arrange
|
||||
const user = userEvent.setup()
|
||||
mockUpdateDatasetSetting.mockRejectedValue(new Error('API Error'))
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: 'common.operation.save' }))
|
||||
// Act
|
||||
await renderSettingsModal(createDataset())
|
||||
await user.click(screen.getByRole('button', { name: 'common.operation.save' }))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({ type: 'error' }))
|
||||
// Assert
|
||||
await waitFor(() => {
|
||||
expect(mockNotify).toHaveBeenCalledWith(expect.objectContaining({ type: 'error' }))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { FC } from 'react'
|
||||
import { useMemo, useRef, useState } from 'react'
|
||||
import { useMount } from 'ahooks'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { isEqual } from 'lodash-es'
|
||||
import { RiCloseLine } from '@remixicon/react'
|
||||
@@ -21,10 +20,10 @@ import PermissionSelector from '@/app/components/datasets/settings/permission-se
|
||||
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
|
||||
import { useModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { fetchMembers } from '@/service/common'
|
||||
import type { Member } from '@/models/common'
|
||||
import { IndexingType } from '@/app/components/datasets/create/step-two'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import { useMembers } from '@/service/use-common'
|
||||
import { checkShowMultiModalTip } from '@/app/components/datasets/settings/utils'
|
||||
import { RetrievalChangeTip, RetrievalSection } from './retrieval-section'
|
||||
|
||||
@@ -63,6 +62,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
|
||||
const [scoreThresholdEnabled, setScoreThresholdEnabled] = useState(localeCurrentDataset?.external_retrieval_model.score_threshold_enabled ?? false)
|
||||
const [selectedMemberIDs, setSelectedMemberIDs] = useState<string[]>(currentDataset.partial_member_list || [])
|
||||
const [memberList, setMemberList] = useState<Member[]>([])
|
||||
const { data: membersData } = useMembers()
|
||||
|
||||
const [indexMethod, setIndexMethod] = useState(currentDataset.indexing_technique)
|
||||
const [retrievalConfig, setRetrievalConfig] = useState(localeCurrentDataset?.retrieval_model_dict as RetrievalConfig)
|
||||
@@ -160,17 +160,12 @@ const SettingsModal: FC<SettingsModalProps> = ({
|
||||
}
|
||||
}
|
||||
|
||||
const getMembers = async () => {
|
||||
const { accounts } = await fetchMembers({ url: '/workspaces/current/members', params: {} })
|
||||
if (!accounts)
|
||||
useEffect(() => {
|
||||
if (!membersData?.accounts)
|
||||
setMemberList([])
|
||||
else
|
||||
setMemberList(accounts)
|
||||
}
|
||||
|
||||
useMount(() => {
|
||||
getMembers()
|
||||
})
|
||||
setMemberList(membersData.accounts)
|
||||
}, [membersData])
|
||||
|
||||
const showMultiModalTip = useMemo(() => {
|
||||
return checkShowMultiModalTip({
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import useSWR from 'swr'
|
||||
import { basePath } from '@/utils/var'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
@@ -72,7 +71,7 @@ import type { Features as FeaturesData, FileUpload } from '@/app/components/base
|
||||
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
|
||||
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
|
||||
import NewFeaturePanel from '@/app/components/base/features/new-feature-panel'
|
||||
import { fetchFileUploadConfig } from '@/service/common'
|
||||
import { useFileUploadConfig } from '@/service/use-common'
|
||||
import {
|
||||
correctModelProvider,
|
||||
correctToolProvider,
|
||||
@@ -101,7 +100,7 @@ const Configuration: FC = () => {
|
||||
showAppConfigureFeaturesModal: state.showAppConfigureFeaturesModal,
|
||||
setShowAppConfigureFeaturesModal: state.setShowAppConfigureFeaturesModal,
|
||||
})))
|
||||
const { data: fileUploadConfigResponse } = useSWR({ url: '/files/upload' }, fetchFileUploadConfig)
|
||||
const { data: fileUploadConfigResponse } = useFileUploadConfig()
|
||||
|
||||
const latestPublishedAt = useMemo(() => appDetail?.model_config?.updated_at, [appDetail])
|
||||
const [formattingChanged, setFormattingChanged] = useState(false)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { FC } from 'react'
|
||||
import { useState } from 'react'
|
||||
import useSWR from 'swr'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import FormGeneration from '@/app/components/base/features/new-feature-panel/moderation/form-generation'
|
||||
@@ -9,7 +8,6 @@ import Button from '@/app/components/base/button'
|
||||
import EmojiPicker from '@/app/components/base/emoji-picker'
|
||||
import ApiBasedExtensionSelector from '@/app/components/header/account-setting/api-based-extension-page/selector'
|
||||
import { BookOpen01 } from '@/app/components/base/icons/src/vender/line/education'
|
||||
import { fetchCodeBasedExtensionList } from '@/service/common'
|
||||
import { SimpleSelect } from '@/app/components/base/select'
|
||||
import I18n from '@/context/i18n'
|
||||
import { LanguagesSupported } from '@/i18n-config/language'
|
||||
@@ -21,6 +19,7 @@ import { useToastContext } from '@/app/components/base/toast'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import { noop } from 'lodash-es'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import { useCodeBasedExtensions } from '@/service/use-common'
|
||||
|
||||
const systemTypes = ['api']
|
||||
type ExternalDataToolModalProps = {
|
||||
@@ -46,10 +45,7 @@ const ExternalDataToolModal: FC<ExternalDataToolModalProps> = ({
|
||||
const { locale } = useContext(I18n)
|
||||
const [localeData, setLocaleData] = useState(data.type ? data : { ...data, type: 'api' })
|
||||
const [showEmojiPicker, setShowEmojiPicker] = useState(false)
|
||||
const { data: codeBasedExtensionList } = useSWR(
|
||||
'/code-based-extension?module=external_data_tool',
|
||||
fetchCodeBasedExtensionList,
|
||||
)
|
||||
const { data: codeBasedExtensionList } = useCodeBasedExtensions('external_data_tool')
|
||||
|
||||
const providers: Provider[] = [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useSWR from 'swr'
|
||||
import { produce } from 'immer'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { RiEqualizer2Line } from '@remixicon/react'
|
||||
@@ -10,9 +9,9 @@ import Button from '@/app/components/base/button'
|
||||
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||
import type { OnFeaturesChange } from '@/app/components/base/features/types'
|
||||
import { FeatureEnum } from '@/app/components/base/features/types'
|
||||
import { fetchCodeBasedExtensionList } from '@/service/common'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import I18n from '@/context/i18n'
|
||||
import { useCodeBasedExtensions } from '@/service/use-common'
|
||||
|
||||
type Props = {
|
||||
disabled?: boolean
|
||||
@@ -28,10 +27,7 @@ const Moderation = ({
|
||||
const { locale } = useContext(I18n)
|
||||
const featuresStore = useFeaturesStore()
|
||||
const moderation = useFeatures(s => s.features.moderation)
|
||||
const { data: codeBasedExtensionList } = useSWR(
|
||||
'/code-based-extension?module=moderation',
|
||||
fetchCodeBasedExtensionList,
|
||||
)
|
||||
const { data: codeBasedExtensionList } = useCodeBasedExtensions('moderation')
|
||||
const [isHovering, setIsHovering] = useState(false)
|
||||
|
||||
const handleOpenModerationSettingModal = () => {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { ChangeEvent, FC } from 'react'
|
||||
import { useState } from 'react'
|
||||
import useSWR from 'swr'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiCloseLine } from '@remixicon/react'
|
||||
@@ -13,10 +12,6 @@ import Divider from '@/app/components/base/divider'
|
||||
import { BookOpen01 } from '@/app/components/base/icons/src/vender/line/education'
|
||||
import type { ModerationConfig, ModerationContentConfig } from '@/models/debug'
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
import {
|
||||
fetchCodeBasedExtensionList,
|
||||
fetchModelProviders,
|
||||
} from '@/service/common'
|
||||
import type { CodeBasedExtensionItem } from '@/models/common'
|
||||
import I18n from '@/context/i18n'
|
||||
import { LanguagesSupported } from '@/i18n-config/language'
|
||||
@@ -27,6 +22,7 @@ import { cn } from '@/utils/classnames'
|
||||
import { noop } from 'lodash-es'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
||||
import { useCodeBasedExtensions, useModelProviders } from '@/service/use-common'
|
||||
|
||||
const systemTypes = ['openai_moderation', 'keywords', 'api']
|
||||
|
||||
@@ -51,21 +47,18 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
|
||||
const docLink = useDocLink()
|
||||
const { notify } = useToastContext()
|
||||
const { locale } = useContext(I18n)
|
||||
const { data: modelProviders, isLoading, mutate } = useSWR('/workspaces/current/model-providers', fetchModelProviders)
|
||||
const { data: modelProviders, isPending: isLoading, refetch: refetchModelProviders } = useModelProviders()
|
||||
const [localeData, setLocaleData] = useState<ModerationConfig>(data)
|
||||
const { setShowAccountSettingModal } = useModalContext()
|
||||
const handleOpenSettingsModal = () => {
|
||||
setShowAccountSettingModal({
|
||||
payload: ACCOUNT_SETTING_TAB.PROVIDER,
|
||||
onCancelCallback: () => {
|
||||
mutate()
|
||||
refetchModelProviders()
|
||||
},
|
||||
})
|
||||
}
|
||||
const { data: codeBasedExtensionList } = useSWR(
|
||||
'/code-based-extension?module=moderation',
|
||||
fetchCodeBasedExtensionList,
|
||||
)
|
||||
const { data: codeBasedExtensionList } = useCodeBasedExtensions('moderation')
|
||||
const openaiProvider = modelProviders?.data.find(item => item.provider === 'langgenius/openai/openai')
|
||||
const systemOpenaiProviderEnabled = openaiProvider?.system_configuration.enabled
|
||||
const systemOpenaiProviderQuota = systemOpenaiProviderEnabled ? openaiProvider?.system_configuration.quota_configurations.find(item => item.quota_type === openaiProvider.system_configuration.current_quota_type) : undefined
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
'use client'
|
||||
import { useCallback, useMemo, useRef, useState } from 'react'
|
||||
import { useMount } from 'ahooks'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import PermissionSelector from '../permission-selector'
|
||||
import IndexMethod from '../index-method'
|
||||
@@ -23,7 +22,6 @@ import ModelSelector from '@/app/components/header/account-setting/model-provide
|
||||
import { useModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import type { DefaultModel } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { fetchMembers } from '@/service/common'
|
||||
import type { Member } from '@/models/common'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import type { AppIconSelection } from '@/app/components/base/app-icon-picker'
|
||||
@@ -34,6 +32,7 @@ import Toast from '@/app/components/base/toast'
|
||||
import { RiAlertFill } from '@remixicon/react'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import { useInvalidDatasetList } from '@/service/knowledge/use-dataset'
|
||||
import { useMembers } from '@/service/use-common'
|
||||
import { checkShowMultiModalTip } from '../utils'
|
||||
|
||||
const rowClass = 'flex gap-x-1'
|
||||
@@ -79,16 +78,9 @@ const Form = () => {
|
||||
)
|
||||
const { data: rerankModelList } = useModelList(ModelTypeEnum.rerank)
|
||||
const { data: embeddingModelList } = useModelList(ModelTypeEnum.textEmbedding)
|
||||
const { data: membersData } = useMembers()
|
||||
const previousAppIcon = useRef(DEFAULT_APP_ICON)
|
||||
|
||||
const getMembers = async () => {
|
||||
const { accounts } = await fetchMembers({ url: '/workspaces/current/members', params: {} })
|
||||
if (!accounts)
|
||||
setMemberList([])
|
||||
else
|
||||
setMemberList(accounts)
|
||||
}
|
||||
|
||||
const handleOpenAppIconPicker = useCallback(() => {
|
||||
setShowAppIconPicker(true)
|
||||
previousAppIcon.current = iconInfo
|
||||
@@ -119,9 +111,12 @@ const Form = () => {
|
||||
setScoreThresholdEnabled(data.score_threshold_enabled)
|
||||
}, [])
|
||||
|
||||
useMount(() => {
|
||||
getMembers()
|
||||
})
|
||||
useEffect(() => {
|
||||
if (!membersData?.accounts)
|
||||
setMemberList([])
|
||||
else
|
||||
setMemberList(membersData.accounts)
|
||||
}, [membersData])
|
||||
|
||||
const invalidDatasetList = useInvalidDatasetList()
|
||||
const handleSave = async () => {
|
||||
|
||||
@@ -5,10 +5,10 @@ import { useRouter } from 'next/navigation'
|
||||
import ExploreContext from '@/context/explore-context'
|
||||
import Sidebar from '@/app/components/explore/sidebar'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { fetchMembers } from '@/service/common'
|
||||
import type { InstalledApp } from '@/models/explore'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useDocumentTitle from '@/hooks/use-document-title'
|
||||
import { useMembers } from '@/service/use-common'
|
||||
|
||||
export type IExploreProps = {
|
||||
children: React.ReactNode
|
||||
@@ -24,18 +24,16 @@ const Explore: FC<IExploreProps> = ({
|
||||
const [installedApps, setInstalledApps] = useState<InstalledApp[]>([])
|
||||
const [isFetchingInstalledApps, setIsFetchingInstalledApps] = useState(false)
|
||||
const { t } = useTranslation()
|
||||
const { data: membersData } = useMembers()
|
||||
|
||||
useDocumentTitle(t('common.menus.explore'))
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const { accounts } = await fetchMembers({ url: '/workspaces/current/members', params: {} })
|
||||
if (!accounts)
|
||||
return
|
||||
const currUser = accounts.find(account => account.id === userProfile.id)
|
||||
setHasEditPermission(currUser?.role !== 'normal')
|
||||
})()
|
||||
}, [])
|
||||
if (!membersData?.accounts)
|
||||
return
|
||||
const currUser = membersData.accounts.find(account => account.id === userProfile.id)
|
||||
setHasEditPermission(currUser?.role !== 'normal')
|
||||
}, [membersData, userProfile.id])
|
||||
|
||||
useEffect(() => {
|
||||
if (isCurrentWorkspaceDatasetOperator)
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useSWR from 'swr'
|
||||
import Link from 'next/link'
|
||||
import s from './index.module.css'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { fetchAccountIntegrates } from '@/service/common'
|
||||
import { useAccountIntegrates } from '@/service/use-common'
|
||||
|
||||
const titleClassName = `
|
||||
mb-2 text-sm font-medium text-gray-900
|
||||
@@ -25,33 +24,38 @@ export default function IntegrationsPage() {
|
||||
},
|
||||
}
|
||||
|
||||
const { data } = useSWR({ url: '/account/integrates' }, fetchAccountIntegrates)
|
||||
const integrates = data?.data?.length ? data.data : []
|
||||
const { data } = useAccountIntegrates()
|
||||
const integrates = data?.data ?? []
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='mb-8'>
|
||||
<div className={titleClassName}>{t('common.integrations.connected')}</div>
|
||||
{
|
||||
integrates.map(integrate => (
|
||||
<div key={integrate.provider} className='mb-2 flex items-center rounded-lg border-[0.5px] border-gray-200 bg-gray-50 px-3 py-2'>
|
||||
<div className={cn('mr-3 h-8 w-8 rounded-lg border border-gray-100 bg-white', s[`${integrate.provider}-icon`])} />
|
||||
<div className='grow'>
|
||||
<div className='text-sm font-medium leading-[21px] text-gray-800'>{integrateMap[integrate.provider].name}</div>
|
||||
<div className='text-xs font-normal leading-[18px] text-gray-500'>{integrateMap[integrate.provider].description}</div>
|
||||
integrates.map((integrate) => {
|
||||
const info = integrateMap[integrate.provider]
|
||||
if (!info)
|
||||
return null
|
||||
return (
|
||||
<div key={integrate.provider} className='mb-2 flex items-center rounded-lg border-[0.5px] border-gray-200 bg-gray-50 px-3 py-2'>
|
||||
<div className={cn('mr-3 h-8 w-8 rounded-lg border border-gray-100 bg-white', s[`${integrate.provider}-icon`])} />
|
||||
<div className='grow'>
|
||||
<div className='text-sm font-medium leading-[21px] text-gray-800'>{info.name}</div>
|
||||
<div className='text-xs font-normal leading-[18px] text-gray-500'>{info.description}</div>
|
||||
</div>
|
||||
{
|
||||
!integrate.is_bound && (
|
||||
<Link
|
||||
className='flex h-8 cursor-pointer items-center rounded-lg border border-gray-200 bg-white px-[7px] text-xs font-medium text-gray-700'
|
||||
href={integrate.link}
|
||||
target='_blank' rel='noopener noreferrer'>
|
||||
{t('common.integrations.connect')}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{
|
||||
!integrate.is_bound && (
|
||||
<Link
|
||||
className='flex h-8 cursor-pointer items-center rounded-lg border border-gray-200 bg-white px-[7px] text-xs font-medium text-gray-700'
|
||||
href={integrate.link}
|
||||
target='_blank' rel='noopener noreferrer'>
|
||||
{t('common.integrations.connect')}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
))
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
{/* <div className='mb-8'>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useSWR from 'swr'
|
||||
import {
|
||||
RiAddLine,
|
||||
} from '@remixicon/react'
|
||||
@@ -7,15 +6,12 @@ import Item from './item'
|
||||
import Empty from './empty'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { fetchApiBasedExtensionList } from '@/service/common'
|
||||
import { useApiBasedExtensions } from '@/service/use-common'
|
||||
|
||||
const ApiBasedExtensionPage = () => {
|
||||
const { t } = useTranslation()
|
||||
const { setShowApiBasedExtensionModal } = useModalContext()
|
||||
const { data, mutate, isLoading } = useSWR(
|
||||
'/api-based-extension',
|
||||
fetchApiBasedExtensionList,
|
||||
)
|
||||
const { data, refetch: mutate, isPending: isLoading } = useApiBasedExtensions()
|
||||
|
||||
const handleOpenApiBasedExtensionModal = () => {
|
||||
setShowApiBasedExtensionModal({
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { FC } from 'react'
|
||||
import { useState } from 'react'
|
||||
import useSWR from 'swr'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiAddLine,
|
||||
@@ -15,8 +14,8 @@ import {
|
||||
ArrowUpRight,
|
||||
} from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { fetchApiBasedExtensionList } from '@/service/common'
|
||||
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
|
||||
import { useApiBasedExtensions } from '@/service/use-common'
|
||||
|
||||
type ApiBasedExtensionSelectorProps = {
|
||||
value: string
|
||||
@@ -33,10 +32,7 @@ const ApiBasedExtensionSelector: FC<ApiBasedExtensionSelectorProps> = ({
|
||||
setShowAccountSettingModal,
|
||||
setShowApiBasedExtensionModal,
|
||||
} = useModalContext()
|
||||
const { data, mutate } = useSWR(
|
||||
'/api-based-extension',
|
||||
fetchApiBasedExtensionList,
|
||||
)
|
||||
const { data, refetch: mutate } = useApiBasedExtensions()
|
||||
const handleSelect = (id: string) => {
|
||||
onChange(id)
|
||||
setOpen(false)
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import useSWR from 'swr'
|
||||
import Panel from '../panel'
|
||||
import { DataSourceType } from '../panel/types'
|
||||
import type { DataSourceNotion as TDataSourceNotion } from '@/models/common'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { fetchNotionConnection } from '@/service/common'
|
||||
import NotionIcon from '@/app/components/base/notion-icon'
|
||||
import { noop } from 'lodash-es'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { useDataSourceIntegrates, useNotionConnection } from '@/service/use-common'
|
||||
|
||||
const Icon: FC<{
|
||||
src: string
|
||||
@@ -26,7 +25,7 @@ const Icon: FC<{
|
||||
)
|
||||
}
|
||||
type Props = {
|
||||
workspaces: TDataSourceNotion[]
|
||||
workspaces?: TDataSourceNotion[]
|
||||
}
|
||||
|
||||
const DataSourceNotion: FC<Props> = ({
|
||||
@@ -34,10 +33,14 @@ const DataSourceNotion: FC<Props> = ({
|
||||
}) => {
|
||||
const { isCurrentWorkspaceManager } = useAppContext()
|
||||
const [canConnectNotion, setCanConnectNotion] = useState(false)
|
||||
const { data } = useSWR(canConnectNotion ? '/oauth/data-source/notion' : null, fetchNotionConnection)
|
||||
const { data: integrates } = useDataSourceIntegrates({
|
||||
initialData: workspaces ? { data: workspaces } : undefined,
|
||||
})
|
||||
const { data } = useNotionConnection(canConnectNotion)
|
||||
const { t } = useTranslation()
|
||||
|
||||
const connected = !!workspaces.length
|
||||
const resolvedWorkspaces = integrates?.data ?? []
|
||||
const connected = !!resolvedWorkspaces.length
|
||||
|
||||
const handleConnectNotion = () => {
|
||||
if (!isCurrentWorkspaceManager)
|
||||
@@ -74,7 +77,7 @@ const DataSourceNotion: FC<Props> = ({
|
||||
onConfigure={handleConnectNotion}
|
||||
readOnly={!isCurrentWorkspaceManager}
|
||||
isSupportList
|
||||
configuredList={workspaces.map(workspace => ({
|
||||
configuredList={resolvedWorkspaces.map(workspace => ({
|
||||
id: workspace.id,
|
||||
logo: ({ className }: { className: string }) => (
|
||||
<Icon
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
'use client'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Fragment } from 'react'
|
||||
import { useSWRConfig } from 'swr'
|
||||
import {
|
||||
RiDeleteBinLine,
|
||||
RiLoopLeftLine,
|
||||
@@ -10,6 +9,7 @@ import {
|
||||
} from '@remixicon/react'
|
||||
import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'
|
||||
import { syncDataSourceNotion, updateDataSourceNotionAction } from '@/service/common'
|
||||
import { useInvalidDataSourceIntegrates } from '@/service/use-common'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
@@ -25,14 +25,14 @@ export default function Operate({
|
||||
onAuthAgain,
|
||||
}: OperateProps) {
|
||||
const { t } = useTranslation()
|
||||
const { mutate } = useSWRConfig()
|
||||
const invalidateDataSourceIntegrates = useInvalidDataSourceIntegrates()
|
||||
|
||||
const updateIntegrates = () => {
|
||||
Toast.notify({
|
||||
type: 'success',
|
||||
message: t('common.api.success'),
|
||||
})
|
||||
mutate({ url: 'data-source/integrates' })
|
||||
invalidateDataSourceIntegrates()
|
||||
}
|
||||
const handleSync = async () => {
|
||||
await syncDataSourceNotion({ url: `/oauth/data-source/notion/${payload.id}/sync` })
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
'use client'
|
||||
import { useState } from 'react'
|
||||
import useSWR from 'swr'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { RiUserAddLine } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@@ -10,7 +9,6 @@ import EditWorkspaceModal from './edit-workspace-modal'
|
||||
import TransferOwnershipModal from './transfer-ownership-modal'
|
||||
import Operation from './operation'
|
||||
import TransferOwnership from './operation/transfer-ownership'
|
||||
import { fetchMembers } from '@/service/common'
|
||||
import I18n from '@/context/i18n'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import Avatar from '@/app/components/base/avatar'
|
||||
@@ -26,6 +24,7 @@ import Tooltip from '@/app/components/base/tooltip'
|
||||
import { RiPencilLine } from '@remixicon/react'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
|
||||
import { useMembers } from '@/service/use-common'
|
||||
|
||||
const MembersPage = () => {
|
||||
const { t } = useTranslation()
|
||||
@@ -39,13 +38,7 @@ const MembersPage = () => {
|
||||
const { locale } = useContext(I18n)
|
||||
|
||||
const { userProfile, currentWorkspace, isCurrentWorkspaceOwner, isCurrentWorkspaceManager } = useAppContext()
|
||||
const { data, mutate } = useSWR(
|
||||
{
|
||||
url: '/workspaces/current/members',
|
||||
params: {},
|
||||
},
|
||||
fetchMembers,
|
||||
)
|
||||
const { data, refetch } = useMembers()
|
||||
const { systemFeatures } = useGlobalPublicStore()
|
||||
const { formatTimeFromNow } = useFormatTimeFromNow()
|
||||
const [inviteModalVisible, setInviteModalVisible] = useState(false)
|
||||
@@ -140,7 +133,7 @@ const MembersPage = () => {
|
||||
<div className='system-sm-regular px-3 text-text-secondary'>{RoleMap[account.role] || RoleMap.normal}</div>
|
||||
)}
|
||||
{isCurrentWorkspaceOwner && account.role !== 'owner' && (
|
||||
<Operation member={account} operatorRole={currentWorkspace.role} onOperate={mutate} />
|
||||
<Operation member={account} operatorRole={currentWorkspace.role} onOperate={refetch} />
|
||||
)}
|
||||
{!isCurrentWorkspaceOwner && (
|
||||
<div className='system-sm-regular px-3 text-text-secondary'>{RoleMap[account.role] || RoleMap.normal}</div>
|
||||
@@ -160,7 +153,7 @@ const MembersPage = () => {
|
||||
onSend={(invitationResults) => {
|
||||
setInvitedModalVisible(true)
|
||||
setInvitationResults(invitationResults)
|
||||
mutate()
|
||||
refetch()
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
import type { FC } from 'react'
|
||||
import React, { useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useSWR from 'swr'
|
||||
import {
|
||||
RiArrowDownSLine,
|
||||
} from '@remixicon/react'
|
||||
import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem'
|
||||
import Avatar from '@/app/components/base/avatar'
|
||||
import Input from '@/app/components/base/input'
|
||||
import { fetchMembers } from '@/service/common'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { useMembers } from '@/service/use-common'
|
||||
|
||||
type Props = {
|
||||
value?: any
|
||||
@@ -27,13 +26,7 @@ const MemberSelector: FC<Props> = ({
|
||||
const [open, setOpen] = useState(false)
|
||||
const [searchValue, setSearchValue] = useState('')
|
||||
|
||||
const { data } = useSWR(
|
||||
{
|
||||
url: '/workspaces/current/members',
|
||||
params: {},
|
||||
},
|
||||
fetchMembers,
|
||||
)
|
||||
const { data } = useMembers()
|
||||
|
||||
const currentValue = useMemo(() => {
|
||||
if (!data?.accounts) return null
|
||||
|
||||
@@ -3,15 +3,21 @@ import { useLanguage } from './hooks'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { after } from 'node:test'
|
||||
|
||||
jest.mock('swr', () => ({
|
||||
__esModule: true,
|
||||
default: jest.fn(), // mock useSWR
|
||||
useSWRConfig: jest.fn(),
|
||||
jest.mock('@tanstack/react-query', () => ({
|
||||
useQuery: jest.fn(),
|
||||
useQueryClient: jest.fn(() => ({
|
||||
invalidateQueries: jest.fn(),
|
||||
})),
|
||||
}))
|
||||
|
||||
// mock use-context-selector
|
||||
jest.mock('use-context-selector', () => ({
|
||||
useContext: jest.fn(),
|
||||
createContext: () => ({
|
||||
Provider: ({ children }: any) => children,
|
||||
Consumer: ({ children }: any) => children(null),
|
||||
}),
|
||||
useContextSelector: jest.fn(),
|
||||
}))
|
||||
|
||||
// mock service/common functions
|
||||
@@ -19,10 +25,15 @@ jest.mock('@/service/common', () => ({
|
||||
fetchDefaultModal: jest.fn(),
|
||||
fetchModelList: jest.fn(),
|
||||
fetchModelProviderCredentials: jest.fn(),
|
||||
fetchModelProviders: jest.fn(),
|
||||
getPayUrl: jest.fn(),
|
||||
}))
|
||||
|
||||
jest.mock('@/service/use-common', () => ({
|
||||
commonQueryKeys: {
|
||||
modelProviders: ['common', 'model-providers'],
|
||||
},
|
||||
}))
|
||||
|
||||
// mock context hooks
|
||||
jest.mock('@/context/i18n', () => ({
|
||||
__esModule: true,
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import useSWR, { useSWRConfig } from 'swr'
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import type {
|
||||
Credential,
|
||||
@@ -27,9 +27,9 @@ import {
|
||||
fetchDefaultModal,
|
||||
fetchModelList,
|
||||
fetchModelProviderCredentials,
|
||||
fetchModelProviders,
|
||||
getPayUrl,
|
||||
} from '@/service/common'
|
||||
import { commonQueryKeys } from '@/service/use-common'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import {
|
||||
useMarketplacePlugins,
|
||||
@@ -81,17 +81,23 @@ export const useProviderCredentialsAndLoadBalancing = (
|
||||
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields,
|
||||
credentialId?: string,
|
||||
) => {
|
||||
const { data: predefinedFormSchemasValue, mutate: mutatePredefined, isLoading: isPredefinedLoading } = useSWR(
|
||||
(configurationMethod === ConfigurationMethodEnum.predefinedModel && configured && credentialId)
|
||||
? `/workspaces/current/model-providers/${provider}/credentials${credentialId ? `?credential_id=${credentialId}` : ''}`
|
||||
: null,
|
||||
fetchModelProviderCredentials,
|
||||
const queryClient = useQueryClient()
|
||||
const predefinedEnabled = configurationMethod === ConfigurationMethodEnum.predefinedModel && configured && !!credentialId
|
||||
const customEnabled = configurationMethod === ConfigurationMethodEnum.customizableModel && !!currentCustomConfigurationModelFixedFields && !!credentialId
|
||||
|
||||
const { data: predefinedFormSchemasValue, isPending: isPredefinedLoading } = useQuery(
|
||||
{
|
||||
queryKey: ['model-providers', 'credentials', provider, credentialId],
|
||||
queryFn: () => fetchModelProviderCredentials(`/workspaces/current/model-providers/${provider}/credentials${credentialId ? `?credential_id=${credentialId}` : ''}`),
|
||||
enabled: predefinedEnabled,
|
||||
},
|
||||
)
|
||||
const { data: customFormSchemasValue, mutate: mutateCustomized, isLoading: isCustomizedLoading } = useSWR(
|
||||
(configurationMethod === ConfigurationMethodEnum.customizableModel && currentCustomConfigurationModelFixedFields && credentialId)
|
||||
? `/workspaces/current/model-providers/${provider}/models/credentials?model=${currentCustomConfigurationModelFixedFields?.__model_name}&model_type=${currentCustomConfigurationModelFixedFields?.__model_type}${credentialId ? `&credential_id=${credentialId}` : ''}`
|
||||
: null,
|
||||
fetchModelProviderCredentials,
|
||||
const { data: customFormSchemasValue, isPending: isCustomizedLoading } = useQuery(
|
||||
{
|
||||
queryKey: ['model-providers', 'models', 'credentials', provider, currentCustomConfigurationModelFixedFields?.__model_type, currentCustomConfigurationModelFixedFields?.__model_name, credentialId],
|
||||
queryFn: () => fetchModelProviderCredentials(`/workspaces/current/model-providers/${provider}/models/credentials?model=${currentCustomConfigurationModelFixedFields?.__model_name}&model_type=${currentCustomConfigurationModelFixedFields?.__model_type}${credentialId ? `&credential_id=${credentialId}` : ''}`),
|
||||
enabled: customEnabled,
|
||||
},
|
||||
)
|
||||
|
||||
const credentials = useMemo(() => {
|
||||
@@ -112,9 +118,11 @@ export const useProviderCredentialsAndLoadBalancing = (
|
||||
])
|
||||
|
||||
const mutate = useMemo(() => () => {
|
||||
mutatePredefined()
|
||||
mutateCustomized()
|
||||
}, [mutateCustomized, mutatePredefined])
|
||||
if (predefinedEnabled)
|
||||
queryClient.invalidateQueries({ queryKey: ['model-providers', 'credentials', provider, credentialId] })
|
||||
if (customEnabled)
|
||||
queryClient.invalidateQueries({ queryKey: ['model-providers', 'models', 'credentials', provider, currentCustomConfigurationModelFixedFields?.__model_type, currentCustomConfigurationModelFixedFields?.__model_name, credentialId] })
|
||||
}, [customEnabled, credentialId, currentCustomConfigurationModelFixedFields?.__model_name, currentCustomConfigurationModelFixedFields?.__model_type, predefinedEnabled, provider, queryClient])
|
||||
|
||||
return {
|
||||
credentials,
|
||||
@@ -129,22 +137,28 @@ export const useProviderCredentialsAndLoadBalancing = (
|
||||
}
|
||||
|
||||
export const useModelList = (type: ModelTypeEnum) => {
|
||||
const { data, mutate, isLoading } = useSWR(`/workspaces/current/models/model-types/${type}`, fetchModelList)
|
||||
const { data, refetch, isPending } = useQuery({
|
||||
queryKey: commonQueryKeys.modelList(type),
|
||||
queryFn: () => fetchModelList(`/workspaces/current/models/model-types/${type}`),
|
||||
})
|
||||
|
||||
return {
|
||||
data: data?.data || [],
|
||||
mutate,
|
||||
isLoading,
|
||||
mutate: refetch,
|
||||
isLoading: isPending,
|
||||
}
|
||||
}
|
||||
|
||||
export const useDefaultModel = (type: ModelTypeEnum) => {
|
||||
const { data, mutate, isLoading } = useSWR(`/workspaces/current/default-model?model_type=${type}`, fetchDefaultModal)
|
||||
const { data, refetch, isPending } = useQuery({
|
||||
queryKey: commonQueryKeys.defaultModel(type),
|
||||
queryFn: () => fetchDefaultModal(`/workspaces/current/default-model?model_type=${type}`),
|
||||
})
|
||||
|
||||
return {
|
||||
data: data?.data,
|
||||
mutate,
|
||||
isLoading,
|
||||
mutate: refetch,
|
||||
isLoading: isPending,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,11 +214,11 @@ export const useModelListAndDefaultModelAndCurrentProviderAndModel = (type: Mode
|
||||
}
|
||||
|
||||
export const useUpdateModelList = () => {
|
||||
const { mutate } = useSWRConfig()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const updateModelList = useCallback((type: ModelTypeEnum) => {
|
||||
mutate(`/workspaces/current/models/model-types/${type}`)
|
||||
}, [mutate])
|
||||
queryClient.invalidateQueries({ queryKey: commonQueryKeys.modelList(type) })
|
||||
}, [queryClient])
|
||||
|
||||
return updateModelList
|
||||
}
|
||||
@@ -230,22 +244,12 @@ export const useAnthropicBuyQuota = () => {
|
||||
return handleGetPayUrl
|
||||
}
|
||||
|
||||
export const useModelProviders = () => {
|
||||
const { data: providersData, mutate, isLoading } = useSWR('/workspaces/current/model-providers', fetchModelProviders)
|
||||
|
||||
return {
|
||||
data: providersData?.data || [],
|
||||
mutate,
|
||||
isLoading,
|
||||
}
|
||||
}
|
||||
|
||||
export const useUpdateModelProviders = () => {
|
||||
const { mutate } = useSWRConfig()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const updateModelProviders = useCallback(() => {
|
||||
mutate('/workspaces/current/model-providers')
|
||||
}, [mutate])
|
||||
queryClient.invalidateQueries({ queryKey: commonQueryKeys.modelProviders })
|
||||
}, [queryClient])
|
||||
|
||||
return updateModelProviders
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import type {
|
||||
ReactNode,
|
||||
} from 'react'
|
||||
import { useMemo, useState } from 'react'
|
||||
import useSWR from 'swr'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type {
|
||||
DefaultModel,
|
||||
@@ -26,11 +25,11 @@ import {
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import { fetchModelParameterRules } from '@/service/common'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { PROVIDER_WITH_PRESET_TONE, STOP_PARAMETER_RULE, TONE_LIST } from '@/config'
|
||||
import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
import { useModelParameterRules } from '@/service/use-common'
|
||||
|
||||
export type ModelParameterModalProps = {
|
||||
popupClassName?: string
|
||||
@@ -69,7 +68,7 @@ const ModelParameterModal: FC<ModelParameterModalProps> = ({
|
||||
const { t } = useTranslation()
|
||||
const { isAPIKeySet } = useProviderContext()
|
||||
const [open, setOpen] = useState(false)
|
||||
const { data: parameterRulesData, isLoading } = useSWR((provider && modelId) ? `/workspaces/current/model-providers/${provider}/models/parameter-rules?model=${modelId}` : null, fetchModelParameterRules)
|
||||
const { data: parameterRulesData, isPending: isLoading } = useModelParameterRules(provider, modelId)
|
||||
const {
|
||||
currentProvider,
|
||||
currentModel,
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { ModelItem, ModelProvider } from '../declarations'
|
||||
import { ModelStatusEnum } from '../declarations'
|
||||
import ModelIcon from '../model-icon'
|
||||
import ModelName from '../model-name'
|
||||
import { useUpdateModelList } from '../hooks'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { Balance } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
@@ -20,21 +21,25 @@ export type ModelListItemProps = {
|
||||
model: ModelItem
|
||||
provider: ModelProvider
|
||||
isConfigurable: boolean
|
||||
onChange?: (provider: string) => void
|
||||
onModifyLoadBalancing?: (model: ModelItem) => void
|
||||
}
|
||||
|
||||
const ModelListItem = ({ model, provider, isConfigurable, onModifyLoadBalancing }: ModelListItemProps) => {
|
||||
const ModelListItem = ({ model, provider, isConfigurable, onChange, onModifyLoadBalancing }: ModelListItemProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { plan } = useProviderContext()
|
||||
const modelLoadBalancingEnabled = useProviderContextSelector(state => state.modelLoadBalancingEnabled)
|
||||
const { isCurrentWorkspaceManager } = useAppContext()
|
||||
const updateModelList = useUpdateModelList()
|
||||
|
||||
const toggleModelEnablingStatus = useCallback(async (enabled: boolean) => {
|
||||
if (enabled)
|
||||
await enableModel(`/workspaces/current/model-providers/${provider.provider}/models/enable`, { model: model.model, model_type: model.model_type })
|
||||
else
|
||||
await disableModel(`/workspaces/current/model-providers/${provider.provider}/models/disable`, { model: model.model, model_type: model.model_type })
|
||||
}, [model.model, model.model_type, provider.provider])
|
||||
updateModelList(model.model_type)
|
||||
onChange?.(provider.provider)
|
||||
}, [model.model, model.model_type, onChange, provider.provider, updateModelList])
|
||||
|
||||
const { run: debouncedToggleModelEnablingStatus } = useDebounceFn(toggleModelEnablingStatus, { wait: 500 })
|
||||
|
||||
|
||||
@@ -91,6 +91,7 @@ const ModelList: FC<ModelListProps> = ({
|
||||
model,
|
||||
provider,
|
||||
isConfigurable,
|
||||
onChange,
|
||||
onModifyLoadBalancing,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import useSWR from 'swr'
|
||||
import { LockClosedIcon } from '@heroicons/react/24/solid'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Link from 'next/link'
|
||||
import SerpapiPlugin from './SerpapiPlugin'
|
||||
import { fetchPluginProviders } from '@/service/common'
|
||||
import type { PluginProvider } from '@/models/common'
|
||||
import { usePluginProviders } from '@/service/use-common'
|
||||
|
||||
const PluginPage = () => {
|
||||
const { t } = useTranslation()
|
||||
const { data: plugins, mutate } = useSWR('/workspaces/current/tool-providers', fetchPluginProviders)
|
||||
const { data: plugins, refetch: mutate } = usePluginProviders()
|
||||
|
||||
const Plugin_MAP: Record<string, (plugin: PluginProvider) => React.JSX.Element> = {
|
||||
serpapi: (plugin: PluginProvider) => <SerpapiPlugin key='serpapi' plugin={plugin} onUpdate={() => mutate()} />,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import useSWR from 'swr'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import PresetsParameter from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter'
|
||||
import ParameterItem from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item'
|
||||
@@ -9,9 +8,9 @@ import type {
|
||||
ModelParameterRule,
|
||||
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import type { ParameterValue } from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item'
|
||||
import { fetchModelParameterRules } from '@/service/common'
|
||||
import { PROVIDER_WITH_PRESET_TONE, STOP_PARAMETER_RULE, TONE_LIST } from '@/config'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { useModelParameterRules } from '@/service/use-common'
|
||||
|
||||
type Props = {
|
||||
isAdvancedMode: boolean
|
||||
@@ -29,11 +28,7 @@ const LLMParamsPanel = ({
|
||||
onCompletionParamsChange,
|
||||
}: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const { data: parameterRulesData, isLoading } = useSWR(
|
||||
(provider && modelId)
|
||||
? `/workspaces/current/model-providers/${provider}/models/parameter-rules?model=${modelId}`
|
||||
: null, fetchModelParameterRules,
|
||||
)
|
||||
const { data: parameterRulesData, isPending: isLoading } = useModelParameterRules(provider, modelId)
|
||||
|
||||
const parameterRules: ModelParameterRule[] = useMemo(() => {
|
||||
return parameterRulesData?.data || []
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import useSWR from 'swr'
|
||||
import { produce } from 'immer'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type { UploadFileSetting } from '../../../types'
|
||||
@@ -11,9 +10,9 @@ import FileTypeItem from './file-type-item'
|
||||
import InputNumberWithSlider from './input-number-with-slider'
|
||||
import Field from '@/app/components/app/configuration/config-var/config-modal/field'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
import { fetchFileUploadConfig } from '@/service/common'
|
||||
import { useFileSizeLimit } from '@/app/components/base/file-uploader/hooks'
|
||||
import { formatFileSize } from '@/utils/format'
|
||||
import { useFileUploadConfig } from '@/service/use-common'
|
||||
|
||||
type Props = {
|
||||
payload: UploadFileSetting
|
||||
@@ -38,7 +37,7 @@ const FileUploadSetting: FC<Props> = ({
|
||||
allowed_file_types,
|
||||
allowed_file_extensions,
|
||||
} = payload
|
||||
const { data: fileUploadConfigResponse } = useSWR({ url: '/files/upload' }, fetchFileUploadConfig)
|
||||
const { data: fileUploadConfigResponse } = useFileUploadConfig()
|
||||
const {
|
||||
imgSizeLimit,
|
||||
docSizeLimit,
|
||||
|
||||
@@ -1,30 +1,28 @@
|
||||
'use client'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useSWR from 'swr'
|
||||
import { useSearchParams } from 'next/navigation'
|
||||
import { basePath } from '@/utils/var'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { CheckCircleIcon } from '@heroicons/react/24/solid'
|
||||
import Input from '../components/base/input'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { changePasswordWithToken, verifyForgotPasswordToken } from '@/service/common'
|
||||
import { changePasswordWithToken } from '@/service/common'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import { validPassword } from '@/config'
|
||||
import { useVerifyForgotPasswordToken } from '@/service/use-common'
|
||||
|
||||
const ChangePasswordForm = () => {
|
||||
const { t } = useTranslation()
|
||||
const searchParams = useSearchParams()
|
||||
const token = searchParams.get('token')
|
||||
const isTokenMissing = !token
|
||||
|
||||
const verifyTokenParams = {
|
||||
url: '/forgot-password/validity',
|
||||
body: { token },
|
||||
}
|
||||
const { data: verifyTokenRes, mutate: revalidateToken } = useSWR(verifyTokenParams, verifyForgotPasswordToken, {
|
||||
revalidateOnFocus: false,
|
||||
})
|
||||
const {
|
||||
data: verifyTokenRes,
|
||||
refetch: revalidateToken,
|
||||
} = useVerifyForgotPasswordToken(token)
|
||||
|
||||
const [password, setPassword] = useState('')
|
||||
const [confirmPassword, setConfirmPassword] = useState('')
|
||||
@@ -82,8 +80,8 @@ const ChangePasswordForm = () => {
|
||||
'md:px-[108px]',
|
||||
)
|
||||
}>
|
||||
{!verifyTokenRes && <Loading />}
|
||||
{verifyTokenRes && !verifyTokenRes.is_valid && (
|
||||
{!isTokenMissing && !verifyTokenRes && <Loading />}
|
||||
{(isTokenMissing || (verifyTokenRes && !verifyTokenRes.is_valid)) && (
|
||||
<div className="flex flex-col md:w-[400px]">
|
||||
<div className="mx-auto w-full">
|
||||
<div className="mb-3 flex h-20 w-20 items-center justify-center rounded-[20px] border border-divider-regular bg-components-option-card-option-bg p-5 text-[40px] font-bold shadow-lg">🤷♂️</div>
|
||||
|
||||
@@ -5,7 +5,6 @@ import { useCallback, useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import useSWR from 'swr'
|
||||
import { RiAccountCircleLine } from '@remixicon/react'
|
||||
import Input from '@/app/components/base/input'
|
||||
import { SimpleSelect } from '@/app/components/base/select'
|
||||
@@ -13,12 +12,13 @@ import Button from '@/app/components/base/button'
|
||||
import { timezones } from '@/utils/timezone'
|
||||
import { LanguagesSupported, languages } from '@/i18n-config/language'
|
||||
import I18n from '@/context/i18n'
|
||||
import { activateMember, invitationCheck } from '@/service/common'
|
||||
import { activateMember } from '@/service/common'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { noop } from 'lodash-es'
|
||||
import { useGlobalPublicStore } from '@/context/global-public-context'
|
||||
import { resolvePostLoginRedirect } from '../utils/post-login-redirect'
|
||||
import { useInvitationCheck } from '@/service/use-common'
|
||||
|
||||
export default function InviteSettingsPage() {
|
||||
const { t } = useTranslation()
|
||||
@@ -38,9 +38,7 @@ export default function InviteSettingsPage() {
|
||||
token,
|
||||
},
|
||||
}
|
||||
const { data: checkRes, mutate: recheck } = useSWR(checkParams, invitationCheck, {
|
||||
revalidateOnFocus: false,
|
||||
})
|
||||
const { data: checkRes, refetch: recheck } = useInvitationCheck(checkParams.params, !!token)
|
||||
|
||||
const handleActivate = useCallback(async () => {
|
||||
try {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
'use client'
|
||||
import React, { type Reducer, useEffect, useReducer } from 'react'
|
||||
import React, { type Reducer, useReducer } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Link from 'next/link'
|
||||
import useSWR from 'swr'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import Input from '../components/base/input'
|
||||
import Button from '@/app/components/base/button'
|
||||
@@ -10,12 +9,11 @@ import Tooltip from '@/app/components/base/tooltip'
|
||||
import { SimpleSelect } from '@/app/components/base/select'
|
||||
import { timezones } from '@/utils/timezone'
|
||||
import { LanguagesSupported, languages } from '@/i18n-config/language'
|
||||
import { oneMoreStep } from '@/service/common'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import { useOneMoreStep } from '@/service/use-common'
|
||||
|
||||
type IState = {
|
||||
formState: 'processing' | 'error' | 'success' | 'initial'
|
||||
invitation_code: string
|
||||
interface_language: string
|
||||
timezone: string
|
||||
@@ -26,7 +24,6 @@ type IAction
|
||||
| { type: 'invitation_code', value: string }
|
||||
| { type: 'interface_language', value: string }
|
||||
| { type: 'timezone', value: string }
|
||||
| { type: 'formState', value: 'processing' }
|
||||
|
||||
const reducer: Reducer<IState, IAction> = (state: IState, action: IAction) => {
|
||||
switch (action.type) {
|
||||
@@ -36,11 +33,8 @@ const reducer: Reducer<IState, IAction> = (state: IState, action: IAction) => {
|
||||
return { ...state, interface_language: action.value }
|
||||
case 'timezone':
|
||||
return { ...state, timezone: action.value }
|
||||
case 'formState':
|
||||
return { ...state, formState: action.value }
|
||||
case 'failed':
|
||||
return {
|
||||
formState: 'initial',
|
||||
invitation_code: '',
|
||||
interface_language: 'en-US',
|
||||
timezone: 'Asia/Shanghai',
|
||||
@@ -57,30 +51,29 @@ const OneMoreStep = () => {
|
||||
const searchParams = useSearchParams()
|
||||
|
||||
const [state, dispatch] = useReducer(reducer, {
|
||||
formState: 'initial',
|
||||
invitation_code: searchParams.get('invitation_code') || '',
|
||||
interface_language: 'en-US',
|
||||
timezone: 'Asia/Shanghai',
|
||||
})
|
||||
const { data, error } = useSWR(state.formState === 'processing'
|
||||
? {
|
||||
url: '/account/init',
|
||||
body: {
|
||||
const { mutateAsync: submitOneMoreStep, isPending } = useOneMoreStep()
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (isPending)
|
||||
return
|
||||
try {
|
||||
await submitOneMoreStep({
|
||||
invitation_code: state.invitation_code,
|
||||
interface_language: state.interface_language,
|
||||
timezone: state.timezone,
|
||||
},
|
||||
})
|
||||
router.push('/apps')
|
||||
}
|
||||
: null, oneMoreStep)
|
||||
|
||||
useEffect(() => {
|
||||
if (error && error.status === 400) {
|
||||
Toast.notify({ type: 'error', message: t('login.invalidInvitationCode') })
|
||||
catch (error: any) {
|
||||
if (error && error.status === 400)
|
||||
Toast.notify({ type: 'error', message: t('login.invalidInvitationCode') })
|
||||
dispatch({ type: 'failed', payload: null })
|
||||
}
|
||||
if (data)
|
||||
router.push('/apps')
|
||||
}, [data, error])
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -151,10 +144,8 @@ const OneMoreStep = () => {
|
||||
<Button
|
||||
variant='primary'
|
||||
className='w-full'
|
||||
disabled={state.formState === 'processing'}
|
||||
onClick={() => {
|
||||
dispatch({ type: 'formState', value: 'processing' })
|
||||
}}
|
||||
disabled={isPending}
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
{t('login.go')}
|
||||
</Button>
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
'use client'
|
||||
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import useSWR from 'swr'
|
||||
import { useCallback, useEffect, useMemo } from 'react'
|
||||
import { createContext, useContext, useContextSelector } from 'use-context-selector'
|
||||
import type { FC, ReactNode } from 'react'
|
||||
import { fetchCurrentWorkspace, fetchLangGeniusVersion, fetchUserProfile } from '@/service/common'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import {
|
||||
useCurrentWorkspace,
|
||||
useLangGeniusVersion,
|
||||
useUserProfile,
|
||||
} from '@/service/use-common'
|
||||
import type { ICurrentWorkspace, LangGeniusVersionResponse, UserProfileResponse } from '@/models/common'
|
||||
import MaintenanceNotice from '@/app/components/header/maintenance-notice'
|
||||
import { noop } from 'lodash-es'
|
||||
@@ -79,48 +83,44 @@ export type AppContextProviderProps = {
|
||||
}
|
||||
|
||||
export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) => {
|
||||
const queryClient = useQueryClient()
|
||||
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
|
||||
const { data: userProfileResponse, mutate: mutateUserProfile, error: userProfileError } = useSWR({ url: '/account/profile', params: {} }, fetchUserProfile)
|
||||
const { data: currentWorkspaceResponse, mutate: mutateCurrentWorkspace, isLoading: isLoadingCurrentWorkspace } = useSWR({ url: '/workspaces/current', params: {} }, fetchCurrentWorkspace)
|
||||
const { data: userProfileResp } = useUserProfile()
|
||||
const { data: currentWorkspaceResp, isPending: isLoadingCurrentWorkspace } = useCurrentWorkspace()
|
||||
const langGeniusVersionQuery = useLangGeniusVersion(
|
||||
userProfileResp?.meta.currentVersion,
|
||||
!systemFeatures.branding.enabled,
|
||||
)
|
||||
|
||||
const userProfile = useMemo<UserProfileResponse>(() => userProfileResp?.profile || userProfilePlaceholder, [userProfileResp?.profile])
|
||||
const currentWorkspace = useMemo<ICurrentWorkspace>(() => currentWorkspaceResp || initialWorkspaceInfo, [currentWorkspaceResp])
|
||||
const langGeniusVersionInfo = useMemo<LangGeniusVersionResponse>(() => {
|
||||
if (!userProfileResp?.meta?.currentVersion || !langGeniusVersionQuery.data)
|
||||
return initialLangGeniusVersionInfo
|
||||
|
||||
const current_version = userProfileResp.meta.currentVersion
|
||||
const current_env = userProfileResp.meta.currentEnv || ''
|
||||
const versionData = langGeniusVersionQuery.data
|
||||
return {
|
||||
...versionData,
|
||||
current_version,
|
||||
latest_version: versionData.version,
|
||||
current_env,
|
||||
}
|
||||
}, [langGeniusVersionQuery.data, userProfileResp?.meta])
|
||||
|
||||
const [userProfile, setUserProfile] = useState<UserProfileResponse>(userProfilePlaceholder)
|
||||
const [langGeniusVersionInfo, setLangGeniusVersionInfo] = useState<LangGeniusVersionResponse>(initialLangGeniusVersionInfo)
|
||||
const [currentWorkspace, setCurrentWorkspace] = useState<ICurrentWorkspace>(initialWorkspaceInfo)
|
||||
const isCurrentWorkspaceManager = useMemo(() => ['owner', 'admin'].includes(currentWorkspace.role), [currentWorkspace.role])
|
||||
const isCurrentWorkspaceOwner = useMemo(() => currentWorkspace.role === 'owner', [currentWorkspace.role])
|
||||
const isCurrentWorkspaceEditor = useMemo(() => ['owner', 'admin', 'editor'].includes(currentWorkspace.role), [currentWorkspace.role])
|
||||
const isCurrentWorkspaceDatasetOperator = useMemo(() => currentWorkspace.role === 'dataset_operator', [currentWorkspace.role])
|
||||
const updateUserProfileAndVersion = useCallback(async () => {
|
||||
if (userProfileResponse && !userProfileResponse.bodyUsed) {
|
||||
try {
|
||||
const result = await userProfileResponse.json()
|
||||
setUserProfile(result)
|
||||
if (!systemFeatures.branding.enabled) {
|
||||
const current_version = userProfileResponse.headers.get('x-version')
|
||||
const current_env = process.env.NODE_ENV === 'development' ? 'DEVELOPMENT' : userProfileResponse.headers.get('x-env')
|
||||
const versionData = await fetchLangGeniusVersion({ url: '/version', params: { current_version } })
|
||||
setLangGeniusVersionInfo({ ...versionData, current_version, latest_version: versionData.version, current_env })
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Failed to update user profile:', error)
|
||||
if (userProfile.id === '')
|
||||
setUserProfile(userProfilePlaceholder)
|
||||
}
|
||||
}
|
||||
else if (userProfileError && userProfile.id === '') {
|
||||
setUserProfile(userProfilePlaceholder)
|
||||
}
|
||||
}, [userProfileResponse, userProfileError, userProfile.id])
|
||||
|
||||
useEffect(() => {
|
||||
updateUserProfileAndVersion()
|
||||
}, [updateUserProfileAndVersion, userProfileResponse])
|
||||
const mutateUserProfile = useCallback(() => {
|
||||
queryClient.invalidateQueries({ queryKey: ['common', 'user-profile'] })
|
||||
}, [queryClient])
|
||||
|
||||
useEffect(() => {
|
||||
if (currentWorkspaceResponse)
|
||||
setCurrentWorkspace(currentWorkspaceResponse)
|
||||
}, [currentWorkspaceResponse])
|
||||
const mutateCurrentWorkspace = useCallback(() => {
|
||||
queryClient.invalidateQueries({ queryKey: ['common', 'current-workspace'] })
|
||||
}, [queryClient])
|
||||
|
||||
// #region Zendesk conversation fields
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
'use client'
|
||||
|
||||
import { createContext, useContext, useContextSelector } from 'use-context-selector'
|
||||
import useSWR from 'swr'
|
||||
import { useEffect, useState } from 'react'
|
||||
import dayjs from 'dayjs'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import {
|
||||
fetchModelList,
|
||||
fetchModelProviders,
|
||||
fetchSupportRetrievalMethods,
|
||||
} from '@/service/common'
|
||||
useModelListByType,
|
||||
useModelProviders,
|
||||
useSupportRetrievalMethods,
|
||||
} from '@/service/use-common'
|
||||
import {
|
||||
CurrentSystemQuotaTypeEnum,
|
||||
ModelStatusEnum,
|
||||
@@ -114,10 +114,10 @@ type ProviderContextProviderProps = {
|
||||
export const ProviderContextProvider = ({
|
||||
children,
|
||||
}: ProviderContextProviderProps) => {
|
||||
const { data: providersData, mutate: refreshModelProviders } = useSWR('/workspaces/current/model-providers', fetchModelProviders)
|
||||
const fetchModelListUrlPrefix = '/workspaces/current/models/model-types/'
|
||||
const { data: textGenerationModelList } = useSWR(`${fetchModelListUrlPrefix}${ModelTypeEnum.textGeneration}`, fetchModelList)
|
||||
const { data: supportRetrievalMethods } = useSWR('/datasets/retrieval-setting', fetchSupportRetrievalMethods)
|
||||
const queryClient = useQueryClient()
|
||||
const { data: providersData } = useModelProviders()
|
||||
const { data: textGenerationModelList } = useModelListByType(ModelTypeEnum.textGeneration)
|
||||
const { data: supportRetrievalMethods } = useSupportRetrievalMethods()
|
||||
|
||||
const [plan, setPlan] = useState(defaultPlan)
|
||||
const [isFetchedPlan, setIsFetchedPlan] = useState(false)
|
||||
@@ -139,6 +139,10 @@ export const ProviderContextProvider = ({
|
||||
const [isAllowTransferWorkspace, setIsAllowTransferWorkspace] = useState(false)
|
||||
const [isAllowPublishAsCustomKnowledgePipelineTemplate, setIsAllowPublishAsCustomKnowledgePipelineTemplate] = useState(false)
|
||||
|
||||
const refreshModelProviders = () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['common', 'model-providers'] })
|
||||
}
|
||||
|
||||
const fetchPlan = async () => {
|
||||
try {
|
||||
const data = await fetchCurrentPlanInfo()
|
||||
@@ -226,7 +230,7 @@ export const ProviderContextProvider = ({
|
||||
modelProviders: providersData?.data || [],
|
||||
refreshModelProviders,
|
||||
textGenerationModelList: textGenerationModelList?.data || [],
|
||||
isAPIKeySet: !!textGenerationModelList?.data.some(model => model.status === ModelStatusEnum.active),
|
||||
isAPIKeySet: !!textGenerationModelList?.data?.some(model => model.status === ModelStatusEnum.active),
|
||||
supportRetrievalMethods: supportRetrievalMethods?.retrieval_method || [],
|
||||
plan,
|
||||
isFetchedPlan,
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { createContext, useContext } from 'use-context-selector'
|
||||
import useSWR from 'swr'
|
||||
import { fetchWorkspaces } from '@/service/common'
|
||||
import { useWorkspaces } from '@/service/use-common'
|
||||
import type { IWorkspace } from '@/models/common'
|
||||
|
||||
export type WorkspacesContextValue = {
|
||||
@@ -20,7 +19,7 @@ type IWorkspaceProviderProps = {
|
||||
export const WorkspaceProvider = ({
|
||||
children,
|
||||
}: IWorkspaceProviderProps) => {
|
||||
const { data } = useSWR({ url: '/workspaces' }, fetchWorkspaces)
|
||||
const { data } = useWorkspaces()
|
||||
|
||||
return (
|
||||
<WorkspacesContext.Provider value={{
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useSWR from 'swr'
|
||||
import {
|
||||
fetchDataSourceNotionBinding,
|
||||
} from '@/service/common'
|
||||
import type { IConfirm } from '@/app/components/base/confirm'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import { useNotionBinding } from '@/service/use-common'
|
||||
|
||||
export type ConfirmType = Pick<IConfirm, 'type' | 'title' | 'content'>
|
||||
|
||||
@@ -58,12 +55,7 @@ export const useCheckNotion = () => {
|
||||
const type = searchParams.get('type')
|
||||
const notionCode = searchParams.get('code')
|
||||
const notionError = searchParams.get('error')
|
||||
const { data } = useSWR(
|
||||
(canBinding && notionCode)
|
||||
? `/oauth/data-source/binding/notion?code=${notionCode}`
|
||||
: null,
|
||||
fetchDataSourceNotionBinding,
|
||||
)
|
||||
const { data } = useNotionBinding(notionCode, canBinding)
|
||||
|
||||
useEffect(() => {
|
||||
if (data)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { Fetcher } from 'swr'
|
||||
import { del, get, patch, post, put } from './base'
|
||||
import type {
|
||||
AccountIntegrate,
|
||||
@@ -49,145 +48,145 @@ type LoginFail = {
|
||||
message: string
|
||||
}
|
||||
type LoginResponse = LoginSuccess | LoginFail
|
||||
export const login: Fetcher<LoginResponse, { url: string; body: Record<string, any> }> = ({ url, body }) => {
|
||||
return post(url, { body }) as Promise<LoginResponse>
|
||||
export const login = ({ url, body }: { url: string; body: Record<string, any> }): Promise<LoginResponse> => {
|
||||
return post<LoginResponse>(url, { body })
|
||||
}
|
||||
export const webAppLogin: Fetcher<LoginResponse, { url: string; body: Record<string, any> }> = ({ url, body }) => {
|
||||
return post(url, { body }, { isPublicAPI: true }) as Promise<LoginResponse>
|
||||
export const webAppLogin = ({ url, body }: { url: string; body: Record<string, any> }): Promise<LoginResponse> => {
|
||||
return post<LoginResponse>(url, { body }, { isPublicAPI: true })
|
||||
}
|
||||
|
||||
export const setup: Fetcher<CommonResponse, { body: Record<string, any> }> = ({ body }) => {
|
||||
export const setup = ({ body }: { body: Record<string, any> }): Promise<CommonResponse> => {
|
||||
return post<CommonResponse>('/setup', { body })
|
||||
}
|
||||
|
||||
export const initValidate: Fetcher<CommonResponse, { body: Record<string, any> }> = ({ body }) => {
|
||||
export const initValidate = ({ body }: { body: Record<string, any> }): Promise<CommonResponse> => {
|
||||
return post<CommonResponse>('/init', { body })
|
||||
}
|
||||
|
||||
export const fetchInitValidateStatus = () => {
|
||||
export const fetchInitValidateStatus = (): Promise<InitValidateStatusResponse> => {
|
||||
return get<InitValidateStatusResponse>('/init')
|
||||
}
|
||||
|
||||
export const fetchSetupStatus = () => {
|
||||
export const fetchSetupStatus = (): Promise<SetupStatusResponse> => {
|
||||
return get<SetupStatusResponse>('/setup')
|
||||
}
|
||||
|
||||
export const fetchUserProfile: Fetcher<UserProfileOriginResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
|
||||
export const fetchUserProfile = ({ url, params }: { url: string; params: Record<string, any> }): Promise<UserProfileOriginResponse> => {
|
||||
return get<UserProfileOriginResponse>(url, params, { needAllResponseContent: true })
|
||||
}
|
||||
|
||||
export const updateUserProfile: Fetcher<CommonResponse, { url: string; body: Record<string, any> }> = ({ url, body }) => {
|
||||
export const updateUserProfile = ({ url, body }: { url: string; body: Record<string, any> }): Promise<CommonResponse> => {
|
||||
return post<CommonResponse>(url, { body })
|
||||
}
|
||||
|
||||
export const fetchLangGeniusVersion: Fetcher<LangGeniusVersionResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
|
||||
export const fetchLangGeniusVersion = ({ url, params }: { url: string; params: Record<string, any> }): Promise<LangGeniusVersionResponse> => {
|
||||
return get<LangGeniusVersionResponse>(url, { params })
|
||||
}
|
||||
|
||||
export const oauth: Fetcher<OauthResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => {
|
||||
export const oauth = ({ url, params }: { url: string; params: Record<string, any> }): Promise<OauthResponse> => {
|
||||
return get<OauthResponse>(url, { params })
|
||||
}
|
||||
|
||||
export const oneMoreStep: Fetcher<CommonResponse, { url: string; body: Record<string, any> }> = ({ url, body }) => {
|
||||
export const oneMoreStep = ({ url, body }: { url: string; body: Record<string, any> }): Promise<CommonResponse> => {
|
||||
return post<CommonResponse>(url, { body })
|
||||
}
|
||||
|
||||
export const fetchMembers: Fetcher<{ accounts: Member[] | null }, { url: string; params: Record<string, any> }> = ({ url, params }) => {
|
||||
export const fetchMembers = ({ url, params }: { url: string; params: Record<string, any> }): Promise<{ accounts: Member[] | null }> => {
|
||||
return get<{ accounts: Member[] | null }>(url, { params })
|
||||
}
|
||||
|
||||
export const fetchProviders: Fetcher<Provider[] | null, { url: string; params: Record<string, any> }> = ({ url, params }) => {
|
||||
export const fetchProviders = ({ url, params }: { url: string; params: Record<string, any> }): Promise<Provider[] | null> => {
|
||||
return get<Provider[] | null>(url, { params })
|
||||
}
|
||||
|
||||
export const validateProviderKey: Fetcher<ValidateOpenAIKeyResponse, { url: string; body: { token: string } }> = ({ url, body }) => {
|
||||
export const validateProviderKey = ({ url, body }: { url: string; body: { token: string } }): Promise<ValidateOpenAIKeyResponse> => {
|
||||
return post<ValidateOpenAIKeyResponse>(url, { body })
|
||||
}
|
||||
export const updateProviderAIKey: Fetcher<UpdateOpenAIKeyResponse, { url: string; body: { token: string | ProviderAzureToken | ProviderAnthropicToken } }> = ({ url, body }) => {
|
||||
export const updateProviderAIKey = ({ url, body }: { url: string; body: { token: string | ProviderAzureToken | ProviderAnthropicToken } }): Promise<UpdateOpenAIKeyResponse> => {
|
||||
return post<UpdateOpenAIKeyResponse>(url, { body })
|
||||
}
|
||||
|
||||
export const fetchAccountIntegrates: Fetcher<{ data: AccountIntegrate[] | null }, { url: string; params: Record<string, any> }> = ({ url, params }) => {
|
||||
export const fetchAccountIntegrates = ({ url, params }: { url: string; params: Record<string, any> }): Promise<{ data: AccountIntegrate[] | null }> => {
|
||||
return get<{ data: AccountIntegrate[] | null }>(url, { params })
|
||||
}
|
||||
|
||||
export const inviteMember: Fetcher<InvitationResponse, { url: string; body: Record<string, any> }> = ({ url, body }) => {
|
||||
export const inviteMember = ({ url, body }: { url: string; body: Record<string, any> }): Promise<InvitationResponse> => {
|
||||
return post<InvitationResponse>(url, { body })
|
||||
}
|
||||
|
||||
export const updateMemberRole: Fetcher<CommonResponse, { url: string; body: Record<string, any> }> = ({ url, body }) => {
|
||||
export const updateMemberRole = ({ url, body }: { url: string; body: Record<string, any> }): Promise<CommonResponse> => {
|
||||
return put<CommonResponse>(url, { body })
|
||||
}
|
||||
|
||||
export const deleteMemberOrCancelInvitation: Fetcher<CommonResponse, { url: string }> = ({ url }) => {
|
||||
export const deleteMemberOrCancelInvitation = ({ url }: { url: string }): Promise<CommonResponse> => {
|
||||
return del<CommonResponse>(url)
|
||||
}
|
||||
|
||||
export const sendOwnerEmail = (body: { language?: string }) =>
|
||||
export const sendOwnerEmail = (body: { language?: string }): Promise<CommonResponse & { data: string }> =>
|
||||
post<CommonResponse & { data: string }>('/workspaces/current/members/send-owner-transfer-confirm-email', { body })
|
||||
|
||||
export const verifyOwnerEmail = (body: { code: string; token: string }) =>
|
||||
export const verifyOwnerEmail = (body: { code: string; token: string }): Promise<CommonResponse & { is_valid: boolean; email: string; token: string }> =>
|
||||
post<CommonResponse & { is_valid: boolean; email: string; token: string }>('/workspaces/current/members/owner-transfer-check', { body })
|
||||
|
||||
export const ownershipTransfer = (memberID: string, body: { token: string }) =>
|
||||
export const ownershipTransfer = (memberID: string, body: { token: string }): Promise<CommonResponse & { is_valid: boolean; email: string; token: string }> =>
|
||||
post<CommonResponse & { is_valid: boolean; email: string; token: string }>(`/workspaces/current/members/${memberID}/owner-transfer`, { body })
|
||||
|
||||
export const fetchFilePreview: Fetcher<{ content: string }, { fileID: string }> = ({ fileID }) => {
|
||||
export const fetchFilePreview = ({ fileID }: { fileID: string }): Promise<{ content: string }> => {
|
||||
return get<{ content: string }>(`/files/${fileID}/preview`)
|
||||
}
|
||||
|
||||
export const fetchCurrentWorkspace: Fetcher<ICurrentWorkspace, { url: string; params: Record<string, any> }> = ({ url, params }) => {
|
||||
export const fetchCurrentWorkspace = ({ url, params }: { url: string; params: Record<string, any> }): Promise<ICurrentWorkspace> => {
|
||||
return post<ICurrentWorkspace>(url, { body: params })
|
||||
}
|
||||
|
||||
export const updateCurrentWorkspace: Fetcher<ICurrentWorkspace, { url: string; body: Record<string, any> }> = ({ url, body }) => {
|
||||
export const updateCurrentWorkspace = ({ url, body }: { url: string; body: Record<string, any> }): Promise<ICurrentWorkspace> => {
|
||||
return post<ICurrentWorkspace>(url, { body })
|
||||
}
|
||||
|
||||
export const fetchWorkspaces: Fetcher<{ workspaces: IWorkspace[] }, { url: string; params: Record<string, any> }> = ({ url, params }) => {
|
||||
export const fetchWorkspaces = ({ url, params }: { url: string; params: Record<string, any> }): Promise<{ workspaces: IWorkspace[] }> => {
|
||||
return get<{ workspaces: IWorkspace[] }>(url, { params })
|
||||
}
|
||||
|
||||
export const switchWorkspace: Fetcher<CommonResponse & { new_tenant: IWorkspace }, { url: string; body: Record<string, any> }> = ({ url, body }) => {
|
||||
export const switchWorkspace = ({ url, body }: { url: string; body: Record<string, any> }): Promise<CommonResponse & { new_tenant: IWorkspace }> => {
|
||||
return post<CommonResponse & { new_tenant: IWorkspace }>(url, { body })
|
||||
}
|
||||
|
||||
export const updateWorkspaceInfo: Fetcher<ICurrentWorkspace, { url: string; body: Record<string, any> }> = ({ url, body }) => {
|
||||
export const updateWorkspaceInfo = ({ url, body }: { url: string; body: Record<string, any> }): Promise<ICurrentWorkspace> => {
|
||||
return post<ICurrentWorkspace>(url, { body })
|
||||
}
|
||||
|
||||
export const fetchDataSource: Fetcher<{ data: DataSourceNotion[] }, { url: string }> = ({ url }) => {
|
||||
export const fetchDataSource = ({ url }: { url: string }): Promise<{ data: DataSourceNotion[] }> => {
|
||||
return get<{ data: DataSourceNotion[] }>(url)
|
||||
}
|
||||
|
||||
export const syncDataSourceNotion: Fetcher<CommonResponse, { url: string }> = ({ url }) => {
|
||||
export const syncDataSourceNotion = ({ url }: { url: string }): Promise<CommonResponse> => {
|
||||
return get<CommonResponse>(url)
|
||||
}
|
||||
|
||||
export const updateDataSourceNotionAction: Fetcher<CommonResponse, { url: string }> = ({ url }) => {
|
||||
export const updateDataSourceNotionAction = ({ url }: { url: string }): Promise<CommonResponse> => {
|
||||
return patch<CommonResponse>(url)
|
||||
}
|
||||
|
||||
export const fetchPluginProviders: Fetcher<PluginProvider[] | null, string> = (url) => {
|
||||
export const fetchPluginProviders = (url: string): Promise<PluginProvider[] | null> => {
|
||||
return get<PluginProvider[] | null>(url)
|
||||
}
|
||||
|
||||
export const validatePluginProviderKey: Fetcher<ValidateOpenAIKeyResponse, { url: string; body: { credentials: any } }> = ({ url, body }) => {
|
||||
export const validatePluginProviderKey = ({ url, body }: { url: string; body: { credentials: any } }): Promise<ValidateOpenAIKeyResponse> => {
|
||||
return post<ValidateOpenAIKeyResponse>(url, { body })
|
||||
}
|
||||
export const updatePluginProviderAIKey: Fetcher<UpdateOpenAIKeyResponse, { url: string; body: { credentials: any } }> = ({ url, body }) => {
|
||||
export const updatePluginProviderAIKey = ({ url, body }: { url: string; body: { credentials: any } }): Promise<UpdateOpenAIKeyResponse> => {
|
||||
return post<UpdateOpenAIKeyResponse>(url, { body })
|
||||
}
|
||||
|
||||
export const invitationCheck: Fetcher<CommonResponse & { is_valid: boolean; data: { workspace_name: string; email: string; workspace_id: string } }, { url: string; params: { workspace_id?: string; email?: string; token: string } }> = ({ url, params }) => {
|
||||
export const invitationCheck = ({ url, params }: { url: string; params: { workspace_id?: string; email?: string; token: string } }): Promise<CommonResponse & { is_valid: boolean; data: { workspace_name: string; email: string; workspace_id: string } }> => {
|
||||
return get<CommonResponse & { is_valid: boolean; data: { workspace_name: string; email: string; workspace_id: string } }>(url, { params })
|
||||
}
|
||||
|
||||
export const activateMember: Fetcher<LoginResponse, { url: string; body: any }> = ({ url, body }) => {
|
||||
export const activateMember = ({ url, body }: { url: string; body: any }): Promise<LoginResponse> => {
|
||||
return post<LoginResponse>(url, { body })
|
||||
}
|
||||
|
||||
export const fetchModelProviders: Fetcher<{ data: ModelProvider[] }, string> = (url) => {
|
||||
export const fetchModelProviders = (url: string): Promise<{ data: ModelProvider[] }> => {
|
||||
return get<{ data: ModelProvider[] }>(url)
|
||||
}
|
||||
|
||||
@@ -195,197 +194,197 @@ export type ModelProviderCredentials = {
|
||||
credentials?: Record<string, string | undefined | boolean>
|
||||
load_balancing: ModelLoadBalancingConfig
|
||||
}
|
||||
export const fetchModelProviderCredentials: Fetcher<ModelProviderCredentials, string> = (url) => {
|
||||
export const fetchModelProviderCredentials = (url: string): Promise<ModelProviderCredentials> => {
|
||||
return get<ModelProviderCredentials>(url)
|
||||
}
|
||||
|
||||
export const fetchModelLoadBalancingConfig: Fetcher<{
|
||||
export const fetchModelLoadBalancingConfig = (url: string): Promise<{
|
||||
credentials?: Record<string, string | undefined | boolean>
|
||||
load_balancing: ModelLoadBalancingConfig
|
||||
}, string> = (url) => {
|
||||
}> => {
|
||||
return get<{
|
||||
credentials?: Record<string, string | undefined | boolean>
|
||||
load_balancing: ModelLoadBalancingConfig
|
||||
}>(url)
|
||||
}
|
||||
|
||||
export const fetchModelProviderModelList: Fetcher<{ data: ModelItem[] }, string> = (url) => {
|
||||
export const fetchModelProviderModelList = (url: string): Promise<{ data: ModelItem[] }> => {
|
||||
return get<{ data: ModelItem[] }>(url)
|
||||
}
|
||||
|
||||
export const fetchModelList: Fetcher<{ data: Model[] }, string> = (url) => {
|
||||
export const fetchModelList = (url: string): Promise<{ data: Model[] }> => {
|
||||
return get<{ data: Model[] }>(url)
|
||||
}
|
||||
|
||||
export const validateModelProvider: Fetcher<ValidateOpenAIKeyResponse, { url: string; body: any }> = ({ url, body }) => {
|
||||
export const validateModelProvider = ({ url, body }: { url: string; body: any }): Promise<ValidateOpenAIKeyResponse> => {
|
||||
return post<ValidateOpenAIKeyResponse>(url, { body })
|
||||
}
|
||||
|
||||
export const validateModelLoadBalancingCredentials: Fetcher<ValidateOpenAIKeyResponse, { url: string; body: any }> = ({ url, body }) => {
|
||||
export const validateModelLoadBalancingCredentials = ({ url, body }: { url: string; body: any }): Promise<ValidateOpenAIKeyResponse> => {
|
||||
return post<ValidateOpenAIKeyResponse>(url, { body })
|
||||
}
|
||||
|
||||
export const setModelProvider: Fetcher<CommonResponse, { url: string; body: any }> = ({ url, body }) => {
|
||||
export const setModelProvider = ({ url, body }: { url: string; body: any }): Promise<CommonResponse> => {
|
||||
return post<CommonResponse>(url, { body })
|
||||
}
|
||||
|
||||
export const deleteModelProvider: Fetcher<CommonResponse, { url: string; body?: any }> = ({ url, body }) => {
|
||||
export const deleteModelProvider = ({ url, body }: { url: string; body?: any }): Promise<CommonResponse> => {
|
||||
return del<CommonResponse>(url, { body })
|
||||
}
|
||||
|
||||
export const changeModelProviderPriority: Fetcher<CommonResponse, { url: string; body: any }> = ({ url, body }) => {
|
||||
export const changeModelProviderPriority = ({ url, body }: { url: string; body: any }): Promise<CommonResponse> => {
|
||||
return post<CommonResponse>(url, { body })
|
||||
}
|
||||
|
||||
export const setModelProviderModel: Fetcher<CommonResponse, { url: string; body: any }> = ({ url, body }) => {
|
||||
export const setModelProviderModel = ({ url, body }: { url: string; body: any }): Promise<CommonResponse> => {
|
||||
return post<CommonResponse>(url, { body })
|
||||
}
|
||||
|
||||
export const deleteModelProviderModel: Fetcher<CommonResponse, { url: string }> = ({ url }) => {
|
||||
export const deleteModelProviderModel = ({ url }: { url: string }): Promise<CommonResponse> => {
|
||||
return del<CommonResponse>(url)
|
||||
}
|
||||
|
||||
export const getPayUrl: Fetcher<{ url: string }, string> = (url) => {
|
||||
export const getPayUrl = (url: string): Promise<{ url: string }> => {
|
||||
return get<{ url: string }>(url)
|
||||
}
|
||||
|
||||
export const fetchDefaultModal: Fetcher<{ data: DefaultModelResponse }, string> = (url) => {
|
||||
export const fetchDefaultModal = (url: string): Promise<{ data: DefaultModelResponse }> => {
|
||||
return get<{ data: DefaultModelResponse }>(url)
|
||||
}
|
||||
|
||||
export const updateDefaultModel: Fetcher<CommonResponse, { url: string; body: any }> = ({ url, body }) => {
|
||||
export const updateDefaultModel = ({ url, body }: { url: string; body: any }): Promise<CommonResponse> => {
|
||||
return post<CommonResponse>(url, { body })
|
||||
}
|
||||
|
||||
export const fetchModelParameterRules: Fetcher<{ data: ModelParameterRule[] }, string> = (url) => {
|
||||
export const fetchModelParameterRules = (url: string): Promise<{ data: ModelParameterRule[] }> => {
|
||||
return get<{ data: ModelParameterRule[] }>(url)
|
||||
}
|
||||
|
||||
export const fetchFileUploadConfig: Fetcher<FileUploadConfigResponse, { url: string }> = ({ url }) => {
|
||||
export const fetchFileUploadConfig = ({ url }: { url: string }): Promise<FileUploadConfigResponse> => {
|
||||
return get<FileUploadConfigResponse>(url)
|
||||
}
|
||||
|
||||
export const fetchNotionConnection: Fetcher<{ data: string }, string> = (url) => {
|
||||
return get(url) as Promise<{ data: string }>
|
||||
export const fetchNotionConnection = (url: string): Promise<{ data: string }> => {
|
||||
return get<{ data: string }>(url)
|
||||
}
|
||||
|
||||
export const fetchDataSourceNotionBinding: Fetcher<{ result: string }, string> = (url) => {
|
||||
return get(url) as Promise<{ result: string }>
|
||||
export const fetchDataSourceNotionBinding = (url: string): Promise<{ result: string }> => {
|
||||
return get<{ result: string }>(url)
|
||||
}
|
||||
|
||||
export const fetchApiBasedExtensionList: Fetcher<ApiBasedExtension[], string> = (url) => {
|
||||
return get(url) as Promise<ApiBasedExtension[]>
|
||||
export const fetchApiBasedExtensionList = (url: string): Promise<ApiBasedExtension[]> => {
|
||||
return get<ApiBasedExtension[]>(url)
|
||||
}
|
||||
|
||||
export const fetchApiBasedExtensionDetail: Fetcher<ApiBasedExtension, string> = (url) => {
|
||||
return get(url) as Promise<ApiBasedExtension>
|
||||
export const fetchApiBasedExtensionDetail = (url: string): Promise<ApiBasedExtension> => {
|
||||
return get<ApiBasedExtension>(url)
|
||||
}
|
||||
|
||||
export const addApiBasedExtension: Fetcher<ApiBasedExtension, { url: string; body: ApiBasedExtension }> = ({ url, body }) => {
|
||||
return post(url, { body }) as Promise<ApiBasedExtension>
|
||||
export const addApiBasedExtension = ({ url, body }: { url: string; body: ApiBasedExtension }): Promise<ApiBasedExtension> => {
|
||||
return post<ApiBasedExtension>(url, { body })
|
||||
}
|
||||
|
||||
export const updateApiBasedExtension: Fetcher<ApiBasedExtension, { url: string; body: ApiBasedExtension }> = ({ url, body }) => {
|
||||
return post(url, { body }) as Promise<ApiBasedExtension>
|
||||
export const updateApiBasedExtension = ({ url, body }: { url: string; body: ApiBasedExtension }): Promise<ApiBasedExtension> => {
|
||||
return post<ApiBasedExtension>(url, { body })
|
||||
}
|
||||
|
||||
export const deleteApiBasedExtension: Fetcher<{ result: string }, string> = (url) => {
|
||||
return del(url) as Promise<{ result: string }>
|
||||
export const deleteApiBasedExtension = (url: string): Promise<{ result: string }> => {
|
||||
return del<{ result: string }>(url)
|
||||
}
|
||||
|
||||
export const fetchCodeBasedExtensionList: Fetcher<CodeBasedExtension, string> = (url) => {
|
||||
return get(url) as Promise<CodeBasedExtension>
|
||||
export const fetchCodeBasedExtensionList = (url: string): Promise<CodeBasedExtension> => {
|
||||
return get<CodeBasedExtension>(url)
|
||||
}
|
||||
|
||||
export const moderate = (url: string, body: { app_id: string; text: string }) => {
|
||||
return post(url, { body }) as Promise<ModerateResponse>
|
||||
export const moderate = (url: string, body: { app_id: string; text: string }): Promise<ModerateResponse> => {
|
||||
return post<ModerateResponse>(url, { body })
|
||||
}
|
||||
|
||||
type RetrievalMethodsRes = {
|
||||
retrieval_method: RETRIEVE_METHOD[]
|
||||
}
|
||||
export const fetchSupportRetrievalMethods: Fetcher<RetrievalMethodsRes, string> = (url) => {
|
||||
export const fetchSupportRetrievalMethods = (url: string): Promise<RetrievalMethodsRes> => {
|
||||
return get<RetrievalMethodsRes>(url)
|
||||
}
|
||||
|
||||
export const getSystemFeatures = () => {
|
||||
export const getSystemFeatures = (): Promise<SystemFeatures> => {
|
||||
return get<SystemFeatures>('/system-features')
|
||||
}
|
||||
|
||||
export const enableModel = (url: string, body: { model: string; model_type: ModelTypeEnum }) =>
|
||||
export const enableModel = (url: string, body: { model: string; model_type: ModelTypeEnum }): Promise<CommonResponse> =>
|
||||
patch<CommonResponse>(url, { body })
|
||||
|
||||
export const disableModel = (url: string, body: { model: string; model_type: ModelTypeEnum }) =>
|
||||
export const disableModel = (url: string, body: { model: string; model_type: ModelTypeEnum }): Promise<CommonResponse> =>
|
||||
patch<CommonResponse>(url, { body })
|
||||
|
||||
export const sendForgotPasswordEmail: Fetcher<CommonResponse & { data: string }, { url: string; body: { email: string } }> = ({ url, body }) =>
|
||||
export const sendForgotPasswordEmail = ({ url, body }: { url: string; body: { email: string } }): Promise<CommonResponse & { data: string }> =>
|
||||
post<CommonResponse & { data: string }>(url, { body })
|
||||
|
||||
export const verifyForgotPasswordToken: Fetcher<CommonResponse & { is_valid: boolean; email: string }, { url: string; body: { token: string } }> = ({ url, body }) => {
|
||||
return post(url, { body }) as Promise<CommonResponse & { is_valid: boolean; email: string }>
|
||||
export const verifyForgotPasswordToken = ({ url, body }: { url: string; body: { token: string } }): Promise<CommonResponse & { is_valid: boolean; email: string }> => {
|
||||
return post<CommonResponse & { is_valid: boolean; email: string }>(url, { body })
|
||||
}
|
||||
|
||||
export const changePasswordWithToken: Fetcher<CommonResponse, { url: string; body: { token: string; new_password: string; password_confirm: string } }> = ({ url, body }) =>
|
||||
export const changePasswordWithToken = ({ url, body }: { url: string; body: { token: string; new_password: string; password_confirm: string } }): Promise<CommonResponse> =>
|
||||
post<CommonResponse>(url, { body })
|
||||
|
||||
export const sendWebAppForgotPasswordEmail: Fetcher<CommonResponse & { data: string }, { url: string; body: { email: string } }> = ({ url, body }) =>
|
||||
export const sendWebAppForgotPasswordEmail = ({ url, body }: { url: string; body: { email: string } }): Promise<CommonResponse & { data: string }> =>
|
||||
post<CommonResponse & { data: string }>(url, { body }, { isPublicAPI: true })
|
||||
|
||||
export const verifyWebAppForgotPasswordToken: Fetcher<CommonResponse & { is_valid: boolean; email: string }, { url: string; body: { token: string } }> = ({ url, body }) => {
|
||||
return post(url, { body }, { isPublicAPI: true }) as Promise<CommonResponse & { is_valid: boolean; email: string }>
|
||||
export const verifyWebAppForgotPasswordToken = ({ url, body }: { url: string; body: { token: string } }): Promise<CommonResponse & { is_valid: boolean; email: string }> => {
|
||||
return post<CommonResponse & { is_valid: boolean; email: string }>(url, { body }, { isPublicAPI: true })
|
||||
}
|
||||
|
||||
export const changeWebAppPasswordWithToken: Fetcher<CommonResponse, { url: string; body: { token: string; new_password: string; password_confirm: string } }> = ({ url, body }) =>
|
||||
export const changeWebAppPasswordWithToken = ({ url, body }: { url: string; body: { token: string; new_password: string; password_confirm: string } }): Promise<CommonResponse> =>
|
||||
post<CommonResponse>(url, { body }, { isPublicAPI: true })
|
||||
|
||||
export const uploadRemoteFileInfo = (url: string, isPublic?: boolean, silent?: boolean) => {
|
||||
export const uploadRemoteFileInfo = (url: string, isPublic?: boolean, silent?: boolean): Promise<{ id: string; name: string; size: number; mime_type: string; url: string }> => {
|
||||
return post<{ id: string; name: string; size: number; mime_type: string; url: string }>('/remote-files/upload', { body: { url } }, { isPublicAPI: isPublic, silent })
|
||||
}
|
||||
|
||||
export const sendEMailLoginCode = (email: string, language = 'en-US') =>
|
||||
export const sendEMailLoginCode = (email: string, language = 'en-US'): Promise<CommonResponse & { data: string }> =>
|
||||
post<CommonResponse & { data: string }>('/email-code-login', { body: { email, language } })
|
||||
|
||||
export const emailLoginWithCode = (data: { email: string; code: string; token: string; language: string }) =>
|
||||
export const emailLoginWithCode = (data: { email: string; code: string; token: string; language: string }): Promise<LoginResponse> =>
|
||||
post<LoginResponse>('/email-code-login/validity', { body: data })
|
||||
|
||||
export const sendResetPasswordCode = (email: string, language = 'en-US') =>
|
||||
export const sendResetPasswordCode = (email: string, language = 'en-US'): Promise<CommonResponse & { data: string; message?: string; code?: string }> =>
|
||||
post<CommonResponse & { data: string; message?: string; code?: string }>('/forgot-password', { body: { email, language } })
|
||||
|
||||
export const verifyResetPasswordCode = (body: { email: string; code: string; token: string }) =>
|
||||
export const verifyResetPasswordCode = (body: { email: string; code: string; token: string }): Promise<CommonResponse & { is_valid: boolean; token: string }> =>
|
||||
post<CommonResponse & { is_valid: boolean; token: string }>('/forgot-password/validity', { body })
|
||||
|
||||
export const sendWebAppEMailLoginCode = (email: string, language = 'en-US') =>
|
||||
export const sendWebAppEMailLoginCode = (email: string, language = 'en-US'): Promise<CommonResponse & { data: string }> =>
|
||||
post<CommonResponse & { data: string }>('/email-code-login', { body: { email, language } }, { isPublicAPI: true })
|
||||
|
||||
export const webAppEmailLoginWithCode = (data: { email: string; code: string; token: string }) =>
|
||||
export const webAppEmailLoginWithCode = (data: { email: string; code: string; token: string }): Promise<LoginResponse> =>
|
||||
post<LoginResponse>('/email-code-login/validity', { body: data }, { isPublicAPI: true })
|
||||
|
||||
export const sendWebAppResetPasswordCode = (email: string, language = 'en-US') =>
|
||||
export const sendWebAppResetPasswordCode = (email: string, language = 'en-US'): Promise<CommonResponse & { data: string; message?: string; code?: string }> =>
|
||||
post<CommonResponse & { data: string; message?: string; code?: string }>('/forgot-password', { body: { email, language } }, { isPublicAPI: true })
|
||||
|
||||
export const verifyWebAppResetPasswordCode = (body: { email: string; code: string; token: string }) =>
|
||||
export const verifyWebAppResetPasswordCode = (body: { email: string; code: string; token: string }): Promise<CommonResponse & { is_valid: boolean; token: string }> =>
|
||||
post<CommonResponse & { is_valid: boolean; token: string }>('/forgot-password/validity', { body }, { isPublicAPI: true })
|
||||
|
||||
export const sendDeleteAccountCode = () =>
|
||||
export const sendDeleteAccountCode = (): Promise<CommonResponse & { data: string }> =>
|
||||
get<CommonResponse & { data: string }>('/account/delete/verify')
|
||||
|
||||
export const verifyDeleteAccountCode = (body: { code: string; token: string }) =>
|
||||
export const verifyDeleteAccountCode = (body: { code: string; token: string }): Promise<CommonResponse & { is_valid: boolean }> =>
|
||||
post<CommonResponse & { is_valid: boolean }>('/account/delete', { body })
|
||||
|
||||
export const submitDeleteAccountFeedback = (body: { feedback: string; email: string }) =>
|
||||
export const submitDeleteAccountFeedback = (body: { feedback: string; email: string }): Promise<CommonResponse> =>
|
||||
post<CommonResponse>('/account/delete/feedback', { body })
|
||||
|
||||
export const getDocDownloadUrl = (doc_name: string) =>
|
||||
export const getDocDownloadUrl = (doc_name: string): Promise<{ url: string }> =>
|
||||
get<{ url: string }>('/compliance/download', { params: { doc_name } }, { silent: true })
|
||||
|
||||
export const sendVerifyCode = (body: { email: string; phase: string; token?: string }) =>
|
||||
export const sendVerifyCode = (body: { email: string; phase: string; token?: string }): Promise<CommonResponse & { data: string }> =>
|
||||
post<CommonResponse & { data: string }>('/account/change-email', { body })
|
||||
|
||||
export const verifyEmail = (body: { email: string; code: string; token: string }) =>
|
||||
export const verifyEmail = (body: { email: string; code: string; token: string }): Promise<CommonResponse & { is_valid: boolean; email: string; token: string }> =>
|
||||
post<CommonResponse & { is_valid: boolean; email: string; token: string }>('/account/change-email/validity', { body })
|
||||
|
||||
export const resetEmail = (body: { new_email: string; token: string }) =>
|
||||
export const resetEmail = (body: { new_email: string; token: string }): Promise<CommonResponse> =>
|
||||
post<CommonResponse>('/account/change-email/reset', { body })
|
||||
|
||||
export const checkEmailExisted = (body: { email: string }) =>
|
||||
export const checkEmailExisted = (body: { email: string }): Promise<CommonResponse> =>
|
||||
post<CommonResponse>('/account/change-email/check-email-unique', { body }, { silent: true })
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { get, post } from './base'
|
||||
import type {
|
||||
AccountIntegrate,
|
||||
CommonResponse,
|
||||
DataSourceNotion,
|
||||
FileUploadConfigResponse,
|
||||
Member,
|
||||
StructuredOutputRulesRequestBody,
|
||||
@@ -7,16 +10,112 @@ import type {
|
||||
} from '@/models/common'
|
||||
import { useMutation, useQuery } from '@tanstack/react-query'
|
||||
import type { FileTypesRes } from './datasets'
|
||||
import type { ICurrentWorkspace, IWorkspace, UserProfileResponse } from '@/models/common'
|
||||
import type {
|
||||
Model,
|
||||
ModelProvider,
|
||||
ModelTypeEnum,
|
||||
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import type { RETRIEVE_METHOD } from '@/types/app'
|
||||
import type { LangGeniusVersionResponse } from '@/models/common'
|
||||
import type { PluginProvider } from '@/models/common'
|
||||
import type { ApiBasedExtension } from '@/models/common'
|
||||
import type { ModelParameterRule } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import type { CodeBasedExtension } from '@/models/common'
|
||||
import { useInvalid } from './use-base'
|
||||
|
||||
const NAME_SPACE = 'common'
|
||||
|
||||
export const commonQueryKeys = {
|
||||
fileUploadConfig: [NAME_SPACE, 'file-upload-config'] as const,
|
||||
userProfile: [NAME_SPACE, 'user-profile'] as const,
|
||||
currentWorkspace: [NAME_SPACE, 'current-workspace'] as const,
|
||||
workspaces: [NAME_SPACE, 'workspaces'] as const,
|
||||
members: [NAME_SPACE, 'members'] as const,
|
||||
filePreview: (fileID: string) => [NAME_SPACE, 'file-preview', fileID] as const,
|
||||
schemaDefinitions: [NAME_SPACE, 'schema-type-definitions'] as const,
|
||||
isLogin: [NAME_SPACE, 'is-login'] as const,
|
||||
modelProviders: [NAME_SPACE, 'model-providers'] as const,
|
||||
modelList: (type: ModelTypeEnum) => [NAME_SPACE, 'model-list', type] as const,
|
||||
defaultModel: (type: ModelTypeEnum) => [NAME_SPACE, 'default-model', type] as const,
|
||||
retrievalMethods: [NAME_SPACE, 'support-retrieval-methods'] as const,
|
||||
accountIntegrates: [NAME_SPACE, 'account-integrates'] as const,
|
||||
pluginProviders: [NAME_SPACE, 'plugin-providers'] as const,
|
||||
notionConnection: [NAME_SPACE, 'notion-connection'] as const,
|
||||
apiBasedExtensions: [NAME_SPACE, 'api-based-extensions'] as const,
|
||||
codeBasedExtensions: (module?: string) => [NAME_SPACE, 'code-based-extensions', module] as const,
|
||||
invitationCheck: (params?: { workspace_id?: string; email?: string; token?: string }) => [
|
||||
NAME_SPACE,
|
||||
'invitation-check',
|
||||
params?.workspace_id ?? '',
|
||||
params?.email ?? '',
|
||||
params?.token ?? '',
|
||||
] as const,
|
||||
notionBinding: (code?: string | null) => [NAME_SPACE, 'notion-binding', code] as const,
|
||||
modelParameterRules: (provider?: string, model?: string) => [NAME_SPACE, 'model-parameter-rules', provider, model] as const,
|
||||
langGeniusVersion: (currentVersion?: string | null) => [NAME_SPACE, 'lang-genius-version', currentVersion] as const,
|
||||
forgotPasswordValidity: (token?: string | null) => [NAME_SPACE, 'forgot-password-validity', token] as const,
|
||||
dataSourceIntegrates: [NAME_SPACE, 'data-source-integrates'] as const,
|
||||
}
|
||||
|
||||
export const useFileUploadConfig = () => {
|
||||
return useQuery<FileUploadConfigResponse>({
|
||||
queryKey: [NAME_SPACE, 'file-upload-config'],
|
||||
queryKey: commonQueryKeys.fileUploadConfig,
|
||||
queryFn: () => get<FileUploadConfigResponse>('/files/upload'),
|
||||
})
|
||||
}
|
||||
|
||||
type UserProfileWithMeta = {
|
||||
profile: UserProfileResponse
|
||||
meta: {
|
||||
currentVersion: string | null
|
||||
currentEnv: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export const useUserProfile = () => {
|
||||
return useQuery<UserProfileWithMeta>({
|
||||
queryKey: commonQueryKeys.userProfile,
|
||||
queryFn: async () => {
|
||||
const response = await get<Response>('/account/profile', {}, { needAllResponseContent: true }) as Response
|
||||
const profile = await response.clone().json() as UserProfileResponse
|
||||
return {
|
||||
profile,
|
||||
meta: {
|
||||
currentVersion: response.headers.get('x-version'),
|
||||
currentEnv: process.env.NODE_ENV === 'development'
|
||||
? 'DEVELOPMENT'
|
||||
: response.headers.get('x-env'),
|
||||
},
|
||||
}
|
||||
},
|
||||
staleTime: 0,
|
||||
gcTime: 0,
|
||||
})
|
||||
}
|
||||
|
||||
export const useLangGeniusVersion = (currentVersion?: string | null, enabled?: boolean) => {
|
||||
return useQuery<LangGeniusVersionResponse>({
|
||||
queryKey: commonQueryKeys.langGeniusVersion(currentVersion || undefined),
|
||||
queryFn: () => get<LangGeniusVersionResponse>('/version', { params: { current_version: currentVersion } }),
|
||||
enabled: !!currentVersion && (enabled ?? true),
|
||||
})
|
||||
}
|
||||
|
||||
export const useCurrentWorkspace = () => {
|
||||
return useQuery<ICurrentWorkspace>({
|
||||
queryKey: commonQueryKeys.currentWorkspace,
|
||||
queryFn: () => post<ICurrentWorkspace>('/workspaces/current', { body: {} }),
|
||||
})
|
||||
}
|
||||
|
||||
export const useWorkspaces = () => {
|
||||
return useQuery<{ workspaces: IWorkspace[] }>({
|
||||
queryKey: commonQueryKeys.workspaces,
|
||||
queryFn: () => get<{ workspaces: IWorkspace[] }>('/workspaces'),
|
||||
})
|
||||
}
|
||||
|
||||
export const useGenerateStructuredOutputRules = () => {
|
||||
return useMutation({
|
||||
mutationKey: [NAME_SPACE, 'generate-structured-output-rules'],
|
||||
@@ -74,10 +173,8 @@ type MemberResponse = {
|
||||
|
||||
export const useMembers = () => {
|
||||
return useQuery<MemberResponse>({
|
||||
queryKey: [NAME_SPACE, 'members'],
|
||||
queryFn: (params: Record<string, any>) => get<MemberResponse>('/workspaces/current/members', {
|
||||
params,
|
||||
}),
|
||||
queryKey: commonQueryKeys.members,
|
||||
queryFn: () => get<MemberResponse>('/workspaces/current/members', { params: {} }),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -87,7 +184,7 @@ type FilePreviewResponse = {
|
||||
|
||||
export const useFilePreview = (fileID: string) => {
|
||||
return useQuery<FilePreviewResponse>({
|
||||
queryKey: [NAME_SPACE, 'file-preview', fileID],
|
||||
queryKey: commonQueryKeys.filePreview(fileID),
|
||||
queryFn: () => get<FilePreviewResponse>(`/files/${fileID}/preview`),
|
||||
enabled: !!fileID,
|
||||
})
|
||||
@@ -102,7 +199,7 @@ export type SchemaTypeDefinition = {
|
||||
|
||||
export const useSchemaTypeDefinitions = () => {
|
||||
return useQuery<SchemaTypeDefinition[]>({
|
||||
queryKey: [NAME_SPACE, 'schema-type-definitions'],
|
||||
queryKey: commonQueryKeys.schemaDefinitions,
|
||||
queryFn: () => get<SchemaTypeDefinition[]>('/spec/schema-definitions'),
|
||||
})
|
||||
}
|
||||
@@ -113,7 +210,7 @@ type isLogin = {
|
||||
|
||||
export const useIsLogin = () => {
|
||||
return useQuery<isLogin>({
|
||||
queryKey: [NAME_SPACE, 'is-login'],
|
||||
queryKey: commonQueryKeys.isLogin,
|
||||
staleTime: 0,
|
||||
gcTime: 0,
|
||||
queryFn: async (): Promise<isLogin> => {
|
||||
@@ -138,3 +235,141 @@ export const useLogout = () => {
|
||||
mutationFn: () => post('/logout'),
|
||||
})
|
||||
}
|
||||
|
||||
type ForgotPasswordValidity = CommonResponse & { is_valid: boolean; email: string }
|
||||
export const useVerifyForgotPasswordToken = (token?: string | null) => {
|
||||
return useQuery<ForgotPasswordValidity>({
|
||||
queryKey: commonQueryKeys.forgotPasswordValidity(token),
|
||||
queryFn: () => post<ForgotPasswordValidity>('/forgot-password/validity', { body: { token } }),
|
||||
enabled: !!token,
|
||||
staleTime: 0,
|
||||
gcTime: 0,
|
||||
retry: false,
|
||||
})
|
||||
}
|
||||
|
||||
type OneMoreStepPayload = {
|
||||
invitation_code: string
|
||||
interface_language: string
|
||||
timezone: string
|
||||
}
|
||||
export const useOneMoreStep = () => {
|
||||
return useMutation({
|
||||
mutationKey: [NAME_SPACE, 'one-more-step'],
|
||||
mutationFn: (body: OneMoreStepPayload) => post<CommonResponse>('/account/init', { body }),
|
||||
})
|
||||
}
|
||||
|
||||
export const useModelProviders = () => {
|
||||
return useQuery<{ data: ModelProvider[] }>({
|
||||
queryKey: commonQueryKeys.modelProviders,
|
||||
queryFn: () => get<{ data: ModelProvider[] }>('/workspaces/current/model-providers'),
|
||||
})
|
||||
}
|
||||
|
||||
export const useModelListByType = (type: ModelTypeEnum, enabled = true) => {
|
||||
return useQuery<{ data: Model[] }>({
|
||||
queryKey: commonQueryKeys.modelList(type),
|
||||
queryFn: () => get<{ data: Model[] }>(`/workspaces/current/models/model-types/${type}`),
|
||||
enabled,
|
||||
})
|
||||
}
|
||||
|
||||
export const useDefaultModelByType = (type: ModelTypeEnum, enabled = true) => {
|
||||
return useQuery({
|
||||
queryKey: commonQueryKeys.defaultModel(type),
|
||||
queryFn: () => get(`/workspaces/current/default-model?model_type=${type}`),
|
||||
enabled,
|
||||
})
|
||||
}
|
||||
|
||||
export const useSupportRetrievalMethods = () => {
|
||||
return useQuery<{ retrieval_method: RETRIEVE_METHOD[] }>({
|
||||
queryKey: commonQueryKeys.retrievalMethods,
|
||||
queryFn: () => get<{ retrieval_method: RETRIEVE_METHOD[] }>('/datasets/retrieval-setting'),
|
||||
})
|
||||
}
|
||||
|
||||
export const useAccountIntegrates = () => {
|
||||
return useQuery<{ data: AccountIntegrate[] | null }>({
|
||||
queryKey: commonQueryKeys.accountIntegrates,
|
||||
queryFn: () => get<{ data: AccountIntegrate[] | null }>('/account/integrates'),
|
||||
})
|
||||
}
|
||||
|
||||
type DataSourceIntegratesOptions = {
|
||||
enabled?: boolean
|
||||
initialData?: { data: DataSourceNotion[] }
|
||||
}
|
||||
|
||||
export const useDataSourceIntegrates = (options: DataSourceIntegratesOptions = {}) => {
|
||||
const { enabled = true, initialData } = options
|
||||
return useQuery<{ data: DataSourceNotion[] }>({
|
||||
queryKey: commonQueryKeys.dataSourceIntegrates,
|
||||
queryFn: () => get<{ data: DataSourceNotion[] }>('/data-source/integrates'),
|
||||
enabled,
|
||||
initialData,
|
||||
})
|
||||
}
|
||||
|
||||
export const useInvalidDataSourceIntegrates = () => {
|
||||
return useInvalid(commonQueryKeys.dataSourceIntegrates)
|
||||
}
|
||||
|
||||
export const usePluginProviders = () => {
|
||||
return useQuery<PluginProvider[] | null>({
|
||||
queryKey: commonQueryKeys.pluginProviders,
|
||||
queryFn: () => get<PluginProvider[] | null>('/workspaces/current/tool-providers'),
|
||||
})
|
||||
}
|
||||
|
||||
export const useCodeBasedExtensions = (module: string) => {
|
||||
return useQuery<CodeBasedExtension>({
|
||||
queryKey: commonQueryKeys.codeBasedExtensions(module),
|
||||
queryFn: () => get<CodeBasedExtension>(`/code-based-extension?module=${module}`),
|
||||
})
|
||||
}
|
||||
|
||||
export const useNotionConnection = (enabled: boolean) => {
|
||||
return useQuery<{ data: string }>({
|
||||
queryKey: commonQueryKeys.notionConnection,
|
||||
queryFn: () => get<{ data: string }>('/oauth/data-source/notion'),
|
||||
enabled,
|
||||
})
|
||||
}
|
||||
|
||||
export const useApiBasedExtensions = () => {
|
||||
return useQuery<ApiBasedExtension[]>({
|
||||
queryKey: commonQueryKeys.apiBasedExtensions,
|
||||
queryFn: () => get<ApiBasedExtension[]>('/api-based-extension'),
|
||||
})
|
||||
}
|
||||
|
||||
export const useInvitationCheck = (params?: { workspace_id?: string; email?: string; token?: string }, enabled?: boolean) => {
|
||||
return useQuery({
|
||||
queryKey: commonQueryKeys.invitationCheck(params),
|
||||
queryFn: () => get<{
|
||||
is_valid: boolean
|
||||
data: { workspace_name: string; email: string; workspace_id: string }
|
||||
result: string
|
||||
}>('/activate/check', { params }),
|
||||
enabled: enabled ?? !!params?.token,
|
||||
retry: false,
|
||||
})
|
||||
}
|
||||
|
||||
export const useNotionBinding = (code?: string | null, enabled?: boolean) => {
|
||||
return useQuery({
|
||||
queryKey: commonQueryKeys.notionBinding(code),
|
||||
queryFn: () => get<{ result: string }>('/oauth/data-source/binding/notion', { params: { code } }),
|
||||
enabled: !!code && (enabled ?? true),
|
||||
})
|
||||
}
|
||||
|
||||
export const useModelParameterRules = (provider?: string, model?: string, enabled?: boolean) => {
|
||||
return useQuery<{ data: ModelParameterRule[] }>({
|
||||
queryKey: commonQueryKeys.modelParameterRules(provider, model),
|
||||
queryFn: () => get<{ data: ModelParameterRule[] }>(`/workspaces/current/model-providers/${provider}/models/parameter-rules`, { params: { model } }),
|
||||
enabled: !!provider && !!model && (enabled ?? true),
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user