From dea4e66456912fa5e7c8271d8e2c4e71266712fe Mon Sep 17 00:00:00 2001
From: yyh <92089059+lyzno1@users.noreply.github.com>
Date: Tue, 2 Jun 2026 15:28:05 +0800
Subject: [PATCH] fix(web): use generated account-profile contracts (#36927)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
---
api/controllers/console/workspace/account.py | 10 +--
api/openapi/markdown/console-swagger.md | 2 +-
.../unit_tests/controllers/test_swagger.py | 20 ++++++
.../app/log/__tests__/list.spec.tsx | 14 ++--
web/app/components/app/log/list.tsx | 8 ++-
web/app/components/app/workflow-log/index.tsx | 8 ++-
.../__tests__/chat-wrapper.spec.tsx | 7 ++
.../chat/chat/answer/__tests__/index.spec.tsx | 7 ++
.../__tests__/index.spec.tsx | 5 +-
.../base/__tests__/date-picker.spec.tsx | 9 ++-
.../datasets/metadata/base/date-picker.tsx | 8 ++-
.../__tests__/index.spec.tsx | 6 ++
.../language-page/__tests__/index.spec.tsx | 6 +-
.../detail-header/__tests__/index.spec.tsx | 9 ++-
.../detail-header/index.tsx | 8 ++-
.../__tests__/index.spec.tsx | 28 ++++----
.../auto-update-setting/index.tsx | 8 ++-
.../__tests__/integration.spec.tsx | 38 +++++-----
.../condition-list/condition-date.tsx | 8 ++-
.../__tests__/use-config.spec.ts | 7 +-
.../nodes/trigger-schedule/use-config.ts | 12 ++--
web/app/education-apply/hooks.ts | 8 ++-
web/context/app-context-provider.tsx | 5 +-
web/context/app-context.ts | 5 +-
web/contract/console/account.ts | 37 ----------
web/contract/router.ts | 9 ---
.../account-profile/__tests__/server.spec.ts | 4 +-
web/features/account-profile/client.ts | 6 +-
web/features/account-profile/server.ts | 4 +-
web/hooks/use-timestamp.spec.ts | 9 +--
web/hooks/use-timestamp.ts | 8 ++-
web/models/common.ts | 24 ++-----
web/service/common.ts | 2 +-
web/test/account-profile-query.ts | 70 +++++++++++++++++++
34 files changed, 268 insertions(+), 151 deletions(-)
delete mode 100644 web/contract/console/account.ts
create mode 100644 web/test/account-profile-query.ts
diff --git a/api/controllers/console/workspace/account.py b/api/controllers/console/workspace/account.py
index d2f5d44b11..984a128376 100644
--- a/api/controllers/console/workspace/account.py
+++ b/api/controllers/console/workspace/account.py
@@ -18,7 +18,7 @@ from controllers.common.fields import (
SimpleResultResponse,
VerificationTokenResponse,
)
-from controllers.common.schema import register_response_schema_models, register_schema_models
+from controllers.common.schema import query_params_from_model, register_response_schema_models, register_schema_models
from controllers.console import console_ns
from controllers.console.auth.error import (
EmailAlreadyInUseError,
@@ -48,7 +48,7 @@ from fields.base import ResponseModel
from fields.member_fields import Account as AccountResponse
from graphon.file import helpers as file_helpers
from libs.datetime_utils import naive_utc_now
-from libs.helper import EmailStr, extract_remote_ip, timezone, to_timestamp
+from libs.helper import EmailStr, dump_response, extract_remote_ip, timezone, to_timestamp
from libs.login import current_account_with_tenant, login_required
from models import AccountIntegrate, InvitationCode
from models.account import AccountStatus, InvitationCodeStatus
@@ -329,9 +329,9 @@ class AccountNameApi(Resource):
@console_ns.route("/account/avatar")
class AccountAvatarApi(Resource):
- @console_ns.expect(console_ns.models[AccountAvatarQuery.__name__])
@console_ns.doc("get_account_avatar")
@console_ns.doc(description="Get account avatar url")
+ @console_ns.doc(params=query_params_from_model(AccountAvatarQuery))
@console_ns.response(200, "Success", console_ns.models[AvatarUrlResponse.__name__])
@setup_required
@login_required
@@ -342,7 +342,7 @@ class AccountAvatarApi(Resource):
avatar = args.avatar
if avatar.startswith(("http://", "https://")):
- return {"avatar_url": avatar}
+ return dump_response(AvatarUrlResponse, {"avatar_url": avatar})
upload_file = db.session.scalar(select(UploadFile).where(UploadFile.id == avatar).limit(1))
if upload_file is None:
@@ -355,7 +355,7 @@ class AccountAvatarApi(Resource):
raise NotFound("Avatar file not found")
avatar_url = file_helpers.get_signed_file_url(upload_file_id=upload_file.id)
- return {"avatar_url": avatar_url}
+ return dump_response(AvatarUrlResponse, {"avatar_url": avatar_url})
@console_ns.expect(console_ns.models[AccountAvatarPayload.__name__])
@setup_required
diff --git a/api/openapi/markdown/console-swagger.md b/api/openapi/markdown/console-swagger.md
index a925ed8989..04235cd528 100644
--- a/api/openapi/markdown/console-swagger.md
+++ b/api/openapi/markdown/console-swagger.md
@@ -27,7 +27,7 @@ Get account avatar url
| Name | Located in | Description | Required | Schema |
| ---- | ---------- | ----------- | -------- | ------ |
-| payload | body | | Yes | [AccountAvatarQuery](#accountavatarquery) |
+| avatar | query | Avatar file ID | Yes | string |
##### Responses
diff --git a/api/tests/unit_tests/controllers/test_swagger.py b/api/tests/unit_tests/controllers/test_swagger.py
index e45c2658d3..4e81763a20 100644
--- a/api/tests/unit_tests/controllers/test_swagger.py
+++ b/api/tests/unit_tests/controllers/test_swagger.py
@@ -140,3 +140,23 @@ def test_service_document_list_documents_query_params_render(monkeypatch: pytest
for name in ("page", "limit", "keyword", "status"):
assert params[name]["in"] == "query"
+
+
+def test_console_account_avatar_query_param_renders_as_query(monkeypatch: pytest.MonkeyPatch):
+ from configs import dify_config
+ from controllers.console import bp as console_bp
+
+ monkeypatch.setattr(dify_config, "SWAGGER_UI_ENABLED", True)
+
+ app = Flask(__name__)
+ app.config["TESTING"] = True
+ app.config["RESTX_INCLUDE_ALL_MODELS"] = True
+ app.register_blueprint(console_bp)
+
+ payload = app.test_client().get("/console/api/swagger.json").get_json()
+ operation = payload["paths"]["/account/avatar"]["get"]
+ params = _parameters_by_name(operation)
+
+ assert "payload" not in params
+ assert params["avatar"]["in"] == "query"
+ assert params["avatar"]["required"] is True
diff --git a/web/app/components/app/log/__tests__/list.spec.tsx b/web/app/components/app/log/__tests__/list.spec.tsx
index dbd350f16b..6812461671 100644
--- a/web/app/components/app/log/__tests__/list.spec.tsx
+++ b/web/app/components/app/log/__tests__/list.spec.tsx
@@ -1,6 +1,7 @@
/* eslint-disable ts/no-explicit-any */
import type { ReactNode } from 'react'
import { act, fireEvent, screen, waitFor } from '@testing-library/react'
+import { AccountProfileQueryProvider, createAccountProfileQueryClient } from '@/test/account-profile-query'
import { renderWithNuqs } from '@/test/nuqs-testing'
import { AppModeEnum } from '@/types/app'
import ConversationList from '../list'
@@ -234,12 +235,15 @@ const renderConversationList = ({
logs?: any
searchParams?: string
} = {}) => {
+ const queryClient = createAccountProfileQueryClient({ timezone: 'Asia/Shanghai' })
return renderWithNuqs(
- ,
+
+
+ ,
{ searchParams },
)
}
diff --git a/web/app/components/app/log/list.tsx b/web/app/components/app/log/list.tsx
index e28f5473f6..315cddaa12 100644
--- a/web/app/components/app/log/list.tsx
+++ b/web/app/components/app/log/list.tsx
@@ -21,6 +21,7 @@ import { StatusDot } from '@langgenius/dify-ui/status-dot'
import { toast } from '@langgenius/dify-ui/toast'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { RiCloseLine, RiEditFill } from '@remixicon/react'
+import { useQuery } from '@tanstack/react-query'
import dayjs from 'dayjs'
import timezone from 'dayjs/plugin/timezone'
import utc from 'dayjs/plugin/utc'
@@ -40,7 +41,7 @@ import CopyIcon from '@/app/components/base/copy-icon'
import Loading from '@/app/components/base/loading'
import MessageLogModal from '@/app/components/base/message-log-modal'
import { WorkflowContextProvider } from '@/app/components/workflow/context'
-import { useAppContext } from '@/context/app-context'
+import { userProfileQueryOptions } from '@/features/account-profile/client'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import useTimestamp from '@/hooks/use-timestamp'
import { fetchChatMessages, updateLogMessageAnnotations, updateLogMessageFeedbacks } from '@/service/log'
@@ -158,7 +159,10 @@ type IDetailPanel = {
function DetailPanel({ detail, onFeedback }: IDetailPanel) {
const MIN_ITEMS_FOR_SCROLL_LOADING = 8
const SCROLL_DEBOUNCE_MS = 200
- const { userProfile: { timezone } } = useAppContext()
+ const { data: timezone } = useQuery({
+ ...userProfileQueryOptions(),
+ select: data => data.profile.timezone ?? undefined,
+ })
const { formatTime } = useTimestamp()
const { onClose, appDetail } = useContext(DrawerContext)
const { currentLogItem, setCurrentLogItem, showMessageLogModal, setShowMessageLogModal, showPromptLogModal, setShowPromptLogModal, currentLogModalActiveTab } = useAppStore(useShallow((state: AppStoreState) => ({
diff --git a/web/app/components/app/workflow-log/index.tsx b/web/app/components/app/workflow-log/index.tsx
index 762a9897d8..ea7c6c178b 100644
--- a/web/app/components/app/workflow-log/index.tsx
+++ b/web/app/components/app/workflow-log/index.tsx
@@ -2,6 +2,7 @@
import type { FC } from 'react'
import type { App } from '@/types/app'
import { Pagination } from '@langgenius/dify-ui/pagination'
+import { useQuery } from '@tanstack/react-query'
import { useDebounce } from 'ahooks'
import dayjs from 'dayjs'
import timezone from 'dayjs/plugin/timezone'
@@ -13,7 +14,7 @@ import { useTranslation } from 'react-i18next'
import EmptyElement from '@/app/components/app/log/empty-element'
import Loading from '@/app/components/base/loading'
import { APP_PAGE_LIMIT } from '@/config'
-import { useAppContext } from '@/context/app-context'
+import { userProfileQueryOptions } from '@/features/account-profile/client'
import { useWorkflowLogs } from '@/service/use-log'
import Filter, { TIME_PERIOD_MAPPING } from './filter'
import List from './list'
@@ -33,7 +34,10 @@ export type QueryParam = {
const Logs: FC = ({ appDetail }) => {
const { t } = useTranslation()
- const { userProfile: { timezone } } = useAppContext()
+ const { data: timezone } = useQuery({
+ ...userProfileQueryOptions(),
+ select: data => data.profile.timezone ?? undefined,
+ })
const [queryParams, setQueryParams] = useState({ status: 'all', period: '2' })
const [currPage, setCurrPage] = React.useState(0)
const debouncedQueryParams = useDebounce(queryParams, { wait: 500 })
diff --git a/web/app/components/base/chat/chat-with-history/__tests__/chat-wrapper.spec.tsx b/web/app/components/base/chat/chat-with-history/__tests__/chat-wrapper.spec.tsx
index 563adbd59e..93fe71e59e 100644
--- a/web/app/components/base/chat/chat-with-history/__tests__/chat-wrapper.spec.tsx
+++ b/web/app/components/base/chat/chat-with-history/__tests__/chat-wrapper.spec.tsx
@@ -64,6 +64,13 @@ vi.mock('@/utils/model-config', () => ({
formatBooleanInputs: vi.fn((forms, inputs) => inputs),
}))
+vi.mock('@/hooks/use-timestamp', () => ({
+ default: () => ({
+ formatTime: (timestamp: number) => `formatted-${timestamp}`,
+ formatDate: (value: string) => `formatted-${value}`,
+ }),
+}))
+
type ChatHookReturn = ReturnType
const mockAppData = {
diff --git a/web/app/components/base/chat/chat/answer/__tests__/index.spec.tsx b/web/app/components/base/chat/chat/answer/__tests__/index.spec.tsx
index 3a9ddf4d5a..c5c5b163c8 100644
--- a/web/app/components/base/chat/chat/answer/__tests__/index.spec.tsx
+++ b/web/app/components/base/chat/chat/answer/__tests__/index.spec.tsx
@@ -10,6 +10,13 @@ vi.mock('../context', () => ({
})),
}))
+vi.mock('@/hooks/use-timestamp', () => ({
+ default: () => ({
+ formatTime: (timestamp: number) => `formatted-${timestamp}`,
+ formatDate: (value: string) => `formatted-${value}`,
+ }),
+}))
+
describe('Answer Component', () => {
const defaultProps = {
item: {
diff --git a/web/app/components/billing/apps-full-in-dialog/__tests__/index.spec.tsx b/web/app/components/billing/apps-full-in-dialog/__tests__/index.spec.tsx
index 899908b32a..db8c6bdca4 100644
--- a/web/app/components/billing/apps-full-in-dialog/__tests__/index.spec.tsx
+++ b/web/app/components/billing/apps-full-in-dialog/__tests__/index.spec.tsx
@@ -1,8 +1,9 @@
+import type { GetAccountProfileResponse } from '@dify/contracts/api/console/account/types.gen'
import type { Mock } from 'vitest'
import type { UsagePlanInfo } from '@/app/components/billing/type'
import type { AppContextValue } from '@/context/app-context'
import type { ProviderContextState } from '@/context/provider-context'
-import type { ICurrentWorkspace, LangGeniusVersionResponse, UserProfileResponse } from '@/models/common'
+import type { ICurrentWorkspace, LangGeniusVersionResponse } from '@/models/common'
import { render, screen } from '@testing-library/react'
import { Plan } from '@/app/components/billing/type'
import { mailToSupport } from '@/app/components/header/utils/util'
@@ -59,7 +60,7 @@ const buildProviderContext = (overrides: Partial = {}): Pr
})
const buildAppContext = (overrides: Partial = {}): AppContextValue => {
- const userProfile: UserProfileResponse = {
+ const userProfile: GetAccountProfileResponse = {
id: 'user-id',
name: 'Test User',
email: 'user@example.com',
diff --git a/web/app/components/datasets/metadata/base/__tests__/date-picker.spec.tsx b/web/app/components/datasets/metadata/base/__tests__/date-picker.spec.tsx
index 6b94a46e5a..5eab01b3f0 100644
--- a/web/app/components/datasets/metadata/base/__tests__/date-picker.spec.tsx
+++ b/web/app/components/datasets/metadata/base/__tests__/date-picker.spec.tsx
@@ -1,5 +1,7 @@
-import { fireEvent, render, screen } from '@testing-library/react'
+import type { ReactElement } from 'react'
+import { fireEvent, render as rtlRender, screen } from '@testing-library/react'
import { describe, expect, it, vi } from 'vitest'
+import { createAccountProfileQueryWrapper } from '@/test/account-profile-query'
import WrappedDatePicker from '../date-picker'
type TriggerArgs = {
@@ -44,6 +46,11 @@ vi.mock('@/hooks/use-timestamp', () => ({
}),
}))
+const render = (ui: ReactElement) => {
+ const Wrapper = createAccountProfileQueryWrapper()
+ return rtlRender(ui, { wrapper: Wrapper })
+}
+
describe('WrappedDatePicker', () => {
describe('Rendering', () => {
it('should render without crashing', () => {
diff --git a/web/app/components/datasets/metadata/base/date-picker.tsx b/web/app/components/datasets/metadata/base/date-picker.tsx
index 66b7e6fd1f..4306bb6330 100644
--- a/web/app/components/datasets/metadata/base/date-picker.tsx
+++ b/web/app/components/datasets/metadata/base/date-picker.tsx
@@ -4,11 +4,12 @@ import {
RiCalendarLine,
RiCloseCircleFill,
} from '@remixicon/react'
+import { useQuery } from '@tanstack/react-query'
import dayjs from 'dayjs'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import DatePicker from '@/app/components/base/date-and-time-picker/date-picker'
-import { useAppContext } from '@/context/app-context'
+import { userProfileQueryOptions } from '@/features/account-profile/client'
import useTimestamp from '@/hooks/use-timestamp'
type Props = {
@@ -24,7 +25,10 @@ const WrappedDatePicker = ({
onChange,
}: Props) => {
const { t } = useTranslation()
- const { userProfile: { timezone } } = useAppContext()
+ const { data: timezone } = useQuery({
+ ...userProfileQueryOptions(),
+ select: data => data.profile.timezone ?? undefined,
+ })
const { formatTime: formatTimestamp } = useTimestamp()
const handleDateChange = useCallback((date?: dayjs.Dayjs) => {
diff --git a/web/app/components/datasets/metadata/metadata-document/__tests__/index.spec.tsx b/web/app/components/datasets/metadata/metadata-document/__tests__/index.spec.tsx
index d51ef2be4d..bf155aa9ac 100644
--- a/web/app/components/datasets/metadata/metadata-document/__tests__/index.spec.tsx
+++ b/web/app/components/datasets/metadata/metadata-document/__tests__/index.spec.tsx
@@ -51,6 +51,12 @@ vi.mock('@/next/navigation', () => ({
}),
}))
+vi.mock('@/hooks/use-timestamp', () => ({
+ default: () => ({
+ formatTime: (timestamp: number) => `formatted-${timestamp}`,
+ }),
+}))
+
describe('MetadataDocument', () => {
const mockDocDetail = {
id: 'doc-1',
diff --git a/web/app/components/header/account-setting/language-page/__tests__/index.spec.tsx b/web/app/components/header/account-setting/language-page/__tests__/index.spec.tsx
index 5ab061dedd..edeb14cb1c 100644
--- a/web/app/components/header/account-setting/language-page/__tests__/index.spec.tsx
+++ b/web/app/components/header/account-setting/language-page/__tests__/index.spec.tsx
@@ -1,4 +1,4 @@
-import type { UserProfileResponse } from '@/models/common'
+import type { GetAccountProfileResponse } from '@dify/contracts/api/console/account/types.gen'
import { ToastHost } from '@langgenius/dify-ui/toast'
import { act, fireEvent, render, screen, waitFor, within } from '@testing-library/react'
import { languages } from '@/i18n-config/language'
@@ -9,7 +9,7 @@ import LanguagePage from '../index'
const mockRefresh = vi.fn()
const mockMutateUserProfile = vi.fn()
let mockLocale: string | undefined = 'en-US'
-let mockUserProfile: UserProfileResponse
+let mockUserProfile: GetAccountProfileResponse
vi.mock('@langgenius/dify-ui/select', async () => {
const React = await import('react')
@@ -89,7 +89,7 @@ vi.mock('@/i18n-config', () => ({
const updateUserProfileMock = vi.mocked(updateUserProfile)
-const createUserProfile = (overrides: Partial = {}): UserProfileResponse => ({
+const createUserProfile = (overrides: Partial = {}): GetAccountProfileResponse => ({
id: 'user-id',
name: 'Test User',
email: 'test@example.com',
diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-detail-panel/detail-header/__tests__/index.spec.tsx
index 24838fea59..278555c0bf 100644
--- a/web/app/components/plugins/plugin-detail-panel/detail-header/__tests__/index.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/detail-header/__tests__/index.spec.tsx
@@ -1,7 +1,9 @@
+import type { ReactElement } from 'react'
import type { PluginDetail } from '@/app/components/plugins/types'
-import { fireEvent, render, screen } from '@testing-library/react'
+import { fireEvent, render as rtlRender, screen } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { PluginCategoryEnum, PluginSource } from '@/app/components/plugins/types'
+import { createAccountProfileQueryWrapper } from '@/test/account-profile-query'
import DetailHeader from '../index'
const mockSetTargetVersion = vi.fn()
@@ -10,6 +12,11 @@ const mockHandleUpdate = vi.fn()
const mockHandleUpdatedFromMarketplace = vi.fn()
const mockHandleDelete = vi.fn()
+const render = (ui: ReactElement) => {
+ const Wrapper = createAccountProfileQueryWrapper({ timezone: 'UTC' })
+ return rtlRender(ui, { wrapper: Wrapper })
+}
+
vi.mock('@/context/app-context', () => ({
useAppContext: () => ({
userProfile: { timezone: 'UTC' },
diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header/index.tsx b/web/app/components/plugins/plugin-detail-panel/detail-header/index.tsx
index 8de6e1b911..2eb84b62af 100644
--- a/web/app/components/plugins/plugin-detail-panel/detail-header/index.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/detail-header/index.tsx
@@ -4,6 +4,7 @@ import type { PluginDetail } from '../../types'
import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
+import { useQuery } from '@tanstack/react-query'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
@@ -12,8 +13,8 @@ import { AuthCategory, PluginAuth } from '@/app/components/plugins/plugin-auth'
import OperationDropdown from '@/app/components/plugins/plugin-detail-panel/operation-dropdown'
import PluginVersionPicker from '@/app/components/plugins/update-plugin/plugin-version-picker'
import { API_PREFIX } from '@/config'
-import { useAppContext } from '@/context/app-context'
import { useGetLanguage, useLocale } from '@/context/i18n'
+import { userProfileQueryOptions } from '@/features/account-profile/client'
import useTheme from '@/hooks/use-theme'
import { useAllToolProviders } from '@/service/use-tools'
import { getMarketplaceUrl } from '@/utils/var'
@@ -72,7 +73,10 @@ const DetailHeader = ({
onUpdate,
}: Props) => {
const { t } = useTranslation()
- const { userProfile: { timezone } } = useAppContext()
+ const { data: timezone } = useQuery({
+ ...userProfileQueryOptions(),
+ select: data => data.profile.timezone ?? undefined,
+ })
const { theme } = useTheme()
const locale = useGetLanguage()
const currentLocale = useLocale()
diff --git a/web/app/components/plugins/reference-setting-modal/auto-update-setting/__tests__/index.spec.tsx b/web/app/components/plugins/reference-setting-modal/auto-update-setting/__tests__/index.spec.tsx
index 0e13be6d48..f04cec8992 100644
--- a/web/app/components/plugins/reference-setting-modal/auto-update-setting/__tests__/index.spec.tsx
+++ b/web/app/components/plugins/reference-setting-modal/auto-update-setting/__tests__/index.spec.tsx
@@ -1,13 +1,14 @@
import type { AutoUpdateConfig } from '../types'
import type { PluginDeclaration, PluginDetail } from '@/app/components/plugins/types'
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
-import { fireEvent, render, screen } from '@testing-library/react'
+import { QueryClientProvider } from '@tanstack/react-query'
+import { fireEvent, render as rtlRender, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import dayjs from 'dayjs'
import timezone from 'dayjs/plugin/timezone'
import utc from 'dayjs/plugin/utc'
import * as React from 'react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
+import { createAccountProfileQueryClient } from '@/test/account-profile-query'
import { PluginCategoryEnum, PluginSource } from '../../../types'
import { defaultValue } from '../config'
import AutoUpdateSetting from '../index'
@@ -291,21 +292,22 @@ const createMockAutoUpdateConfig = (overrides: Partial = {}):
// Helper Functions
// ================================
-const createQueryClient = () => new QueryClient({
- defaultOptions: {
- queries: {
- retry: false,
- },
- },
-})
+const createQueryClient = () => createAccountProfileQueryClient({ timezone: mockTimezone })
+
+const render = (ui: React.ReactElement) => {
+ const queryClient = createQueryClient()
+ const Wrapper = ({ children }: { children: React.ReactNode }) => (
+ {children}
+ )
+ return rtlRender(ui, { wrapper: Wrapper })
+}
const renderWithQueryClient = (ui: React.ReactElement) => {
const queryClient = createQueryClient()
- return render(
-
- {ui}
- ,
+ const Wrapper = ({ children }: { children: React.ReactNode }) => (
+ {children}
)
+ return rtlRender(ui, { wrapper: Wrapper })
}
// ================================
diff --git a/web/app/components/plugins/reference-setting-modal/auto-update-setting/index.tsx b/web/app/components/plugins/reference-setting-modal/auto-update-setting/index.tsx
index 30e646c0a1..bf22f26083 100644
--- a/web/app/components/plugins/reference-setting-modal/auto-update-setting/index.tsx
+++ b/web/app/components/plugins/reference-setting-modal/auto-update-setting/index.tsx
@@ -4,6 +4,7 @@ import type { AutoUpdateConfig } from './types'
import type { TriggerParams } from '@/app/components/base/date-and-time-picker/types'
import { cn } from '@langgenius/dify-ui/cn'
import { RiTimeLine } from '@remixicon/react'
+import { useQuery } from '@tanstack/react-query'
import * as React from 'react'
import { useCallback, useMemo } from 'react'
import { Trans, useTranslation } from 'react-i18next'
@@ -11,8 +12,8 @@ import TimePicker from '@/app/components/base/date-and-time-picker/time-picker'
import { convertTimezoneToOffsetStr } from '@/app/components/base/date-and-time-picker/utils/dayjs'
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card'
-import { useAppContext } from '@/context/app-context'
import { useModalContextSelector } from '@/context/modal-context'
+import { userProfileQueryOptions } from '@/features/account-profile/client'
import Label from '../label'
import PluginsPicker from './plugins-picker'
import StrategyPicker from './strategy-picker'
@@ -47,7 +48,10 @@ const AutoUpdateSetting: FC = ({
onChange,
}) => {
const { t } = useTranslation()
- const { userProfile: { timezone } } = useAppContext()
+ const { data: timezone } = useQuery({
+ ...userProfileQueryOptions(),
+ select: data => data.profile.timezone ?? undefined,
+ })
const {
strategy_setting,
diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/__tests__/integration.spec.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/__tests__/integration.spec.tsx
index 1c7513d13f..aec46ee2c7 100644
--- a/web/app/components/workflow/nodes/knowledge-retrieval/__tests__/integration.spec.tsx
+++ b/web/app/components/workflow/nodes/knowledge-retrieval/__tests__/integration.spec.tsx
@@ -12,6 +12,7 @@ import {
DatasetPermission,
DataSourceType,
} from '@/models/datasets'
+import { AccountProfileQueryProvider, createAccountProfileQueryClient } from '@/test/account-profile-query'
import { RETRIEVE_METHOD, RETRIEVE_TYPE } from '@/types/app'
import { DatasetsDetailContext } from '../../../datasets-detail-store/provider'
import { createDatasetsDetailStore } from '../../../datasets-detail-store/store'
@@ -583,25 +584,28 @@ describe('knowledge-retrieval path', () => {
const onDateChange = vi.fn()
const onRemoveCondition = vi.fn()
const onUpdateCondition = vi.fn()
+ const queryClient = createAccountProfileQueryClient({ timezone: 'UTC' })
const { container } = render(
-
-
-
-
-
,
+
+
+
+
+
+
+ ,
)
await user.click(screen.getAllByRole('button', { name: /contains/i })[0]!)
diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-date.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-date.tsx
index 5b27963d98..57c1f40309 100644
--- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-date.tsx
+++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-date.tsx
@@ -4,11 +4,12 @@ import {
RiCalendarLine,
RiCloseCircleFill,
} from '@remixicon/react'
+import { useQuery } from '@tanstack/react-query'
import dayjs from 'dayjs'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import DatePicker from '@/app/components/base/date-and-time-picker/date-picker'
-import { useAppContext } from '@/context/app-context'
+import { userProfileQueryOptions } from '@/features/account-profile/client'
type ConditionDateProps = {
value?: number
@@ -19,7 +20,10 @@ const ConditionDate = ({
onChange,
}: ConditionDateProps) => {
const { t } = useTranslation()
- const { userProfile: { timezone } } = useAppContext()
+ const { data: timezone } = useQuery({
+ ...userProfileQueryOptions(),
+ select: data => data.profile.timezone ?? undefined,
+ })
const handleDateChange = useCallback((date?: dayjs.Dayjs) => {
if (date)
diff --git a/web/app/components/workflow/nodes/trigger-schedule/__tests__/use-config.spec.ts b/web/app/components/workflow/nodes/trigger-schedule/__tests__/use-config.spec.ts
index 40b4c925c6..c334f0e952 100644
--- a/web/app/components/workflow/nodes/trigger-schedule/__tests__/use-config.spec.ts
+++ b/web/app/components/workflow/nodes/trigger-schedule/__tests__/use-config.spec.ts
@@ -3,6 +3,7 @@ import { renderHook } from '@testing-library/react'
import { useNodesReadOnly } from '@/app/components/workflow/hooks'
import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
import { useAppContext } from '@/context/app-context'
+import { createAccountProfileQueryWrapper } from '@/test/account-profile-query'
import { BlockEnum } from '../../../types'
import useConfig from '../use-config'
@@ -60,7 +61,7 @@ describe('trigger-schedule/use-config', () => {
frequency: undefined,
timezone: undefined,
visual_config: undefined,
- })))
+ })), { wrapper: createAccountProfileQueryWrapper() })
expect(mockUseNodeCrud).toHaveBeenCalledWith('schedule-node', expect.objectContaining({
mode: 'visual',
@@ -78,7 +79,7 @@ describe('trigger-schedule/use-config', () => {
it('updates visual mode configuration and clears cron expression when needed', () => {
const { result } = renderHook(() => useConfig('schedule-node', createData({
cron_expression: '0 0 * * *',
- })))
+ })), { wrapper: createAccountProfileQueryWrapper() })
result.current.handleModeChange('cron')
result.current.handleFrequencyChange('hourly')
@@ -107,7 +108,7 @@ describe('trigger-schedule/use-config', () => {
})
it('switches to raw cron mode and clears visual schedule fields', () => {
- const { result } = renderHook(() => useConfig('schedule-node', createData()))
+ const { result } = renderHook(() => useConfig('schedule-node', createData()), { wrapper: createAccountProfileQueryWrapper() })
result.current.handleCronExpressionChange('*/15 * * * *')
diff --git a/web/app/components/workflow/nodes/trigger-schedule/use-config.ts b/web/app/components/workflow/nodes/trigger-schedule/use-config.ts
index a3e5959f2e..128f87b2ed 100644
--- a/web/app/components/workflow/nodes/trigger-schedule/use-config.ts
+++ b/web/app/components/workflow/nodes/trigger-schedule/use-config.ts
@@ -1,27 +1,31 @@
import type { ScheduleFrequency, ScheduleMode, ScheduleTriggerNodeType } from './types'
+import { useQuery } from '@tanstack/react-query'
import { useCallback, useMemo } from 'react'
import { useNodesReadOnly } from '@/app/components/workflow/hooks'
import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
-import { useAppContext } from '@/context/app-context'
+import { userProfileQueryOptions } from '@/features/account-profile/client'
import { getDefaultVisualConfig } from './constants'
const useConfig = (id: string, payload: ScheduleTriggerNodeType) => {
const { nodesReadOnly: readOnly } = useNodesReadOnly()
- const { userProfile } = useAppContext()
+ const { data: timezone } = useQuery({
+ ...userProfileQueryOptions(),
+ select: data => data.profile.timezone ?? undefined,
+ })
const frontendPayload = useMemo(() => {
return {
...payload,
mode: payload.mode || 'visual',
frequency: payload.frequency || 'daily',
- timezone: payload.timezone || userProfile.timezone || 'UTC',
+ timezone: payload.timezone || timezone || 'UTC',
visual_config: {
...getDefaultVisualConfig(),
...payload.visual_config,
},
}
- }, [payload, userProfile.timezone])
+ }, [payload, timezone])
const { inputs, setInputs } = useNodeCrud(id, frontendPayload)
diff --git a/web/app/education-apply/hooks.ts b/web/app/education-apply/hooks.ts
index 0c7802b592..764ab1a090 100644
--- a/web/app/education-apply/hooks.ts
+++ b/web/app/education-apply/hooks.ts
@@ -1,4 +1,5 @@
import type { SearchParams } from './types'
+import { useQuery } from '@tanstack/react-query'
import { useDebounceFn } from 'ahooks'
import dayjs from 'dayjs'
import timezone from 'dayjs/plugin/timezone'
@@ -9,9 +10,9 @@ import {
useState,
} from 'react'
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
-import { useAppContext } from '@/context/app-context'
import { useModalContextSelector } from '@/context/modal-context'
import { useProviderContext } from '@/context/provider-context'
+import { userProfileQueryOptions } from '@/features/account-profile/client'
import { useLocalStorage } from '@/hooks/use-local-storage'
import { useRouter, useSearchParams } from '@/next/navigation'
import { useEducationAutocomplete, useEducationVerify } from '@/service/use-education'
@@ -81,7 +82,10 @@ const isExpired = (expireAt?: number, timezone?: string) => {
const useEducationReverifyNotice = ({
onNotice,
}: useEducationReverifyNoticeParams) => {
- const { userProfile: { timezone } } = useAppContext()
+ const { data: timezone } = useQuery({
+ ...userProfileQueryOptions(),
+ select: data => data.profile.timezone ?? undefined,
+ })
// const [educationInfo, setEducationInfo] = useState<{ is_student: boolean, allow_refresh: boolean, expire_at: number | null } | null>(null)
// const isLoading = !educationInfo
const { educationAccountExpireAt, allowRefreshEducationVerify, isLoadingEducationAccountInfo: isLoading } = useProviderContext()
diff --git a/web/context/app-context-provider.tsx b/web/context/app-context-provider.tsx
index 6f2b85e8ed..23bdb12dd6 100644
--- a/web/context/app-context-provider.tsx
+++ b/web/context/app-context-provider.tsx
@@ -1,8 +1,9 @@
'use client'
+import type { GetAccountProfileResponse } from '@dify/contracts/api/console/account/types.gen'
import type { PostWorkspacesCurrentResponse } from '@dify/contracts/api/console/workspaces/types.gen'
import type { FC, ReactNode } from 'react'
-import type { ICurrentWorkspace, LangGeniusVersionResponse, UserProfileResponse } from '@/models/common'
+import type { ICurrentWorkspace, LangGeniusVersionResponse } from '@/models/common'
import { useQuery, useQueryClient, useSuspenseQuery } from '@tanstack/react-query'
import { useCallback, useEffect, useMemo } from 'react'
import { setUserId, setUserProperties } from '@/app/components/base/amplitude'
@@ -72,7 +73,7 @@ export const AppContextProvider: FC = ({ children }) =>
!systemFeatures.branding.enabled,
)
- const userProfile = useMemo(() => userProfileResp?.profile || userProfilePlaceholder, [userProfileResp?.profile])
+ const userProfile = useMemo(() => userProfileResp?.profile || userProfilePlaceholder, [userProfileResp?.profile])
const currentWorkspace = useMemo(() => normalizeCurrentWorkspace(currentWorkspaceResp), [currentWorkspaceResp])
const langGeniusVersionInfo = useMemo(() => {
if (!userProfileResp?.meta?.currentVersion || !langGeniusVersionQuery.data)
diff --git a/web/context/app-context.ts b/web/context/app-context.ts
index 298e213e7d..50df95e71e 100644
--- a/web/context/app-context.ts
+++ b/web/context/app-context.ts
@@ -1,11 +1,12 @@
'use client'
-import type { ICurrentWorkspace, LangGeniusVersionResponse, UserProfileResponse } from '@/models/common'
+import type { GetAccountProfileResponse } from '@dify/contracts/api/console/account/types.gen'
+import type { ICurrentWorkspace, LangGeniusVersionResponse } from '@/models/common'
import { noop } from 'es-toolkit/function'
import { createContext, useContext, useContextSelector } from 'use-context-selector'
export type AppContextValue = {
- userProfile: UserProfileResponse
+ userProfile: GetAccountProfileResponse
mutateUserProfile: VoidFunction
currentWorkspace: ICurrentWorkspace
isCurrentWorkspaceManager: boolean
diff --git a/web/contract/console/account.ts b/web/contract/console/account.ts
deleted file mode 100644
index 5e8e27e015..0000000000
--- a/web/contract/console/account.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { type } from '@orpc/contract'
-import { base } from '../base'
-
-export type AccountProfileResponse = {
- id: string
- name: string
- email: string
- avatar: string
- avatar_url: string | null
- is_password_set: boolean
- interface_language?: string
- interface_theme?: string
- timezone?: string
- last_login_at?: string
- last_active_at?: string
- last_login_ip?: string
- created_at?: string
-}
-
-export const accountProfileContract = base
- .route({
- path: '/account/profile',
- method: 'GET',
- })
- .output(type())
-
-export const accountAvatarContract = base
- .route({
- path: '/account/avatar',
- method: 'GET',
- })
- .input(type<{
- query: {
- avatar: string
- }
- }>())
- .output(type<{ avatar_url: string }>())
diff --git a/web/contract/router.ts b/web/contract/router.ts
index 58395b32c9..d03b2f2271 100644
--- a/web/contract/router.ts
+++ b/web/contract/router.ts
@@ -1,7 +1,6 @@
import type { InferContractRouterInputs } from '@orpc/contract'
import { contract as communityContract } from '@dify/contracts/api/console/orpc.gen'
import { contract as enterpriseContract } from '@dify/contracts/enterprise/orpc.gen'
-import { accountAvatarContract, accountProfileContract } from './console/account'
import { appDeleteContract, appListContract, workflowOnlineUsersContract } from './console/apps'
import { bindPartnerStackContract, invoicesContract } from './console/billing'
import {
@@ -71,14 +70,6 @@ export type MarketPlaceInputs = InferContractRouterInputs ({
cookies: () => cookiesMock(),
}))
-const createProfile = (overrides: Partial = {}): AccountProfileResponse => ({
+const createProfile = (overrides: Partial = {}): GetAccountProfileResponse => ({
id: 'account-id',
name: 'Dify User',
email: 'user@example.com',
diff --git a/web/features/account-profile/client.ts b/web/features/account-profile/client.ts
index 128cd5b20e..fd56d8827b 100644
--- a/web/features/account-profile/client.ts
+++ b/web/features/account-profile/client.ts
@@ -1,4 +1,4 @@
-import type { AccountProfileResponse } from '@/contract/console/account'
+import type { GetAccountProfileResponse } from '@dify/contracts/api/console/account/types.gen'
import { queryOptions } from '@tanstack/react-query'
import { IS_DEV } from '@/config'
// eslint-disable-next-line no-restricted-imports
@@ -6,7 +6,7 @@ import { get } from '@/service/base'
import { consoleQuery } from '@/service/client'
export type UserProfileWithMeta = {
- profile: AccountProfileResponse
+ profile: GetAccountProfileResponse
meta: {
currentVersion: string | null
currentEnv: string | null
@@ -24,7 +24,7 @@ export const userProfileQueryOptions = () =>
needAllResponseContent: true,
silent: true,
})
- const profile: AccountProfileResponse = await response.clone().json()
+ const profile: GetAccountProfileResponse = await response.clone().json()
return {
profile,
meta: {
diff --git a/web/features/account-profile/server.ts b/web/features/account-profile/server.ts
index 77adbcf2e5..90aba7cc5a 100644
--- a/web/features/account-profile/server.ts
+++ b/web/features/account-profile/server.ts
@@ -1,5 +1,5 @@
+import type { GetAccountProfileResponse } from '@dify/contracts/api/console/account/types.gen'
import type { UserProfileWithMeta } from './client'
-import type { AccountProfileResponse } from '@/contract/console/account'
import { queryOptions } from '@tanstack/react-query'
import { getServerConsoleRequestHeaders, resolveServerConsoleApiUrl, serverConsoleQuery } from '@/service/server'
@@ -22,7 +22,7 @@ export const serverUserProfileQueryOptions = () =>
if (!response.ok)
throw response
- const profile: AccountProfileResponse = await response.clone().json()
+ const profile: GetAccountProfileResponse = await response.clone().json()
return {
profile,
meta: {
diff --git a/web/hooks/use-timestamp.spec.ts b/web/hooks/use-timestamp.spec.ts
index e78211bbb1..22fb5f5016 100644
--- a/web/hooks/use-timestamp.spec.ts
+++ b/web/hooks/use-timestamp.spec.ts
@@ -1,4 +1,5 @@
import { renderHook } from '@testing-library/react'
+import { createAccountProfileQueryWrapper } from '@/test/account-profile-query'
import useTimestamp from './use-timestamp'
vi.mock('@/context/app-context', () => ({
@@ -23,7 +24,7 @@ vi.mock('@/context/app-context', () => ({
describe('useTimestamp', () => {
describe('formatTime', () => {
it('should format unix timestamp correctly', () => {
- const { result } = renderHook(() => useTimestamp())
+ const { result } = renderHook(() => useTimestamp(), { wrapper: createAccountProfileQueryWrapper() })
const timestamp = 1704132000
expect(result.current.formatTime(timestamp, 'YYYY-MM-DD HH:mm:ss'))
@@ -31,7 +32,7 @@ describe('useTimestamp', () => {
})
it('should format with different patterns', () => {
- const { result } = renderHook(() => useTimestamp())
+ const { result } = renderHook(() => useTimestamp(), { wrapper: createAccountProfileQueryWrapper() })
const timestamp = 1704132000
expect(result.current.formatTime(timestamp, 'MM/DD/YYYY'))
@@ -44,7 +45,7 @@ describe('useTimestamp', () => {
describe('formatDate', () => {
it('should format date string correctly', () => {
- const { result } = renderHook(() => useTimestamp())
+ const { result } = renderHook(() => useTimestamp(), { wrapper: createAccountProfileQueryWrapper() })
const dateString = '2024-01-01T12:00:00Z'
expect(result.current.formatDate(dateString, 'YYYY-MM-DD HH:mm:ss'))
@@ -52,7 +53,7 @@ describe('useTimestamp', () => {
})
it('should format with different patterns', () => {
- const { result } = renderHook(() => useTimestamp())
+ const { result } = renderHook(() => useTimestamp(), { wrapper: createAccountProfileQueryWrapper() })
const dateString = '2024-01-01T12:00:00Z'
expect(result.current.formatDate(dateString, 'MM/DD/YYYY'))
diff --git a/web/hooks/use-timestamp.ts b/web/hooks/use-timestamp.ts
index 05afa8e178..a872b4c852 100644
--- a/web/hooks/use-timestamp.ts
+++ b/web/hooks/use-timestamp.ts
@@ -1,15 +1,19 @@
'use client'
+import { useQuery } from '@tanstack/react-query'
import dayjs from 'dayjs'
import timezone from 'dayjs/plugin/timezone'
import utc from 'dayjs/plugin/utc'
import { useCallback } from 'react'
-import { useAppContext } from '@/context/app-context'
+import { userProfileQueryOptions } from '@/features/account-profile/client'
dayjs.extend(utc)
dayjs.extend(timezone)
const useTimestamp = () => {
- const { userProfile: { timezone } } = useAppContext()
+ const { data: timezone } = useQuery({
+ ...userProfileQueryOptions(),
+ select: data => data.profile.timezone ?? undefined,
+ })
const formatTime = useCallback((value: number, format: string) => {
return dayjs.unix(value).tz(timezone).format(format)
diff --git a/web/models/common.ts b/web/models/common.ts
index 2b3acd1d2f..3fe550a701 100644
--- a/web/models/common.ts
+++ b/web/models/common.ts
@@ -1,3 +1,4 @@
+import type { GetAccountProfileResponse } from '@dify/contracts/api/console/account/types.gen'
import type { I18nText } from '@/i18n-config/language'
import type { Model } from '@/types/app'
@@ -18,24 +19,8 @@ export type InitValidateStatusResponse = {
status: 'finished' | 'not_started'
}
-export type UserProfileResponse = {
- id: string
- name: string
- email: string
- avatar: string
- avatar_url: string | null
- is_password_set: boolean
- interface_language?: string
- interface_theme?: string
- timezone?: string
- last_login_at?: string
- last_active_at?: string
- last_login_ip?: string
- created_at?: string
-}
-
export type UserProfileOriginResponse = {
- json: () => Promise
+ json: () => Promise
bodyUsed: boolean
headers: any
}
@@ -50,8 +35,11 @@ export type LangGeniusVersionResponse = {
current_env: string
}
-export type Member = Pick & {
+export type Member = Pick & {
avatar: string
+ last_login_at?: string
+ last_active_at?: string
+ created_at?: string
status: 'pending' | 'active' | 'banned' | 'closed'
role: 'owner' | 'admin' | 'editor' | 'normal' | 'dataset_operator'
}
diff --git a/web/service/common.ts b/web/service/common.ts
index 3f0ae66a9b..7c068eabe1 100644
--- a/web/service/common.ts
+++ b/web/service/common.ts
@@ -371,5 +371,5 @@ export const checkEmailExisted = (body: { email: string }): Promise => {
const { consoleClient } = await import('./client')
- return consoleClient.account.avatar({ query: { avatar } })
+ return consoleClient.account.avatar.get({ query: { avatar } })
}
diff --git a/web/test/account-profile-query.ts b/web/test/account-profile-query.ts
new file mode 100644
index 0000000000..9bb5ca7861
--- /dev/null
+++ b/web/test/account-profile-query.ts
@@ -0,0 +1,70 @@
+import type { GetAccountProfileResponse } from '@dify/contracts/api/console/account/types.gen'
+import type { QueryClient } from '@tanstack/react-query'
+import type { ReactNode } from 'react'
+import type { UserProfileWithMeta } from '@/features/account-profile/client'
+import { QueryClientProvider, QueryClient as TanStackQueryClient } from '@tanstack/react-query'
+import { createElement } from 'react'
+import { userProfileQueryOptions } from '@/features/account-profile/client'
+
+const createMockAccountProfile = (
+ overrides: Partial = {},
+): GetAccountProfileResponse => ({
+ id: 'user-1',
+ name: 'Test User',
+ email: 'test@dify.ai',
+ avatar: '',
+ avatar_url: null,
+ is_password_set: false,
+ timezone: 'Asia/Shanghai',
+ ...overrides,
+})
+
+const createMockUserProfileResponse = (
+ profile: Partial = {},
+): UserProfileWithMeta => ({
+ profile: createMockAccountProfile(profile),
+ meta: {
+ currentVersion: null,
+ currentEnv: null,
+ },
+})
+
+export const createAccountProfileQueryClient = (
+ profile: Partial = {},
+) => {
+ const queryClient = new TanStackQueryClient({
+ defaultOptions: {
+ queries: {
+ retry: false,
+ staleTime: Number.POSITIVE_INFINITY,
+ },
+ },
+ })
+
+ queryClient.setQueryData(
+ userProfileQueryOptions().queryKey,
+ createMockUserProfileResponse(profile),
+ )
+
+ return queryClient
+}
+
+export const createAccountProfileQueryWrapper = (
+ profile: Partial = {},
+) => {
+ const queryClient = createAccountProfileQueryClient(profile)
+
+ return function AccountProfileQueryWrapper({ children }: { children: ReactNode }) {
+ return createElement(QueryClientProvider, { client: queryClient }, children)
+ }
+}
+
+export function AccountProfileQueryProvider({
+ children,
+ queryClient,
+}: {
+ children: ReactNode
+ queryClient: QueryClient
+}) {
+ return createElement(QueryClientProvider, { client: queryClient }, children)
+}