From 0f531ee723cbb9fd8beda88ae586d2362e303848 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 11 Dec 2025 20:00:33 +0000 Subject: [PATCH 01/29] feat: add search space zod schemas for GET and PUT endpoints --- .../contracts/types/search-space.types.ts | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 surfsense_web/contracts/types/search-space.types.ts diff --git a/surfsense_web/contracts/types/search-space.types.ts b/surfsense_web/contracts/types/search-space.types.ts new file mode 100644 index 000000000..e8af831bd --- /dev/null +++ b/surfsense_web/contracts/types/search-space.types.ts @@ -0,0 +1,84 @@ +import { z } from "zod"; +import { paginationQueryParams } from "."; + +// Base schemas +export const searchSpace = z.object({ + id: z.number(), + name: z.string(), + description: z.string().nullable(), + created_at: z.string(), + user_id: z.string(), + citations_enabled: z.boolean(), + qna_custom_instructions: z.string().nullable(), + member_count: z.number(), + is_owner: z.boolean(), +}); + +/** + * Get search spaces + */ +export const getSearchSpacesRequest = z.object({ + queryParams: paginationQueryParams + .extend({ + owned_only: z.boolean().optional(), + }) + .nullish(), +}); + +export const getSearchSpacesResponse = z.array(searchSpace); + +/** + * Create search space + */ +export const createSearchSpaceRequest = searchSpace + .pick({ name: true, description: true }) + .extend({ + citations_enabled: z.boolean().default(true).optional(), + qna_custom_instructions: z.string().nullable().optional(), + }); + +export const createSearchSpaceResponse = searchSpace.omit({ member_count: true, is_owner: true }); + +/** + * Get community prompts + */ +export const getCommunityPromptsResponse = z.array( + z.object({ + key: z.string(), + value: z.string(), + author: z.string(), + link: z.string(), + category: z.string(), + }) +); + +/** + * Get search space + */ +export const getSearchSpaceRequest = searchSpace.pick({ id: true }); + +export const getSearchSpaceResponse = searchSpace.omit({ member_count: true, is_owner: true }); + +/** + * Update search space + */ +export const updateSearchSpaceRequest = z.object({ + id: z.number(), + data: searchSpace + .pick({ name: true, description: true, citations_enabled: true, qna_custom_instructions: true }) + .partial(), +}); + +export const updateSearchSpaceResponse = searchSpace.omit({ member_count: true, is_owner: true }); + +// Inferred types +export type SearchSpace = z.infer; +export type GetSearchSpacesRequest = z.infer; +export type GetSearchSpacesResponse = z.infer; +export type CreateSearchSpaceRequest = z.infer; +export type CreateSearchSpaceResponse = z.infer; +export type GetCommunityPromptsResponse = z.infer; +export type GetSearchSpaceRequest = z.infer; +export type GetSearchSpaceResponse = z.infer; +export type UpdateSearchSpaceRequest = z.infer; +export type UpdateSearchSpaceResponse = z.infer; From 3ef860fb1c3b117e8d6939aabf558fcb52d8dd67 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 11 Dec 2025 20:02:08 +0000 Subject: [PATCH 02/29] feat: add delete search space zod schemas --- surfsense_web/contracts/types/search-space.types.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/surfsense_web/contracts/types/search-space.types.ts b/surfsense_web/contracts/types/search-space.types.ts index e8af831bd..7c324b55a 100644 --- a/surfsense_web/contracts/types/search-space.types.ts +++ b/surfsense_web/contracts/types/search-space.types.ts @@ -71,6 +71,15 @@ export const updateSearchSpaceRequest = z.object({ export const updateSearchSpaceResponse = searchSpace.omit({ member_count: true, is_owner: true }); +/** + * Delete search space + */ +export const deleteSearchSpaceRequest = searchSpace.pick({ id: true }); + +export const deleteSearchSpaceResponse = z.object({ + message: z.literal("Search space deleted successfully"), +}); + // Inferred types export type SearchSpace = z.infer; export type GetSearchSpacesRequest = z.infer; @@ -82,3 +91,5 @@ export type GetSearchSpaceRequest = z.infer; export type GetSearchSpaceResponse = z.infer; export type UpdateSearchSpaceRequest = z.infer; export type UpdateSearchSpaceResponse = z.infer; +export type DeleteSearchSpaceRequest = z.infer; +export type DeleteSearchSpaceResponse = z.infer; From c43cd16dabd4da2029ae8f233fe5c731d1f6fa55 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 11 Dec 2025 20:11:32 +0000 Subject: [PATCH 03/29] feat: add search spaces API service with getSearchSpaces endpoint --- .../lib/apis/search-spaces-api.service.ts | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 surfsense_web/lib/apis/search-spaces-api.service.ts diff --git a/surfsense_web/lib/apis/search-spaces-api.service.ts b/surfsense_web/lib/apis/search-spaces-api.service.ts new file mode 100644 index 000000000..bf2464a9e --- /dev/null +++ b/surfsense_web/lib/apis/search-spaces-api.service.ts @@ -0,0 +1,40 @@ +import { + type GetSearchSpacesRequest, + getSearchSpacesRequest, + getSearchSpacesResponse, +} from "@/contracts/types/search-space.types"; +import { ValidationError } from "../error"; +import { baseApiService } from "./base-api.service"; + +class SearchSpacesApiService { + /** + * Get a list of search spaces with optional filtering and pagination + */ + getSearchSpaces = async (request?: GetSearchSpacesRequest) => { + const parsedRequest = getSearchSpacesRequest.safeParse(request || {}); + + if (!parsedRequest.success) { + console.error("Invalid request:", parsedRequest.error); + + const errorMessage = parsedRequest.error.errors.map((err) => err.message).join(", "); + throw new ValidationError(`Invalid request: ${errorMessage}`); + } + + // Transform query params to be string values + const transformedQueryParams = parsedRequest.data.queryParams + ? Object.fromEntries( + Object.entries(parsedRequest.data.queryParams).map(([k, v]) => { + return [k, String(v)]; + }) + ) + : undefined; + + const queryParams = transformedQueryParams + ? new URLSearchParams(transformedQueryParams).toString() + : ""; + + return baseApiService.get(`/api/v1/searchspaces?${queryParams}`, getSearchSpacesResponse); + }; +} + +export const searchSpacesApiService = new SearchSpacesApiService(); From f983d1f7ffb56257630e3c22483a1fd9c9fd8242 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 11 Dec 2025 20:17:20 +0000 Subject: [PATCH 04/29] feat: add createSearchSpace endpoint to API service --- .../lib/apis/search-spaces-api.service.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/surfsense_web/lib/apis/search-spaces-api.service.ts b/surfsense_web/lib/apis/search-spaces-api.service.ts index bf2464a9e..7363dc671 100644 --- a/surfsense_web/lib/apis/search-spaces-api.service.ts +++ b/surfsense_web/lib/apis/search-spaces-api.service.ts @@ -1,5 +1,8 @@ import { + type CreateSearchSpaceRequest, type GetSearchSpacesRequest, + createSearchSpaceRequest, + createSearchSpaceResponse, getSearchSpacesRequest, getSearchSpacesResponse, } from "@/contracts/types/search-space.types"; @@ -35,6 +38,24 @@ class SearchSpacesApiService { return baseApiService.get(`/api/v1/searchspaces?${queryParams}`, getSearchSpacesResponse); }; + + /** + * Create a new search space + */ + createSearchSpace = async (request: CreateSearchSpaceRequest) => { + const parsedRequest = createSearchSpaceRequest.safeParse(request); + + if (!parsedRequest.success) { + console.error("Invalid request:", parsedRequest.error); + + const errorMessage = parsedRequest.error.errors.map((err) => err.message).join(", "); + throw new ValidationError(`Invalid request: ${errorMessage}`); + } + + return baseApiService.post(`/api/v1/searchspaces`, createSearchSpaceResponse, { + body: parsedRequest.data, + }); + }; } export const searchSpacesApiService = new SearchSpacesApiService(); From 1d6bfe1e29311d80746ce65db8858f9a540684a4 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 11 Dec 2025 20:21:16 +0000 Subject: [PATCH 05/29] feat: add getCommunityPrompts endpoint to API service --- surfsense_web/lib/apis/search-spaces-api.service.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/surfsense_web/lib/apis/search-spaces-api.service.ts b/surfsense_web/lib/apis/search-spaces-api.service.ts index 7363dc671..b7c7a4c99 100644 --- a/surfsense_web/lib/apis/search-spaces-api.service.ts +++ b/surfsense_web/lib/apis/search-spaces-api.service.ts @@ -3,6 +3,7 @@ import { type GetSearchSpacesRequest, createSearchSpaceRequest, createSearchSpaceResponse, + getCommunityPromptsResponse, getSearchSpacesRequest, getSearchSpacesResponse, } from "@/contracts/types/search-space.types"; @@ -56,6 +57,13 @@ class SearchSpacesApiService { body: parsedRequest.data, }); }; + + /** + * Get community-curated prompts for search space system instructions + */ + getCommunityPrompts = async () => { + return baseApiService.get(`/api/v1/searchspaces/prompts/community`, getCommunityPromptsResponse); + }; } export const searchSpacesApiService = new SearchSpacesApiService(); From 646065d745172efbdbad2a6c04fe27591940a2b8 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 11 Dec 2025 20:26:30 +0000 Subject: [PATCH 06/29] feat(search-spaces): add getSearchSpace endpoint to API service --- .../lib/apis/search-spaces-api.service.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/surfsense_web/lib/apis/search-spaces-api.service.ts b/surfsense_web/lib/apis/search-spaces-api.service.ts index b7c7a4c99..60e2b9d57 100644 --- a/surfsense_web/lib/apis/search-spaces-api.service.ts +++ b/surfsense_web/lib/apis/search-spaces-api.service.ts @@ -1,9 +1,12 @@ import { type CreateSearchSpaceRequest, + type GetSearchSpaceRequest, type GetSearchSpacesRequest, createSearchSpaceRequest, createSearchSpaceResponse, getCommunityPromptsResponse, + getSearchSpaceRequest, + getSearchSpaceResponse, getSearchSpacesRequest, getSearchSpacesResponse, } from "@/contracts/types/search-space.types"; @@ -64,6 +67,22 @@ class SearchSpacesApiService { getCommunityPrompts = async () => { return baseApiService.get(`/api/v1/searchspaces/prompts/community`, getCommunityPromptsResponse); }; + + /** + * Get a single search space by ID + */ + getSearchSpace = async (request: GetSearchSpaceRequest) => { + const parsedRequest = getSearchSpaceRequest.safeParse(request); + + if (!parsedRequest.success) { + console.error("Invalid request:", parsedRequest.error); + + const errorMessage = parsedRequest.error.errors.map((err) => err.message).join(", "); + throw new ValidationError(`Invalid request: ${errorMessage}`); + } + + return baseApiService.get(`/api/v1/searchspaces/${request.id}`, getSearchSpaceResponse); + }; } export const searchSpacesApiService = new SearchSpacesApiService(); From 80357d450bc563dee7a053c9aea5f76dfa0ef401 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 11 Dec 2025 20:28:56 +0000 Subject: [PATCH 07/29] feat(search-spaces): add updateSearchSpace endpoint to API service --- .../lib/apis/search-spaces-api.service.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/surfsense_web/lib/apis/search-spaces-api.service.ts b/surfsense_web/lib/apis/search-spaces-api.service.ts index 60e2b9d57..57cacd096 100644 --- a/surfsense_web/lib/apis/search-spaces-api.service.ts +++ b/surfsense_web/lib/apis/search-spaces-api.service.ts @@ -2,6 +2,7 @@ import { type CreateSearchSpaceRequest, type GetSearchSpaceRequest, type GetSearchSpacesRequest, + type UpdateSearchSpaceRequest, createSearchSpaceRequest, createSearchSpaceResponse, getCommunityPromptsResponse, @@ -9,6 +10,8 @@ import { getSearchSpaceResponse, getSearchSpacesRequest, getSearchSpacesResponse, + updateSearchSpaceRequest, + updateSearchSpaceResponse, } from "@/contracts/types/search-space.types"; import { ValidationError } from "../error"; import { baseApiService } from "./base-api.service"; @@ -83,6 +86,24 @@ class SearchSpacesApiService { return baseApiService.get(`/api/v1/searchspaces/${request.id}`, getSearchSpaceResponse); }; + + /** + * Update an existing search space + */ + updateSearchSpace = async (request: UpdateSearchSpaceRequest) => { + const parsedRequest = updateSearchSpaceRequest.safeParse(request); + + if (!parsedRequest.success) { + console.error("Invalid request:", parsedRequest.error); + + const errorMessage = parsedRequest.error.errors.map((err) => err.message).join(", "); + throw new ValidationError(`Invalid request: ${errorMessage}`); + } + + return baseApiService.put(`/api/v1/searchspaces/${request.id}`, updateSearchSpaceResponse, { + body: parsedRequest.data.data, + }); + }; } export const searchSpacesApiService = new SearchSpacesApiService(); From 172f58f7424061c28c47d4b42644aa8c4e87a244 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 11 Dec 2025 20:31:09 +0000 Subject: [PATCH 08/29] feat(search-spaces): add deleteSearchSpace endpoint to API service --- .../lib/apis/search-spaces-api.service.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/surfsense_web/lib/apis/search-spaces-api.service.ts b/surfsense_web/lib/apis/search-spaces-api.service.ts index 57cacd096..73b57ee3c 100644 --- a/surfsense_web/lib/apis/search-spaces-api.service.ts +++ b/surfsense_web/lib/apis/search-spaces-api.service.ts @@ -1,10 +1,13 @@ import { type CreateSearchSpaceRequest, + type DeleteSearchSpaceRequest, type GetSearchSpaceRequest, type GetSearchSpacesRequest, type UpdateSearchSpaceRequest, createSearchSpaceRequest, createSearchSpaceResponse, + deleteSearchSpaceRequest, + deleteSearchSpaceResponse, getCommunityPromptsResponse, getSearchSpaceRequest, getSearchSpaceResponse, @@ -104,6 +107,22 @@ class SearchSpacesApiService { body: parsedRequest.data.data, }); }; + + /** + * Delete a search space + */ + deleteSearchSpace = async (request: DeleteSearchSpaceRequest) => { + const parsedRequest = deleteSearchSpaceRequest.safeParse(request); + + if (!parsedRequest.success) { + console.error("Invalid request:", parsedRequest.error); + + const errorMessage = parsedRequest.error.errors.map((err) => err.message).join(", "); + throw new ValidationError(`Invalid request: ${errorMessage}`); + } + + return baseApiService.delete(`/api/v1/searchspaces/${request.id}`, deleteSearchSpaceResponse); + }; } export const searchSpacesApiService = new SearchSpacesApiService(); From edfe2aa87e5bf451bad1506a591a03e51612d3e2 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 11 Dec 2025 20:35:01 +0000 Subject: [PATCH 09/29] feat(search-spaces): add cache keys for search spaces and llm configs --- surfsense_web/lib/query-client/cache-keys.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/surfsense_web/lib/query-client/cache-keys.ts b/surfsense_web/lib/query-client/cache-keys.ts index fcdf322bc..e488d2c0e 100644 --- a/surfsense_web/lib/query-client/cache-keys.ts +++ b/surfsense_web/lib/query-client/cache-keys.ts @@ -2,6 +2,7 @@ import type { GetChatsRequest } from "@/contracts/types/chat.types"; import type { GetDocumentsRequest } from "@/contracts/types/document.types"; import type { GetLLMConfigsRequest } from "@/contracts/types/llm-config.types"; import type { GetPodcastsRequest } from "@/contracts/types/podcast.types"; +import type { GetSearchSpacesRequest } from "@/contracts/types/search-space.types"; export const cacheKeys = { chats: { @@ -33,4 +34,17 @@ export const cacheKeys = { auth: { user: ["auth", "user"] as const, }, + searchSpaces: { + all: ["search-spaces"] as const, + withQueryParams: (queries: GetSearchSpacesRequest["queryParams"]) => + ["search-spaces", ...(queries ? Object.values(queries) : [])] as const, + detail: (searchSpaceId: string) => ["search-spaces", searchSpaceId] as const, + communityPrompts: ["search-spaces", "community-prompts"] as const, + }, + llmConfigs: { + all: (searchSpaceId: string) => ["llm-configs", searchSpaceId] as const, + global: ["llm-configs", "global"] as const, + detail: (searchSpaceId: string, llmConfigId: string) => ["llm-configs", searchSpaceId, llmConfigId] as const, + preferences: (searchSpaceId: string) => ["llm-configs", "preferences", searchSpaceId] as const, + }, }; From 58a14f422db329a946ccf5b7bb2abe6e0921bf0e Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 11 Dec 2025 20:42:27 +0000 Subject: [PATCH 10/29] feat(search-spaces): add searchSpacesAtom query atom for fetching search spaces list --- .../search-spaces/search-space-query.atoms.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 surfsense_web/atoms/search-spaces/search-space-query.atoms.ts diff --git a/surfsense_web/atoms/search-spaces/search-space-query.atoms.ts b/surfsense_web/atoms/search-spaces/search-space-query.atoms.ts new file mode 100644 index 000000000..00cf60abd --- /dev/null +++ b/surfsense_web/atoms/search-spaces/search-space-query.atoms.ts @@ -0,0 +1,26 @@ +import { atomWithQuery } from "jotai-tanstack-query"; +import { atom } from "jotai"; +import type { GetSearchSpacesRequest } from "@/contracts/types/search-space.types"; +import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; +import { cacheKeys } from "@/lib/query-client/cache-keys"; + +// Atom to store current query params for search spaces +export const searchSpacesQueryParamsAtom = atom({ + page: 0, + page_size: 10, +}); + +// Query atom to fetch search spaces with query params +export const searchSpacesAtom = atomWithQuery((get) => { + const queryParams = get(searchSpacesQueryParamsAtom); + + return { + queryKey: cacheKeys.searchSpaces.withQueryParams(queryParams), + staleTime: 5 * 60 * 1000, // 5 minutes + queryFn: async () => { + return searchSpacesApiService.getSearchSpaces({ + queryParams, + }); + }, + }; +}); From b4874111e2f9f6bfbdf4e37d97260e8b8cfa9e54 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 11 Dec 2025 20:50:04 +0000 Subject: [PATCH 11/29] fix(search-spaces): correct pagination params to use skip/limit instead of page/page_size --- .../atoms/search-spaces/search-space-query.atoms.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/surfsense_web/atoms/search-spaces/search-space-query.atoms.ts b/surfsense_web/atoms/search-spaces/search-space-query.atoms.ts index 00cf60abd..92c2d4198 100644 --- a/surfsense_web/atoms/search-spaces/search-space-query.atoms.ts +++ b/surfsense_web/atoms/search-spaces/search-space-query.atoms.ts @@ -6,8 +6,9 @@ import { cacheKeys } from "@/lib/query-client/cache-keys"; // Atom to store current query params for search spaces export const searchSpacesQueryParamsAtom = atom({ - page: 0, - page_size: 10, + skip: 0, + limit: 10, + owned_only: false, }); // Query atom to fetch search spaces with query params From fc08f721b0de018e32fb0a778cd9b7a52b8de6c7 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 11 Dec 2025 21:00:17 +0000 Subject: [PATCH 12/29] feat(search-spaces): add communityPromptsAtom and remove obvious comments --- .../search-spaces/search-space-query.atoms.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/surfsense_web/atoms/search-spaces/search-space-query.atoms.ts b/surfsense_web/atoms/search-spaces/search-space-query.atoms.ts index 92c2d4198..b28d19b82 100644 --- a/surfsense_web/atoms/search-spaces/search-space-query.atoms.ts +++ b/surfsense_web/atoms/search-spaces/search-space-query.atoms.ts @@ -4,20 +4,18 @@ import type { GetSearchSpacesRequest } from "@/contracts/types/search-space.type import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; import { cacheKeys } from "@/lib/query-client/cache-keys"; -// Atom to store current query params for search spaces export const searchSpacesQueryParamsAtom = atom({ skip: 0, limit: 10, owned_only: false, }); -// Query atom to fetch search spaces with query params export const searchSpacesAtom = atomWithQuery((get) => { const queryParams = get(searchSpacesQueryParamsAtom); return { queryKey: cacheKeys.searchSpaces.withQueryParams(queryParams), - staleTime: 5 * 60 * 1000, // 5 minutes + staleTime: 5 * 60 * 1000, queryFn: async () => { return searchSpacesApiService.getSearchSpaces({ queryParams, @@ -25,3 +23,13 @@ export const searchSpacesAtom = atomWithQuery((get) => { }, }; }); + +export const communityPromptsAtom = atomWithQuery(() => { + return { + queryKey: cacheKeys.searchSpaces.communityPrompts, + staleTime: 30 * 60 * 1000, + queryFn: async () => { + return searchSpacesApiService.getCommunityPrompts(); + }, + }; +}); From 3fafb1808904ed05d84cf710fc54f4f022f91d3f Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 11 Dec 2025 21:07:32 +0000 Subject: [PATCH 13/29] refactor: remove unnecessary comments from search-space types --- surfsense_web/contracts/types/search-space.types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/surfsense_web/contracts/types/search-space.types.ts b/surfsense_web/contracts/types/search-space.types.ts index 7c324b55a..c0096b41c 100644 --- a/surfsense_web/contracts/types/search-space.types.ts +++ b/surfsense_web/contracts/types/search-space.types.ts @@ -1,7 +1,6 @@ import { z } from "zod"; import { paginationQueryParams } from "."; -// Base schemas export const searchSpace = z.object({ id: z.number(), name: z.string(), From 6bac012474081c9c9b8ff5e7aef39ff8d9a62ce2 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 11 Dec 2025 21:12:00 +0000 Subject: [PATCH 14/29] feat: add createSearchSpaceMutationAtom --- .../search-space-mutation.atoms.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 surfsense_web/atoms/search-spaces/search-space-mutation.atoms.ts diff --git a/surfsense_web/atoms/search-spaces/search-space-mutation.atoms.ts b/surfsense_web/atoms/search-spaces/search-space-mutation.atoms.ts new file mode 100644 index 000000000..c9cc37a85 --- /dev/null +++ b/surfsense_web/atoms/search-spaces/search-space-mutation.atoms.ts @@ -0,0 +1,24 @@ +import { atomWithMutation } from "jotai-tanstack-query"; +import { toast } from "sonner"; +import type { + CreateSearchSpaceRequest, +} from "@/contracts/types/search-space.types"; +import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; +import { cacheKeys } from "@/lib/query-client/cache-keys"; +import { queryClient } from "@/lib/query-client/client"; + +export const createSearchSpaceMutationAtom = atomWithMutation(() => { + return { + mutationKey: ["create-search-space"], + mutationFn: async (request: CreateSearchSpaceRequest) => { + return searchSpacesApiService.createSearchSpace(request); + }, + + onSuccess: () => { + toast.success("Search space created successfully"); + queryClient.invalidateQueries({ + queryKey: cacheKeys.searchSpaces.all, + }); + }, + }; +}); From 2f2898f0c3eaf20d2b855ffb99b1930586b9bf0c Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 11 Dec 2025 21:16:15 +0000 Subject: [PATCH 15/29] refactor: merge seach-spaces and search-spaces folders, fix typo --- surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx | 2 +- surfsense_web/atoms/chats/chat-mutation.atoms.ts | 2 +- surfsense_web/atoms/chats/chat-query.atoms.ts | 2 +- surfsense_web/atoms/documents/document-mutation.atoms.ts | 2 +- surfsense_web/atoms/documents/document-query.atoms.ts | 2 +- surfsense_web/atoms/podcasts/podcast-mutation.atoms.ts | 2 +- .../search-space-queries.atom.ts} | 0 7 files changed, 6 insertions(+), 6 deletions(-) rename surfsense_web/atoms/{seach-spaces/seach-space-queries.atom.ts => search-spaces/search-space-queries.atom.ts} (100%) diff --git a/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx b/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx index 40f68938b..ef3448583 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx @@ -9,7 +9,7 @@ import type React from "react"; import { useCallback, useEffect, useMemo, useState } from "react"; import { activeChathatUIAtom, activeChatIdAtom } from "@/atoms/chats/ui.atoms"; import { llmPreferencesAtom } from "@/atoms/llm-config/llm-config-query.atoms"; -import { activeSearchSpaceIdAtom } from "@/atoms/seach-spaces/seach-space-queries.atom"; +import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-queries.atom"; import { ChatPanelContainer } from "@/components/chat/ChatPanel/ChatPanelContainer"; import { DashboardBreadcrumb } from "@/components/dashboard-breadcrumb"; import { LanguageSwitcher } from "@/components/LanguageSwitcher"; diff --git a/surfsense_web/atoms/chats/chat-mutation.atoms.ts b/surfsense_web/atoms/chats/chat-mutation.atoms.ts index 6105ef615..a5bbad88a 100644 --- a/surfsense_web/atoms/chats/chat-mutation.atoms.ts +++ b/surfsense_web/atoms/chats/chat-mutation.atoms.ts @@ -10,7 +10,7 @@ import { chatsApiService } from "@/lib/apis/chats-api.service"; import { getBearerToken } from "@/lib/auth-utils"; import { cacheKeys } from "@/lib/query-client/cache-keys"; import { queryClient } from "@/lib/query-client/client"; -import { activeSearchSpaceIdAtom } from "../seach-spaces/seach-space-queries.atom"; +import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-queries.atom"; import { globalChatsQueryParamsAtom } from "./ui.atoms"; export const deleteChatMutationAtom = atomWithMutation((get) => { diff --git a/surfsense_web/atoms/chats/chat-query.atoms.ts b/surfsense_web/atoms/chats/chat-query.atoms.ts index 36871dbd0..2347effbd 100644 --- a/surfsense_web/atoms/chats/chat-query.atoms.ts +++ b/surfsense_web/atoms/chats/chat-query.atoms.ts @@ -1,5 +1,5 @@ import { atomWithQuery } from "jotai-tanstack-query"; -import { activeSearchSpaceIdAtom } from "@/atoms/seach-spaces/seach-space-queries.atom"; +import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-queries.atom"; import { chatsApiService } from "@/lib/apis/chats-api.service"; import { podcastsApiService } from "@/lib/apis/podcasts-api.service"; import { getBearerToken } from "@/lib/auth-utils"; diff --git a/surfsense_web/atoms/documents/document-mutation.atoms.ts b/surfsense_web/atoms/documents/document-mutation.atoms.ts index 6eaaa014a..385b0e6d1 100644 --- a/surfsense_web/atoms/documents/document-mutation.atoms.ts +++ b/surfsense_web/atoms/documents/document-mutation.atoms.ts @@ -1,6 +1,6 @@ import { atomWithMutation } from "jotai-tanstack-query"; import { toast } from "sonner"; -import { activeSearchSpaceIdAtom } from "@/atoms/seach-spaces/seach-space-queries.atom"; +import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-queries.atom"; import type { CreateDocumentRequest, DeleteDocumentRequest, diff --git a/surfsense_web/atoms/documents/document-query.atoms.ts b/surfsense_web/atoms/documents/document-query.atoms.ts index acdff9aaf..eadc5c740 100644 --- a/surfsense_web/atoms/documents/document-query.atoms.ts +++ b/surfsense_web/atoms/documents/document-query.atoms.ts @@ -1,5 +1,5 @@ import { atomWithQuery } from "jotai-tanstack-query"; -import { activeSearchSpaceIdAtom } from "@/atoms/seach-spaces/seach-space-queries.atom"; +import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-queries.atom"; import type { SearchDocumentsRequest } from "@/contracts/types/document.types"; import { documentsApiService } from "@/lib/apis/documents-api.service"; import { cacheKeys } from "@/lib/query-client/cache-keys"; diff --git a/surfsense_web/atoms/podcasts/podcast-mutation.atoms.ts b/surfsense_web/atoms/podcasts/podcast-mutation.atoms.ts index 7e51891be..706d3279c 100644 --- a/surfsense_web/atoms/podcasts/podcast-mutation.atoms.ts +++ b/surfsense_web/atoms/podcasts/podcast-mutation.atoms.ts @@ -1,6 +1,6 @@ import { atomWithMutation } from "jotai-tanstack-query"; import { toast } from "sonner"; -import { activeSearchSpaceIdAtom } from "@/atoms/seach-spaces/seach-space-queries.atom"; +import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-queries.atom"; import type { DeletePodcastRequest, GeneratePodcastRequest, diff --git a/surfsense_web/atoms/seach-spaces/seach-space-queries.atom.ts b/surfsense_web/atoms/search-spaces/search-space-queries.atom.ts similarity index 100% rename from surfsense_web/atoms/seach-spaces/seach-space-queries.atom.ts rename to surfsense_web/atoms/search-spaces/search-space-queries.atom.ts From 00b5b7dec89a3155cb81a4a8e0a4655e53eebd8a Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Thu, 11 Dec 2025 21:19:29 +0000 Subject: [PATCH 16/29] refactor: merge search space query atoms into single file --- .../app/dashboard/[search_space_id]/client-layout.tsx | 2 +- surfsense_web/atoms/chats/chat-mutation.atoms.ts | 2 +- surfsense_web/atoms/chats/chat-query.atoms.ts | 2 +- surfsense_web/atoms/documents/document-mutation.atoms.ts | 2 +- surfsense_web/atoms/documents/document-query.atoms.ts | 2 +- surfsense_web/atoms/podcasts/podcast-mutation.atoms.ts | 2 +- surfsense_web/atoms/search-spaces/search-space-queries.atom.ts | 3 --- surfsense_web/atoms/search-spaces/search-space-query.atoms.ts | 2 ++ 8 files changed, 8 insertions(+), 9 deletions(-) delete mode 100644 surfsense_web/atoms/search-spaces/search-space-queries.atom.ts diff --git a/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx b/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx index ef3448583..028c0efdc 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx @@ -9,7 +9,7 @@ import type React from "react"; import { useCallback, useEffect, useMemo, useState } from "react"; import { activeChathatUIAtom, activeChatIdAtom } from "@/atoms/chats/ui.atoms"; import { llmPreferencesAtom } from "@/atoms/llm-config/llm-config-query.atoms"; -import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-queries.atom"; +import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { ChatPanelContainer } from "@/components/chat/ChatPanel/ChatPanelContainer"; import { DashboardBreadcrumb } from "@/components/dashboard-breadcrumb"; import { LanguageSwitcher } from "@/components/LanguageSwitcher"; diff --git a/surfsense_web/atoms/chats/chat-mutation.atoms.ts b/surfsense_web/atoms/chats/chat-mutation.atoms.ts index a5bbad88a..a08dcd21f 100644 --- a/surfsense_web/atoms/chats/chat-mutation.atoms.ts +++ b/surfsense_web/atoms/chats/chat-mutation.atoms.ts @@ -10,7 +10,7 @@ import { chatsApiService } from "@/lib/apis/chats-api.service"; import { getBearerToken } from "@/lib/auth-utils"; import { cacheKeys } from "@/lib/query-client/cache-keys"; import { queryClient } from "@/lib/query-client/client"; -import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-queries.atom"; +import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-query.atoms"; import { globalChatsQueryParamsAtom } from "./ui.atoms"; export const deleteChatMutationAtom = atomWithMutation((get) => { diff --git a/surfsense_web/atoms/chats/chat-query.atoms.ts b/surfsense_web/atoms/chats/chat-query.atoms.ts index 2347effbd..5a1242ded 100644 --- a/surfsense_web/atoms/chats/chat-query.atoms.ts +++ b/surfsense_web/atoms/chats/chat-query.atoms.ts @@ -1,5 +1,5 @@ import { atomWithQuery } from "jotai-tanstack-query"; -import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-queries.atom"; +import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { chatsApiService } from "@/lib/apis/chats-api.service"; import { podcastsApiService } from "@/lib/apis/podcasts-api.service"; import { getBearerToken } from "@/lib/auth-utils"; diff --git a/surfsense_web/atoms/documents/document-mutation.atoms.ts b/surfsense_web/atoms/documents/document-mutation.atoms.ts index 385b0e6d1..0eae1081d 100644 --- a/surfsense_web/atoms/documents/document-mutation.atoms.ts +++ b/surfsense_web/atoms/documents/document-mutation.atoms.ts @@ -1,6 +1,6 @@ import { atomWithMutation } from "jotai-tanstack-query"; import { toast } from "sonner"; -import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-queries.atom"; +import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import type { CreateDocumentRequest, DeleteDocumentRequest, diff --git a/surfsense_web/atoms/documents/document-query.atoms.ts b/surfsense_web/atoms/documents/document-query.atoms.ts index eadc5c740..656706a62 100644 --- a/surfsense_web/atoms/documents/document-query.atoms.ts +++ b/surfsense_web/atoms/documents/document-query.atoms.ts @@ -1,5 +1,5 @@ import { atomWithQuery } from "jotai-tanstack-query"; -import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-queries.atom"; +import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import type { SearchDocumentsRequest } from "@/contracts/types/document.types"; import { documentsApiService } from "@/lib/apis/documents-api.service"; import { cacheKeys } from "@/lib/query-client/cache-keys"; diff --git a/surfsense_web/atoms/podcasts/podcast-mutation.atoms.ts b/surfsense_web/atoms/podcasts/podcast-mutation.atoms.ts index 706d3279c..cdb28ceb2 100644 --- a/surfsense_web/atoms/podcasts/podcast-mutation.atoms.ts +++ b/surfsense_web/atoms/podcasts/podcast-mutation.atoms.ts @@ -1,6 +1,6 @@ import { atomWithMutation } from "jotai-tanstack-query"; import { toast } from "sonner"; -import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-queries.atom"; +import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import type { DeletePodcastRequest, GeneratePodcastRequest, diff --git a/surfsense_web/atoms/search-spaces/search-space-queries.atom.ts b/surfsense_web/atoms/search-spaces/search-space-queries.atom.ts deleted file mode 100644 index 4bccf496f..000000000 --- a/surfsense_web/atoms/search-spaces/search-space-queries.atom.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { atom } from "jotai"; - -export const activeSearchSpaceIdAtom = atom(null); diff --git a/surfsense_web/atoms/search-spaces/search-space-query.atoms.ts b/surfsense_web/atoms/search-spaces/search-space-query.atoms.ts index b28d19b82..1f03e25a2 100644 --- a/surfsense_web/atoms/search-spaces/search-space-query.atoms.ts +++ b/surfsense_web/atoms/search-spaces/search-space-query.atoms.ts @@ -4,6 +4,8 @@ import type { GetSearchSpacesRequest } from "@/contracts/types/search-space.type import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; import { cacheKeys } from "@/lib/query-client/cache-keys"; +export const activeSearchSpaceIdAtom = atom(null); + export const searchSpacesQueryParamsAtom = atom({ skip: 0, limit: 10, From d3dd2fc886cdad6e0043af25c66e1884d9a1ad92 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Fri, 12 Dec 2025 06:29:10 +0000 Subject: [PATCH 17/29] feat: add updateSearchSpaceMutationAtom --- .../search-space-mutation.atoms.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/surfsense_web/atoms/search-spaces/search-space-mutation.atoms.ts b/surfsense_web/atoms/search-spaces/search-space-mutation.atoms.ts index c9cc37a85..2ec64ab13 100644 --- a/surfsense_web/atoms/search-spaces/search-space-mutation.atoms.ts +++ b/surfsense_web/atoms/search-spaces/search-space-mutation.atoms.ts @@ -2,7 +2,9 @@ import { atomWithMutation } from "jotai-tanstack-query"; import { toast } from "sonner"; import type { CreateSearchSpaceRequest, + UpdateSearchSpaceRequest, } from "@/contracts/types/search-space.types"; +import { activeSearchSpaceIdAtom } from "./search-space-query.atoms"; import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; import { cacheKeys } from "@/lib/query-client/cache-keys"; import { queryClient } from "@/lib/query-client/client"; @@ -22,3 +24,27 @@ export const createSearchSpaceMutationAtom = atomWithMutation(() => { }, }; }); + +export const updateSearchSpaceMutationAtom = atomWithMutation((get) => { + const activeSearchSpaceId = get(activeSearchSpaceIdAtom); + + return { + mutationKey: ["update-search-space", activeSearchSpaceId], + enabled: !!activeSearchSpaceId, + mutationFn: async (request: UpdateSearchSpaceRequest) => { + return searchSpacesApiService.updateSearchSpace(request); + }, + + onSuccess: (_, request: UpdateSearchSpaceRequest) => { + toast.success("Search space updated successfully"); + queryClient.invalidateQueries({ + queryKey: cacheKeys.searchSpaces.all, + }); + if (request.pathParams?.search_space_id) { + queryClient.invalidateQueries({ + queryKey: cacheKeys.searchSpaces.detail(request.pathParams.search_space_id), + }); + } + }, + }; +}); From 46061f64eeb60dee46a3f8ce5dea95a78cebf29e Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Fri, 12 Dec 2025 06:36:59 +0000 Subject: [PATCH 18/29] fix: correct UpdateSearchSpaceRequest structure in mutation atom --- .../atoms/search-spaces/search-space-mutation.atoms.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/surfsense_web/atoms/search-spaces/search-space-mutation.atoms.ts b/surfsense_web/atoms/search-spaces/search-space-mutation.atoms.ts index 2ec64ab13..54e934b5a 100644 --- a/surfsense_web/atoms/search-spaces/search-space-mutation.atoms.ts +++ b/surfsense_web/atoms/search-spaces/search-space-mutation.atoms.ts @@ -40,9 +40,9 @@ export const updateSearchSpaceMutationAtom = atomWithMutation((get) => { queryClient.invalidateQueries({ queryKey: cacheKeys.searchSpaces.all, }); - if (request.pathParams?.search_space_id) { + if (request.id) { queryClient.invalidateQueries({ - queryKey: cacheKeys.searchSpaces.detail(request.pathParams.search_space_id), + queryKey: cacheKeys.searchSpaces.detail(request.id), }); } }, From a820598cfdaa24bbf6ba145c1c068eeebad78454 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Fri, 12 Dec 2025 06:40:13 +0000 Subject: [PATCH 19/29] fix: convert id to string for cache key detail function --- .../atoms/search-spaces/search-space-mutation.atoms.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/surfsense_web/atoms/search-spaces/search-space-mutation.atoms.ts b/surfsense_web/atoms/search-spaces/search-space-mutation.atoms.ts index 54e934b5a..a97cc3379 100644 --- a/surfsense_web/atoms/search-spaces/search-space-mutation.atoms.ts +++ b/surfsense_web/atoms/search-spaces/search-space-mutation.atoms.ts @@ -42,7 +42,7 @@ export const updateSearchSpaceMutationAtom = atomWithMutation((get) => { }); if (request.id) { queryClient.invalidateQueries({ - queryKey: cacheKeys.searchSpaces.detail(request.id), + queryKey: cacheKeys.searchSpaces.detail(String(request.id)), }); } }, From 862288367dd1a90dc60672ba34b00819982292c4 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Fri, 12 Dec 2025 06:44:02 +0000 Subject: [PATCH 20/29] feat: add deleteSearchSpaceMutationAtom --- .../search-space-mutation.atoms.ts | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/surfsense_web/atoms/search-spaces/search-space-mutation.atoms.ts b/surfsense_web/atoms/search-spaces/search-space-mutation.atoms.ts index a97cc3379..ea1415869 100644 --- a/surfsense_web/atoms/search-spaces/search-space-mutation.atoms.ts +++ b/surfsense_web/atoms/search-spaces/search-space-mutation.atoms.ts @@ -3,6 +3,7 @@ import { toast } from "sonner"; import type { CreateSearchSpaceRequest, UpdateSearchSpaceRequest, + DeleteSearchSpaceRequest, } from "@/contracts/types/search-space.types"; import { activeSearchSpaceIdAtom } from "./search-space-query.atoms"; import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; @@ -48,3 +49,27 @@ export const updateSearchSpaceMutationAtom = atomWithMutation((get) => { }, }; }); + +export const deleteSearchSpaceMutationAtom = atomWithMutation((get) => { + const activeSearchSpaceId = get(activeSearchSpaceIdAtom); + + return { + mutationKey: ["delete-search-space", activeSearchSpaceId], + enabled: !!activeSearchSpaceId, + mutationFn: async (request: DeleteSearchSpaceRequest) => { + return searchSpacesApiService.deleteSearchSpace(request); + }, + + onSuccess: (_, request: DeleteSearchSpaceRequest) => { + toast.success("Search space deleted successfully"); + queryClient.invalidateQueries({ + queryKey: cacheKeys.searchSpaces.all, + }); + if (request.id) { + queryClient.removeQueries({ + queryKey: cacheKeys.searchSpaces.detail(String(request.id)), + }); + } + }, + }; +}); From 4aa026cae4d8b56d37481d7ad3f1100e40103b44 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Fri, 12 Dec 2025 07:51:00 +0000 Subject: [PATCH 21/29] feat: migrate searchspaces page to use createSearchSpaceMutationAtom --- .../app/dashboard/searchspaces/page.tsx | 43 +++++-------------- 1 file changed, 11 insertions(+), 32 deletions(-) diff --git a/surfsense_web/app/dashboard/searchspaces/page.tsx b/surfsense_web/app/dashboard/searchspaces/page.tsx index 520c4358e..db2e06d44 100644 --- a/surfsense_web/app/dashboard/searchspaces/page.tsx +++ b/surfsense_web/app/dashboard/searchspaces/page.tsx @@ -1,46 +1,25 @@ "use client"; +import { useAtomValue } from "jotai"; import { motion } from "motion/react"; import { useRouter } from "next/navigation"; -import { toast } from "sonner"; import { SearchSpaceForm } from "@/components/search-space-form"; -import { authenticatedFetch } from "@/lib/auth-utils"; +import { createSearchSpaceMutationAtom } from "@/atoms/search-spaces/search-space-mutation.atoms"; export default function SearchSpacesPage() { const router = useRouter(); + const createSearchSpace = useAtomValue(createSearchSpaceMutationAtom); + const handleCreateSearchSpace = async (data: { name: string; description?: string }) => { - try { - const response = await authenticatedFetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces`, - { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - name: data.name, - description: data.description || "", - }), - } - ); + const result = await createSearchSpace.mutateAsync({ + name: data.name, + description: data.description || "", + }); - if (!response.ok) { - toast.error("Failed to create search space"); - throw new Error("Failed to create search space"); - } + // Redirect to the newly created search space's onboarding + router.push(`/dashboard/${result.id}/onboard`); - const result = await response.json(); - - toast.success("Search space created successfully", { - description: `"${data.name}" has been created.`, - }); - - // Redirect to the newly created search space's onboarding - router.push(`/dashboard/${result.id}/onboard`); - - return result; - } catch (error) { - console.error("Error creating search space:", error); - throw error; - } + return result; }; return ( From 734265c64591be22eb07b322fecf7b269cc5f4d1 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Fri, 12 Dec 2025 08:16:00 +0000 Subject: [PATCH 22/29] feat: migrate dashboard page to use searchSpacesAtom and deleteSearchSpaceMutationAtom --- surfsense_web/app/dashboard/page.tsx | 31 +++++-------------- .../app/dashboard/searchspaces/page.tsx | 4 +-- 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/surfsense_web/app/dashboard/page.tsx b/surfsense_web/app/dashboard/page.tsx index 21dbce82b..b1525a9db 100644 --- a/surfsense_web/app/dashboard/page.tsx +++ b/surfsense_web/app/dashboard/page.tsx @@ -1,5 +1,6 @@ "use client"; +import { useAtomValue } from "jotai"; import { AlertCircle, Loader2, Plus, Search, Trash2, UserCheck, Users } from "lucide-react"; import { motion, type Variants } from "motion/react"; import Image from "next/image"; @@ -35,7 +36,8 @@ import { import { Spotlight } from "@/components/ui/spotlight"; import { Tilt } from "@/components/ui/tilt"; import { useUser } from "@/hooks"; -import { useSearchSpaces } from "@/hooks/use-search-spaces"; +import { searchSpacesAtom } from "@/atoms/search-spaces/search-space-query.atoms"; +import { deleteSearchSpaceMutationAtom } from "@/atoms/search-spaces/search-space-mutation.atoms"; import { authenticatedFetch } from "@/lib/auth-utils"; /** @@ -154,7 +156,8 @@ const DashboardPage = () => { }, }; - const { searchSpaces, loading, error, refreshSearchSpaces } = useSearchSpaces(); + const { data: searchSpaces = [], isLoading: loading, error, refetch: refreshSearchSpaces } = useAtomValue(searchSpacesAtom); + const { mutateAsync: deleteSearchSpace } = useAtomValue(deleteSearchSpaceMutationAtom); // Fetch user details const { user, loading: isLoadingUser, error: userError } = useUser(); @@ -169,29 +172,11 @@ const DashboardPage = () => { }; if (loading) return ; - if (error) return ; + if (error) return ; const handleDeleteSearchSpace = async (id: number) => { - // Send DELETE request to the API - try { - const response = await authenticatedFetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces/${id}`, - { method: "DELETE" } - ); - - if (!response.ok) { - toast.error("Failed to delete search space"); - throw new Error("Failed to delete search space"); - } - - // Refresh the search spaces list after successful deletion - refreshSearchSpaces(); - } catch (error) { - console.error("Error deleting search space:", error); - toast.error("An error occurred while deleting the search space"); - return; - } - toast.success("Search space deleted successfully"); + await deleteSearchSpace({ id }); + refreshSearchSpaces(); }; return ( diff --git a/surfsense_web/app/dashboard/searchspaces/page.tsx b/surfsense_web/app/dashboard/searchspaces/page.tsx index db2e06d44..76e17c0ce 100644 --- a/surfsense_web/app/dashboard/searchspaces/page.tsx +++ b/surfsense_web/app/dashboard/searchspaces/page.tsx @@ -8,10 +8,10 @@ import { createSearchSpaceMutationAtom } from "@/atoms/search-spaces/search-spac export default function SearchSpacesPage() { const router = useRouter(); - const createSearchSpace = useAtomValue(createSearchSpaceMutationAtom); + const { mutateAsync: createSearchSpace } = useAtomValue(createSearchSpaceMutationAtom); const handleCreateSearchSpace = async (data: { name: string; description?: string }) => { - const result = await createSearchSpace.mutateAsync({ + const result = await createSearchSpace({ name: data.name, description: data.description || "", }); From 6be93182a1a88fd0439dfda8080b83b9539f2ed2 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Fri, 12 Dec 2025 08:38:32 +0000 Subject: [PATCH 23/29] refactor: migrate prompt-config-manager to use useQuery for search space fetch --- .../components/settings/prompt-config-manager.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/surfsense_web/components/settings/prompt-config-manager.tsx b/surfsense_web/components/settings/prompt-config-manager.tsx index a1199c10d..10f36c11e 100644 --- a/surfsense_web/components/settings/prompt-config-manager.tsx +++ b/surfsense_web/components/settings/prompt-config-manager.tsx @@ -23,8 +23,10 @@ import { Skeleton } from "@/components/ui/skeleton"; import { Switch } from "@/components/ui/switch"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Textarea } from "@/components/ui/textarea"; +import { useQuery } from "@tanstack/react-query"; import { type CommunityPrompt, useCommunityPrompts } from "@/hooks/use-community-prompts"; -import { useSearchSpace } from "@/hooks/use-search-space"; +import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; +import { cacheKeys } from "@/lib/query-client/cache-keys"; import { authenticatedFetch } from "@/lib/auth-utils"; interface PromptConfigManagerProps { @@ -32,9 +34,10 @@ interface PromptConfigManagerProps { } export function PromptConfigManager({ searchSpaceId }: PromptConfigManagerProps) { - const { searchSpace, loading, fetchSearchSpace } = useSearchSpace({ - searchSpaceId, - autoFetch: true, + const { data: searchSpace, isLoading: loading, refetch: fetchSearchSpace } = useQuery({ + queryKey: cacheKeys.searchSpaces.detail(searchSpaceId.toString()), + queryFn: () => searchSpacesApiService.getSearchSpace({ id: searchSpaceId }), + enabled: !!searchSpaceId, }); const { prompts, loading: loadingPrompts } = useCommunityPrompts(); From 1172a91a0724a15688c551dbb759654e32ea1801 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Fri, 12 Dec 2025 08:59:13 +0000 Subject: [PATCH 24/29] refactor: migrate AppSidebarProvider to use useQuery for search space fetch --- .../components/sidebar/AppSidebarProvider.tsx | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/surfsense_web/components/sidebar/AppSidebarProvider.tsx b/surfsense_web/components/sidebar/AppSidebarProvider.tsx index 76d92ba3b..fce8697f7 100644 --- a/surfsense_web/components/sidebar/AppSidebarProvider.tsx +++ b/surfsense_web/components/sidebar/AppSidebarProvider.tsx @@ -17,7 +17,10 @@ import { DialogHeader, DialogTitle, } from "@/components/ui/dialog"; -import { useSearchSpace, useUser } from "@/hooks"; +import { useUser } from "@/hooks"; +import { useQuery } from "@tanstack/react-query"; +import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; +import { cacheKeys } from "@/lib/query-client/cache-keys"; interface AppSidebarProviderProps { searchSpaceId: string; @@ -55,11 +58,15 @@ export function AppSidebarProvider({ }, [searchSpaceId]); const { - searchSpace, - loading: isLoadingSearchSpace, + data: searchSpace, + isLoading: isLoadingSearchSpace, error: searchSpaceError, - fetchSearchSpace, - } = useSearchSpace({ searchSpaceId }); + refetch: fetchSearchSpace, + } = useQuery({ + queryKey: cacheKeys.searchSpaces.detail(searchSpaceId), + queryFn: () => searchSpacesApiService.getSearchSpace({ id: Number(searchSpaceId) }), + enabled: !!searchSpaceId, + }); const { user } = useUser(); From 291bb0369accb8521e1e201fefd532b6f204d6ae Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Fri, 12 Dec 2025 09:18:16 +0000 Subject: [PATCH 25/29] refactor: migrate dashboard-breadcrumb to useQuery and remove obvious comments --- surfsense_web/components/chat/SourceDetailSheet.tsx | 1 - surfsense_web/components/dashboard-breadcrumb.tsx | 12 +++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/surfsense_web/components/chat/SourceDetailSheet.tsx b/surfsense_web/components/chat/SourceDetailSheet.tsx index 70b73fcf2..7f5ebbeab 100644 --- a/surfsense_web/components/chat/SourceDetailSheet.tsx +++ b/surfsense_web/components/chat/SourceDetailSheet.tsx @@ -52,7 +52,6 @@ export function SourceDetailSheet({ const highlightedChunkRef = useRef(null); const [summaryOpen, setSummaryOpen] = useState(false); - // Add useQuery to fetch document by chunk const { data: document, isLoading: isDocumentByChunkFetching, diff --git a/surfsense_web/components/dashboard-breadcrumb.tsx b/surfsense_web/components/dashboard-breadcrumb.tsx index f809f36b6..6335f9f1a 100644 --- a/surfsense_web/components/dashboard-breadcrumb.tsx +++ b/surfsense_web/components/dashboard-breadcrumb.tsx @@ -13,7 +13,9 @@ import { BreadcrumbPage, BreadcrumbSeparator, } from "@/components/ui/breadcrumb"; -import { useSearchSpace } from "@/hooks/use-search-space"; +import { useQuery } from "@tanstack/react-query"; +import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; +import { cacheKeys } from "@/lib/query-client/cache-keys"; import { authenticatedFetch, getBearerToken } from "@/lib/auth-utils"; interface BreadcrumbItemInterface { @@ -29,10 +31,10 @@ export function DashboardBreadcrumb() { const segments = pathname.split("/").filter(Boolean); const searchSpaceId = segments[0] === "dashboard" && segments[1] ? segments[1] : null; - // Fetch search space details if we have an ID - const { searchSpace } = useSearchSpace({ - searchSpaceId: searchSpaceId || "", - autoFetch: !!searchSpaceId, + const { data: searchSpace } = useQuery({ + queryKey: cacheKeys.searchSpaces.detail(searchSpaceId || ""), + queryFn: () => searchSpacesApiService.getSearchSpace({ id: Number(searchSpaceId) }), + enabled: !!searchSpaceId, }); // State to store document title for editor breadcrumb From ae1cd09013165381d7d26bd556d6a501bb294dcd Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Fri, 12 Dec 2025 09:20:52 +0000 Subject: [PATCH 26/29] refactor: delete unused search space hooks and update exports --- surfsense_web/hooks/index.ts | 1 - surfsense_web/hooks/use-search-space.ts | 60 ------------------- surfsense_web/hooks/use-search-spaces.ts | 76 ------------------------ 3 files changed, 137 deletions(-) delete mode 100644 surfsense_web/hooks/use-search-space.ts delete mode 100644 surfsense_web/hooks/use-search-spaces.ts diff --git a/surfsense_web/hooks/index.ts b/surfsense_web/hooks/index.ts index 2cea293e8..f7ef22534 100644 --- a/surfsense_web/hooks/index.ts +++ b/surfsense_web/hooks/index.ts @@ -1,5 +1,4 @@ export * from "./use-logs"; export * from "./use-rbac"; export * from "./use-search-source-connectors"; -export * from "./use-search-space"; export * from "./use-user"; diff --git a/surfsense_web/hooks/use-search-space.ts b/surfsense_web/hooks/use-search-space.ts deleted file mode 100644 index 849aad413..000000000 --- a/surfsense_web/hooks/use-search-space.ts +++ /dev/null @@ -1,60 +0,0 @@ -"use client"; - -import { useCallback, useEffect, useState } from "react"; -import { toast } from "sonner"; -import { authenticatedFetch } from "@/lib/auth-utils"; - -interface SearchSpace { - created_at: string; - id: number; - name: string; - description: string; - user_id: string; - citations_enabled: boolean; - qna_custom_instructions: string | null; -} - -interface UseSearchSpaceOptions { - searchSpaceId: string | number; - autoFetch?: boolean; -} - -export function useSearchSpace({ searchSpaceId, autoFetch = true }: UseSearchSpaceOptions) { - const [searchSpace, setSearchSpace] = useState(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - const fetchSearchSpace = useCallback(async () => { - try { - // Only run on client-side - if (typeof window === "undefined") return; - - setLoading(true); - const response = await authenticatedFetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces/${searchSpaceId}`, - { method: "GET" } - ); - - if (!response.ok) { - throw new Error(`Failed to fetch search space: ${response.status}`); - } - - const data = await response.json(); - setSearchSpace(data); - setError(null); - } catch (err: any) { - setError(err.message || "Failed to fetch search space"); - console.error("Error fetching search space:", err); - } finally { - setLoading(false); - } - }, [searchSpaceId]); - - useEffect(() => { - if (autoFetch) { - fetchSearchSpace(); - } - }, [autoFetch, fetchSearchSpace]); - - return { searchSpace, loading, error, fetchSearchSpace }; -} diff --git a/surfsense_web/hooks/use-search-spaces.ts b/surfsense_web/hooks/use-search-spaces.ts deleted file mode 100644 index 03a87881c..000000000 --- a/surfsense_web/hooks/use-search-spaces.ts +++ /dev/null @@ -1,76 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; -import { toast } from "sonner"; -import { authenticatedFetch } from "@/lib/auth-utils"; - -interface SearchSpace { - id: number; - name: string; - description: string; - created_at: string; - citations_enabled: boolean; - qna_custom_instructions: string | null; - member_count: number; - is_owner: boolean; -} - -export function useSearchSpaces() { - const [searchSpaces, setSearchSpaces] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - const fetchSearchSpaces = async () => { - try { - setLoading(true); - const response = await authenticatedFetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces`, - { method: "GET" } - ); - - if (!response.ok) { - toast.error("Failed to fetch search spaces"); - throw new Error("Failed to fetch search spaces"); - } - - const data = await response.json(); - setSearchSpaces(data); - setError(null); - } catch (err: any) { - setError(err.message || "Failed to fetch search spaces"); - console.error("Error fetching search spaces:", err); - } finally { - setLoading(false); - } - }; - - fetchSearchSpaces(); - }, []); - - // Function to refresh the search spaces list - const refreshSearchSpaces = async () => { - setLoading(true); - try { - const response = await authenticatedFetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces`, - { method: "GET" } - ); - - if (!response.ok) { - toast.error("Failed to fetch search spaces"); - throw new Error("Failed to fetch search spaces"); - } - - const data = await response.json(); - setSearchSpaces(data); - setError(null); - } catch (err: any) { - setError(err.message || "Failed to fetch search spaces"); - } finally { - setLoading(false); - } - }; - - return { searchSpaces, loading, error, refreshSearchSpaces }; -} From ccce47846b8c1fd7db03960405384617c10ddae5 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Mon, 15 Dec 2025 09:55:01 +0000 Subject: [PATCH 27/29] fix: build issues --- .../atoms/llm-config/llm-config-mutation.atoms.ts | 2 +- surfsense_web/atoms/llm-config/llm-config-query.atoms.ts | 2 +- surfsense_web/lib/query-client/cache-keys.ts | 8 +------- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/surfsense_web/atoms/llm-config/llm-config-mutation.atoms.ts b/surfsense_web/atoms/llm-config/llm-config-mutation.atoms.ts index 8d0bedc82..f28b1d708 100644 --- a/surfsense_web/atoms/llm-config/llm-config-mutation.atoms.ts +++ b/surfsense_web/atoms/llm-config/llm-config-mutation.atoms.ts @@ -1,6 +1,5 @@ import { atomWithMutation } from "jotai-tanstack-query"; import { toast } from "sonner"; -import { activeSearchSpaceIdAtom } from "@/atoms/seach-spaces/seach-space-queries.atom"; import type { CreateLLMConfigRequest, DeleteLLMConfigRequest, @@ -12,6 +11,7 @@ import type { import { llmConfigApiService } from "@/lib/apis/llm-config-api.service"; import { cacheKeys } from "@/lib/query-client/cache-keys"; import { queryClient } from "@/lib/query-client/client"; +import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-query.atoms"; export const createLLMConfigMutationAtom = atomWithMutation((get) => { const searchSpaceId = get(activeSearchSpaceIdAtom); diff --git a/surfsense_web/atoms/llm-config/llm-config-query.atoms.ts b/surfsense_web/atoms/llm-config/llm-config-query.atoms.ts index 66c11b61a..22ae63d7f 100644 --- a/surfsense_web/atoms/llm-config/llm-config-query.atoms.ts +++ b/surfsense_web/atoms/llm-config/llm-config-query.atoms.ts @@ -1,7 +1,7 @@ import { atomWithQuery } from "jotai-tanstack-query"; -import { activeSearchSpaceIdAtom } from "@/atoms/seach-spaces/seach-space-queries.atom"; import { llmConfigApiService } from "@/lib/apis/llm-config-api.service"; import { cacheKeys } from "@/lib/query-client/cache-keys"; +import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-query.atoms"; export const llmConfigsAtom = atomWithQuery((get) => { const searchSpaceId = get(activeSearchSpaceIdAtom); diff --git a/surfsense_web/lib/query-client/cache-keys.ts b/surfsense_web/lib/query-client/cache-keys.ts index e488d2c0e..797c40b65 100644 --- a/surfsense_web/lib/query-client/cache-keys.ts +++ b/surfsense_web/lib/query-client/cache-keys.ts @@ -40,11 +40,5 @@ export const cacheKeys = { ["search-spaces", ...(queries ? Object.values(queries) : [])] as const, detail: (searchSpaceId: string) => ["search-spaces", searchSpaceId] as const, communityPrompts: ["search-spaces", "community-prompts"] as const, - }, - llmConfigs: { - all: (searchSpaceId: string) => ["llm-configs", searchSpaceId] as const, - global: ["llm-configs", "global"] as const, - detail: (searchSpaceId: string, llmConfigId: string) => ["llm-configs", searchSpaceId, llmConfigId] as const, - preferences: (searchSpaceId: string) => ["llm-configs", "preferences", searchSpaceId] as const, - }, + } }; From 8727acb5ad4d1a9309be6d0155ed99d3a43c5875 Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Mon, 15 Dec 2025 10:24:13 +0000 Subject: [PATCH 28/29] refactor: migrate community prompts from imperative hook to jotai atom --- .../components/onboard/setup-prompt-step.tsx | 7 ++- .../settings/prompt-config-manager.tsx | 7 ++- surfsense_web/hooks/use-community-prompts.ts | 45 ------------------- 3 files changed, 10 insertions(+), 49 deletions(-) delete mode 100644 surfsense_web/hooks/use-community-prompts.ts diff --git a/surfsense_web/components/onboard/setup-prompt-step.tsx b/surfsense_web/components/onboard/setup-prompt-step.tsx index 899d856fa..3327b5489 100644 --- a/surfsense_web/components/onboard/setup-prompt-step.tsx +++ b/surfsense_web/components/onboard/setup-prompt-step.tsx @@ -12,8 +12,9 @@ import { ScrollArea } from "@/components/ui/scroll-area"; import { Switch } from "@/components/ui/switch"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Textarea } from "@/components/ui/textarea"; -import { type CommunityPrompt, useCommunityPrompts } from "@/hooks/use-community-prompts"; +import { communityPromptsAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { authenticatedFetch } from "@/lib/auth-utils"; +import { useAtomValue } from "jotai"; interface SetupPromptStepProps { searchSpaceId: number; @@ -21,7 +22,9 @@ interface SetupPromptStepProps { } export function SetupPromptStep({ searchSpaceId, onComplete }: SetupPromptStepProps) { - const { prompts, loading: loadingPrompts } = useCommunityPrompts(); + const communityPromptsQuery = useAtomValue(communityPromptsAtom); + const prompts = communityPromptsQuery.data || []; + const loadingPrompts = communityPromptsQuery.isPending; const [enableCitations, setEnableCitations] = useState(true); const [customInstructions, setCustomInstructions] = useState(""); const [saving, setSaving] = useState(false); diff --git a/surfsense_web/components/settings/prompt-config-manager.tsx b/surfsense_web/components/settings/prompt-config-manager.tsx index 10f36c11e..082f438b0 100644 --- a/surfsense_web/components/settings/prompt-config-manager.tsx +++ b/surfsense_web/components/settings/prompt-config-manager.tsx @@ -24,9 +24,10 @@ import { Switch } from "@/components/ui/switch"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Textarea } from "@/components/ui/textarea"; import { useQuery } from "@tanstack/react-query"; -import { type CommunityPrompt, useCommunityPrompts } from "@/hooks/use-community-prompts"; +import { communityPromptsAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { searchSpacesApiService } from "@/lib/apis/search-spaces-api.service"; import { cacheKeys } from "@/lib/query-client/cache-keys"; +import { useAtomValue } from "jotai"; import { authenticatedFetch } from "@/lib/auth-utils"; interface PromptConfigManagerProps { @@ -39,7 +40,9 @@ export function PromptConfigManager({ searchSpaceId }: PromptConfigManagerProps) queryFn: () => searchSpacesApiService.getSearchSpace({ id: searchSpaceId }), enabled: !!searchSpaceId, }); - const { prompts, loading: loadingPrompts } = useCommunityPrompts(); + const communityPromptsQuery = useAtomValue(communityPromptsAtom); + const prompts = communityPromptsQuery.data || []; + const loadingPrompts = communityPromptsQuery.isPending; const [enableCitations, setEnableCitations] = useState(true); const [customInstructions, setCustomInstructions] = useState(""); diff --git a/surfsense_web/hooks/use-community-prompts.ts b/surfsense_web/hooks/use-community-prompts.ts deleted file mode 100644 index 3b4ac59db..000000000 --- a/surfsense_web/hooks/use-community-prompts.ts +++ /dev/null @@ -1,45 +0,0 @@ -"use client"; - -import { useCallback, useEffect, useState } from "react"; - -export interface CommunityPrompt { - key: string; - value: string; - author: string; - link: string | null; - category?: string; -} - -export function useCommunityPrompts() { - const [prompts, setPrompts] = useState([]); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - const fetchPrompts = useCallback(async () => { - try { - setLoading(true); - const response = await fetch( - `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces/prompts/community` - ); - - if (!response.ok) { - throw new Error(`Failed to fetch community prompts: ${response.status}`); - } - - const data = await response.json(); - setPrompts(data); - setError(null); - } catch (err: any) { - setError(err.message || "Failed to fetch community prompts"); - console.error("Error fetching community prompts:", err); - } finally { - setLoading(false); - } - }, []); - - useEffect(() => { - fetchPrompts(); - }, [fetchPrompts]); - - return { prompts, loading, error, refetch: fetchPrompts }; -} From 3c514756c767411dc8093c96c9f90b36dfe09eef Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Mon, 15 Dec 2025 10:42:35 +0000 Subject: [PATCH 29/29] refactor: use destructuring pattern for community prompts atom --- surfsense_web/components/onboard/setup-prompt-step.tsx | 4 +--- surfsense_web/components/settings/prompt-config-manager.tsx | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/surfsense_web/components/onboard/setup-prompt-step.tsx b/surfsense_web/components/onboard/setup-prompt-step.tsx index 3327b5489..5e3683031 100644 --- a/surfsense_web/components/onboard/setup-prompt-step.tsx +++ b/surfsense_web/components/onboard/setup-prompt-step.tsx @@ -22,9 +22,7 @@ interface SetupPromptStepProps { } export function SetupPromptStep({ searchSpaceId, onComplete }: SetupPromptStepProps) { - const communityPromptsQuery = useAtomValue(communityPromptsAtom); - const prompts = communityPromptsQuery.data || []; - const loadingPrompts = communityPromptsQuery.isPending; + const { data: prompts = [], isPending: loadingPrompts } = useAtomValue(communityPromptsAtom); const [enableCitations, setEnableCitations] = useState(true); const [customInstructions, setCustomInstructions] = useState(""); const [saving, setSaving] = useState(false); diff --git a/surfsense_web/components/settings/prompt-config-manager.tsx b/surfsense_web/components/settings/prompt-config-manager.tsx index 082f438b0..dae842305 100644 --- a/surfsense_web/components/settings/prompt-config-manager.tsx +++ b/surfsense_web/components/settings/prompt-config-manager.tsx @@ -40,9 +40,7 @@ export function PromptConfigManager({ searchSpaceId }: PromptConfigManagerProps) queryFn: () => searchSpacesApiService.getSearchSpace({ id: searchSpaceId }), enabled: !!searchSpaceId, }); - const communityPromptsQuery = useAtomValue(communityPromptsAtom); - const prompts = communityPromptsQuery.data || []; - const loadingPrompts = communityPromptsQuery.isPending; + const { data: prompts = [], isPending: loadingPrompts } = useAtomValue(communityPromptsAtom); const [enableCitations, setEnableCitations] = useState(true); const [customInstructions, setCustomInstructions] = useState("");