This commit is contained in:
devin-ai-integration[bot] 2026-10-04 01:43:49 -07:00 • committed by GitHub
commit fb5ff7f4cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 334 additions and 39 deletions

View file

@ -5,6 +5,7 @@ import prettier from "eslint-config-prettier/flat";
import unusedImports from "eslint-plugin-unused-imports";
import testingLibrary from "eslint-plugin-testing-library";
import jestDom from "eslint-plugin-jest-dom";
import pluginQuery from "@tanstack/eslint-plugin-query";
import local from "./scripts/eslint-rules/index.mjs";
const eslintConfig = [
@ -14,10 +15,12 @@ const eslintConfig = [
js.configs.recommended,
...tseslint.configs.recommended,
...nextCoreWebVitals,
...pluginQuery.configs["flat/recommended"],
prettier,
{
plugins: { "unused-imports": unusedImports, local },
rules: {
"@tanstack/query/exhaustive-deps": ["error", { allowlist: { variables: ["accessToken", "apiClient", "demo"] } }],
"unused-imports/no-unused-imports": "error",
"local/no-large-inline-object-arg": "warn",
"local/no-long-condition-chain": "warn",

View file

@ -60,6 +60,7 @@
"@eslint/js": "9.39.2",
"@tailwindcss/forms": "0.5.11",
"@tailwindcss/postcss": "4.3.2",
"@tanstack/eslint-plugin-query": "5.104.0",
"@testing-library/dom": "10.4.1",
"@testing-library/jest-dom": "6.9.1",
"@testing-library/react": "16.3.2",
@ -3440,6 +3441,183 @@
"url": "https://github.com/sponsors/tannerlinsley"
}
},
"node_modules/@tanstack/eslint-plugin-query": {
"version": "5.104.0",
"resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.104.0.tgz",
"integrity": "sha512-WHStlNTKraESaAjeuZ0zJr+t3ZigZgAJ/FQX1sdLGSF+U0vXlHgcAoPqm27VixMRyXaFHCx9xBJiUmPNcxMpjQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/utils": "^8.70.1"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/tannerlinsley"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": "^5.6.0 || ^6.0.0 || ^7.0.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/project-service": {
"version": "8.71.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.71.0.tgz",
"integrity": "sha512-aABjw5rjBacYONVPaPiWOCjJu0vEF4a25iQuodlmQYL1trtLZ0X/y+2Vzl3BKI1odM4LnwLE1oUDXYp1wzx1TQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/tsconfig-utils": "^8.71.0",
"@typescript-eslint/types": "^8.71.0",
"debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/scope-manager": {
"version": "8.71.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.71.0.tgz",
"integrity": "sha512-gWF0BhUcnjZxSpLE8ngS/59n2SB0J3YqRxvX1+2aoRJk9hNtHSLOV+TcarFiOr5ipXm3yc1QrI4c9YZc8zyCxw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.71.0",
"@typescript-eslint/visitor-keys": "8.71.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/tsconfig-utils": {
"version": "8.71.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.71.0.tgz",
"integrity": "sha512-Z1UlWHADEK2Mlb9NpWfDeSjqoZ5EyrOv4R3eQpbkzqn/EwaIdOpXXupEA1+0ZIOSJSZZDBHG0BrQyN8zUG6Pwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/types": {
"version": "8.71.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.71.0.tgz",
"integrity": "sha512-cJ4OoxPGWvFnBTnSZyaU+qJzGTqPTGJY+gDchj6cRyLRdmIdt4rcsE4twj+zPfrNiWuVi38wijHzShL++Z9atQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/typescript-estree": {
"version": "8.71.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.71.0.tgz",
"integrity": "sha512-PEEF4G5sLLWAS5BpPrUvms4ySZkiBQQZM4z+3ReI46axK5Vqr/vXBQatJQIZZOYdGyPUAKTtsrWzpqKuU+3DEw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/project-service": "8.71.0",
"@typescript-eslint/tsconfig-utils": "8.71.0",
"@typescript-eslint/types": "8.71.0",
"@typescript-eslint/visitor-keys": "8.71.0",
"debug": "^4.4.3",
"minimatch": "^10.2.2",
"semver": "^7.7.3",
"tinyglobby": "^0.2.15",
"ts-api-utils": "^2.5.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/utils": {
"version": "8.71.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.71.0.tgz",
"integrity": "sha512-pKR/tEMVrXZG23UFKUn5BQf3zfmfk7KQceI2cGzywZ5nxM5Eu3hEJU1utjWzydtzBbcJAQhHN8iPCxobHpPcZQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@typescript-eslint/scope-manager": "8.71.0",
"@typescript-eslint/types": "8.71.0",
"@typescript-eslint/typescript-estree": "8.71.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.1.0"
}
},
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/visitor-keys": {
"version": "8.71.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.71.0.tgz",
"integrity": "sha512-8eQ9R218XORK+KLosnf4bu/QsUXvUyVwTbArg7/0NMB1Pu87OJKvj4nhFblkYE8gQV73mW1dx1ptlPCkwRGa7A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "8.71.0",
"eslint-visitor-keys": "^5.0.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@tanstack/eslint-plugin-query/node_modules/eslint-visitor-keys": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
"integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@tanstack/pacer": {
"version": "0.21.1",
"resolved": "https://registry.npmjs.org/@tanstack/pacer/-/pacer-0.21.1.tgz",

View file

@ -77,6 +77,7 @@
"@eslint/js": "9.39.2",
"@tailwindcss/forms": "0.5.11",
"@tailwindcss/postcss": "4.3.2",
"@tanstack/eslint-plugin-query": "5.104.0",
"@testing-library/dom": "10.4.1",
"@testing-library/jest-dom": "6.9.1",
"@testing-library/react": "16.3.2",

View file

@ -40,7 +40,7 @@ export function GuardrailDetail({ guardrailId, onBack, accessToken = null, start
error: detailError,
} = useGuardrailsUsageDetail(guardrailId, { accessToken, startDate, endDate });
const { data: logsData, isLoading: logsLoading } = useQuery({
queryKey: ["guardrails-usage-logs", guardrailId, logsPage, logsPageSize],
queryKey: ["guardrails-usage-logs", guardrailId, logsPage, logsPageSize, startDate, endDate],
queryFn: () =>
getGuardrailsUsageLogs(accessToken!, {
guardrailId,

View file

@ -52,7 +52,14 @@ export const useMCPServerHealth = () => {
);
return {
...query,
data: query.data,
error: query.error,
isError: query.isError,
isFetching: query.isFetching,
isLoading: query.isLoading,
isSuccess: query.isSuccess,
refetch: query.refetch,
status: query.status,
recheckServerHealth,
recheckingServerIds,
};

View file

@ -621,6 +621,30 @@ describe("useSelectedTeamModels", () => {
expect(modelAvailableCall).toHaveBeenCalledTimes(1);
});
it("keeps team model results in separate cache entries", async () => {
const teamOneResponse = { data: [{ ...mockProxyModel, id: "team-1-model" }] };
const teamTwoResponse = { data: [{ ...mockProxyModel, id: "team-2-model" }] };
(modelAvailableCall as any).mockResolvedValueOnce(teamOneResponse).mockResolvedValueOnce(teamTwoResponse);
const { result, rerender } = renderHook(({ teamID }: { teamID: string }) => useSelectedTeamModels(teamID), {
wrapper,
initialProps: { teamID: "team-1" },
});
await waitFor(() => expect(result.current.data).toEqual(teamOneResponse));
rerender({ teamID: "team-2" });
await waitFor(() => expect(result.current.data).toEqual(teamTwoResponse));
expect(modelAvailableCall).toHaveBeenCalledTimes(2);
expect(
queryClient
.getQueriesData<AllProxyModelsResponse>({ queryKey: ["selectedTeamModels", "list"] })
.map(([, data]) => data?.data[0]?.id)
.sort(),
).toEqual(["team-1-model", "team-2-model"]);
});
it("should handle error when modelAvailableCall fails", async () => {
const errorMessage = "Failed to fetch team models";
const testError = new Error(errorMessage);

View file

@ -247,7 +247,12 @@ export const useModelHub = () => {
export const useAllProxyModels = () => {
const { accessToken, userId, userRole } = useAuthorized();
return useQuery<AllProxyModelsResponse>({
queryKey: allProxyModelsKeys.list({}),
queryKey: allProxyModelsKeys.list({
filters: {
userId: userId ?? "",
userRole: userRole ?? "",
},
}),
queryFn: async () => await modelAvailableCall(accessToken!, userId!, userRole!, true, null, true, false, "expand"),
enabled: Boolean(accessToken && userId && userRole),
});
@ -256,7 +261,12 @@ export const useAllProxyModels = () => {
export const useUserModels = (): UseQueryResult<string[]> => {
const { accessToken, userId, userRole } = useAuthorized();
return useQuery<string[]>({
queryKey: userModelsKeys.list({}),
queryKey: userModelsKeys.list({
filters: {
userId: userId ?? "",
userRole: userRole ?? "",
},
}),
queryFn: async () => {
const response = await modelAvailableCall(accessToken!, userId!, userRole!);
return response["data"].map((model: { id: string }) => model.id);
@ -268,7 +278,13 @@ export const useUserModels = (): UseQueryResult<string[]> => {
export const useSelectedTeamModels = (teamID: string | null) => {
const { accessToken, userId, userRole } = useAuthorized();
return useQuery<AllProxyModelsResponse>({
queryKey: selectedTeamModelsKeys.list({}),
queryKey: selectedTeamModelsKeys.list({
filters: {
teamID: teamID ?? "",
userId: userId ?? "",
userRole: userRole ?? "",
},
}),
queryFn: async () => await modelAvailableCall(accessToken!, userId!, userRole!, true, teamID!),
enabled: Boolean(accessToken && userId && userRole && teamID),
});

View file

@ -116,6 +116,36 @@ describe("useTeams", () => {
expect(fetchTeams).toHaveBeenCalledTimes(1);
});
it("keeps team list results scoped to the authorized user and role", async () => {
const otherUserTeams = [mockTeams[1]];
vi.mocked(fetchTeams).mockResolvedValueOnce(mockTeams).mockResolvedValueOnce(otherUserTeams);
const { result, rerender } = renderHook(() => useTeams(), { wrapper });
await waitFor(() => expect(result.current.data).toEqual(mockTeams));
mockUseAuthorized.mockReturnValue({
accessToken: "test-access-token",
userId: "another-user-id",
userRole: "Internal User",
token: "test-token",
userEmail: "test@example.com",
premiumUser: false,
disabledPersonalKeyCreation: null,
showSSOBanner: false,
});
rerender();
await waitFor(() => expect(result.current.data).toEqual(otherUserTeams));
expect(fetchTeams).toHaveBeenCalledTimes(2);
expect(
queryClient
.getQueriesData<Team[]>({ queryKey: ["teams", "list"] })
.map(([, teams]) => teams?.[0]?.team_id)
.sort(),
).toEqual(["team-1", "team-2"]);
});
it("should handle error when fetchTeams fails", async () => {
const errorMessage = "Failed to fetch teams";
const testError = new Error(errorMessage);
@ -398,7 +428,10 @@ describe("useTeam", () => {
});
it("should use initialData from teams list cache when available", async () => {
queryClient.setQueryData(["teams", "list", { params: {} }], mockTeams);
queryClient.setQueryData(
["teams", "list", { params: { filters: { userId: "test-user-id", userRole: "Admin" } } }],
mockTeams,
);
const { result } = renderHook(() => useTeam("team-1"), { wrapper });
@ -413,7 +446,10 @@ describe("useTeam", () => {
});
it("should return undefined initialData when teamId is not in cache", () => {
queryClient.setQueryData(["teams", "list", { params: {} }], mockTeams);
queryClient.setQueryData(
["teams", "list", { params: { filters: { userId: "test-user-id", userRole: "Admin" } } }],
mockTeams,
);
const { result } = renderHook(() => useTeam("non-existent-team"), { wrapper });

View file

@ -121,7 +121,12 @@ export const teamKeys = createQueryKeys("teams");
export const useTeams = (): UseQueryResult<Team[]> => {
const { accessToken, userId, userRole } = useAuthorized();
return useQuery<Team[]>({
queryKey: teamKeys.list({}),
queryKey: teamKeys.list({
filters: {
userId: userId ?? "",
userRole: userRole ?? "",
},
}),
queryFn: async () => await fetchTeams(accessToken!, userId, userRole, null),
enabled: Boolean(accessToken),
});
@ -159,7 +164,7 @@ export const useAllTeams = (): UseQueryResult<Team[]> => {
};
export const useTeam = (teamId?: string) => {
const { accessToken } = useAuthorized();
const { accessToken, userId, userRole } = useAuthorized();
const queryClient = useQueryClient();
return useQuery<Team>({
queryKey: teamKeys.detail(teamId!),
@ -177,7 +182,14 @@ export const useTeam = (teamId?: string) => {
initialData: () => {
if (!teamId) return undefined;
const teams = queryClient.getQueryData<Team[]>(teamKeys.list({}));
const teams = queryClient.getQueryData<Team[]>(
teamKeys.list({
filters: {
userId: userId ?? "",
userRole: userRole ?? "",
},
}),
);
return teams?.find((team) => team.team_id === teamId);
},
@ -202,6 +214,7 @@ export const useInfiniteTeams = (pageSize: number = 50, search?: string, organiz
...(search && { search }),
...(organizationId && { organizationId }),
...(userId && { userId }),
isAdmin: isAdmin ? "true" : "false",
},
}),
queryFn: async ({ pageParam }) => {

View file

@ -155,7 +155,7 @@ const MCPToolsViewer = ({
error: mcpToolsError,
refetch: refetchTools,
} = useQuery({
queryKey: ["mcpTools", serverId, passthroughHeaders, oauthToken],
queryKey: ["mcpTools", serverId, passthroughHeaders, oauthToken, userID],
queryFn: async () => {
if (!accessToken) throw new Error("Access Token required");
const result = await listMCPTools(accessToken, serverId, buildCustomHeaders());

View file

@ -244,17 +244,19 @@ const ViewUserDashboard: React.FC<ViewUserDashboardProps> = ({
return await userListCall(
accessToken,
userIdFilter ? [userIdFilter] : null,
pagination.pageIndex + 1,
pagination.pageSize,
userListQueryFilters.userId ? [userListQueryFilters.userId] : null,
userListQueryFilters.page,
userListQueryFilters.pageSize,
null,
userRoleFilter ?? null,
teamFilter ?? null,
ssoUserIdFilter ?? null,
sortBy,
sortOrder,
orgAdminOrgIds ? orgAdminOrgIds.map((o) => o.organization_id) : null,
searchFilter,
userListQueryFilters.role ?? null,
userListQueryFilters.team ?? null,
userListQueryFilters.ssoUserId ?? null,
userListQueryFilters.sortBy,
userListQueryFilters.sortOrder,
userListQueryFilters.orgAdminOrgIds
? userListQueryFilters.orgAdminOrgIds.map((organization) => organization.organization_id)
: null,
userListQueryFilters.search,
);
},
enabled: Boolean(accessToken && token && userRole && userID),

View file

@ -30,7 +30,11 @@ export const useAutoRouterAvailability = (accessToken: string, body: Request, en
queryFn: ({ signal }) =>
apiClient.post<Availability>("/auto_router/availability", {
accessToken,
body: { ...body, complexity_router_config: JSON.parse(debounced) },
body: {
team_id: body.team_id,
saved_model_id: body.saved_model_id,
complexity_router_config: JSON.parse(debounced),
},
signal,
}),
enabled: enabled && Boolean(accessToken),
@ -53,8 +57,10 @@ export const useAutoRouterAvailability = (accessToken: string, body: Request, en
return query.data.error ?? null;
};
return {
...query,
data: query.data,
isError: query.isError,
isPending: query.isPending || (query.isFetching && !query.isFetchedAfterMount),
refetch: query.refetch,
isChecking,
saveBlockedReason: saveBlockedReason(),
};

View file

@ -246,20 +246,19 @@ const LogsPanel: React.FC<Props> = ({ accessToken, userId }) => {
const [page, setPage] = useState(1);
const [selectedLog, setSelectedLog] = useState<LogRow | null>(null);
const startDate = getStartMoment(timeRange).utc().format("YYYY-MM-DD HH:mm:ss");
const endDate = moment().utc().format("YYYY-MM-DD HH:mm:ss");
const logsCallOptions = {
accessToken,
start_date: startDate,
end_date: endDate,
page,
page_size: PAGE_SIZE,
params: { user_id: userId, sort_by: "startTime", sort_order: "desc" as const },
};
const logsQueryOptions = {
queryKey: [LOGS_QUERY_KEY, accessToken, userId, timeRange, page],
queryFn: () => uiSpendLogsCall(logsCallOptions),
queryFn: () => {
const logsCallOptions = {
accessToken,
start_date: getStartMoment(timeRange).utc().format("YYYY-MM-DD HH:mm:ss"),
end_date: moment().utc().format("YYYY-MM-DD HH:mm:ss"),
page,
page_size: PAGE_SIZE,
params: { user_id: userId, sort_by: "startTime", sort_order: "desc" as const },
};
return uiSpendLogsCall(logsCallOptions);
},
enabled: !!accessToken && !!userId,
placeholderData: keepPreviousData,
};
@ -270,10 +269,14 @@ const LogsPanel: React.FC<Props> = ({ accessToken, userId }) => {
const totalPages = logs?.total_pages ?? 0;
const total = logs?.total ?? 0;
const detailStartDate = selectedLog ? moment(selectedLog.startTime).utc().format("YYYY-MM-DD HH:mm:ss") : "";
const { data: detailData, isLoading: isDetailLoading } = useQuery({
queryKey: [LOGS_QUERY_KEY, "detail", accessToken, selectedLog?.request_id, selectedLog?.startTime],
queryFn: () => uiSpendLogDetailsCall(accessToken, selectedLog!.request_id, detailStartDate),
queryFn: () =>
uiSpendLogDetailsCall(
accessToken,
selectedLog!.request_id,
moment(selectedLog!.startTime).utc().format("YYYY-MM-DD HH:mm:ss"),
),
enabled: !!accessToken && !!selectedLog,
});
const details = detailData as LogDetails | undefined;

View file

@ -91,11 +91,11 @@ const TIME_RANGE_OPTIONS: { value: TimeRange; label: string }[] = [
const UsagePanel: React.FC<Props> = ({ accessToken, userId }) => {
const [timeRange, setTimeRange] = useState<TimeRange>("30d");
const { start, end } = getDateRange(timeRange);
const { data, isLoading } = useQuery({
queryKey: [USAGE_QUERY_KEY, accessToken, userId, timeRange],
queryFn: () => {
const { start, end } = getDateRange(timeRange);
const request = {
accessToken,
startTime: start,

View file

@ -136,7 +136,7 @@ export default function RequestLogsPanel({ accessToken, token, userRole, userID,
const { data: selectedKeyInfo } = useQuery(keyInfoQueryOptions);
const urlLogQueryOptions: UseQueryOptions<LogEntry | null> = {
queryKey: ["logs", "byId", urlLogId, accessToken],
queryKey: ["logs", "byId", urlLogId, accessToken, startTime, endTime, isCustomDate],
queryFn: async () => {
if (urlLogId === null) return null;
const window = formatLogsWindow(startTime, endTime, isCustomDate);

View file

@ -161,6 +161,9 @@ describe("useLogFilterLogic", () => {
["startTime", { startTime: "2025-02-02T00:00:00" }],
["columnFilters", { columnFilters: [{ id: LOG_FILTER_IDS.TEAM_ID, value: "team-2" }] }],
["excludeInternalHealthChecks", { excludeInternalHealthChecks: true }],
["token", { token: "other-token" }],
["userRole", { userRole: "Internal User" }],
["userID", { userID: "user-2" }],
])("refetches when %s changes", async (_label, nextProps) => {
const { rerender } = renderHook((props: HookOverrides) => useLogFilterLogic({ ...defaultProps, ...props }), {
wrapper,

View file

@ -155,6 +155,9 @@ export function useLogFilterLogic({
sortOrder,
excludeInternalHealthChecks,
sessionCursor,
token,
userRole,
userID,
],
queryFn: async () => {
if (!accessToken || !token || !userRole || !userID) {