mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-06 20:15:17 +02:00
Merge pull request #583 from CREDO23/feat/add-jotai-tanstack-search-spaces-new
[Feat] Search spaces | Add jotai & tanstack
This commit is contained in:
commit
8aedead33e
25 changed files with 401 additions and 267 deletions
|
|
@ -9,7 +9,7 @@ import type React from "react";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { activeChathatUIAtom, activeChatIdAtom } from "@/atoms/chats/ui.atoms";
|
import { activeChathatUIAtom, activeChatIdAtom } from "@/atoms/chats/ui.atoms";
|
||||||
import { llmPreferencesAtom } from "@/atoms/llm-config/llm-config-query.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-query.atoms";
|
||||||
import { ChatPanelContainer } from "@/components/chat/ChatPanel/ChatPanelContainer";
|
import { ChatPanelContainer } from "@/components/chat/ChatPanel/ChatPanelContainer";
|
||||||
import { DashboardBreadcrumb } from "@/components/dashboard-breadcrumb";
|
import { DashboardBreadcrumb } from "@/components/dashboard-breadcrumb";
|
||||||
import { LanguageSwitcher } from "@/components/LanguageSwitcher";
|
import { LanguageSwitcher } from "@/components/LanguageSwitcher";
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useAtomValue } from "jotai";
|
||||||
import { AlertCircle, Loader2, Plus, Search, Trash2, UserCheck, Users } from "lucide-react";
|
import { AlertCircle, Loader2, Plus, Search, Trash2, UserCheck, Users } from "lucide-react";
|
||||||
import { motion, type Variants } from "motion/react";
|
import { motion, type Variants } from "motion/react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
@ -35,7 +36,8 @@ import {
|
||||||
import { Spotlight } from "@/components/ui/spotlight";
|
import { Spotlight } from "@/components/ui/spotlight";
|
||||||
import { Tilt } from "@/components/ui/tilt";
|
import { Tilt } from "@/components/ui/tilt";
|
||||||
import { useUser } from "@/hooks";
|
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";
|
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
|
// Fetch user details
|
||||||
const { user, loading: isLoadingUser, error: userError } = useUser();
|
const { user, loading: isLoadingUser, error: userError } = useUser();
|
||||||
|
|
@ -169,29 +172,11 @@ const DashboardPage = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) return <LoadingScreen />;
|
if (loading) return <LoadingScreen />;
|
||||||
if (error) return <ErrorScreen message={error} />;
|
if (error) return <ErrorScreen message={error?.message || 'Failed to load search spaces'} />;
|
||||||
|
|
||||||
const handleDeleteSearchSpace = async (id: number) => {
|
const handleDeleteSearchSpace = async (id: number) => {
|
||||||
// Send DELETE request to the API
|
await deleteSearchSpace({ id });
|
||||||
try {
|
refreshSearchSpaces();
|
||||||
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");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,25 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useAtomValue } from "jotai";
|
||||||
import { motion } from "motion/react";
|
import { motion } from "motion/react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { toast } from "sonner";
|
|
||||||
import { SearchSpaceForm } from "@/components/search-space-form";
|
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() {
|
export default function SearchSpacesPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const { mutateAsync: createSearchSpace } = useAtomValue(createSearchSpaceMutationAtom);
|
||||||
|
|
||||||
const handleCreateSearchSpace = async (data: { name: string; description?: string }) => {
|
const handleCreateSearchSpace = async (data: { name: string; description?: string }) => {
|
||||||
try {
|
const result = await createSearchSpace({
|
||||||
const response = await authenticatedFetch(
|
name: data.name,
|
||||||
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces`,
|
description: data.description || "",
|
||||||
{
|
});
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({
|
|
||||||
name: data.name,
|
|
||||||
description: data.description || "",
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
// Redirect to the newly created search space's onboarding
|
||||||
toast.error("Failed to create search space");
|
router.push(`/dashboard/${result.id}/onboard`);
|
||||||
throw new Error("Failed to create search space");
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await response.json();
|
return result;
|
||||||
|
|
||||||
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 (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { chatsApiService } from "@/lib/apis/chats-api.service";
|
||||||
import { getBearerToken } from "@/lib/auth-utils";
|
import { getBearerToken } from "@/lib/auth-utils";
|
||||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||||
import { queryClient } from "@/lib/query-client/client";
|
import { queryClient } from "@/lib/query-client/client";
|
||||||
import { activeSearchSpaceIdAtom } from "../seach-spaces/seach-space-queries.atom";
|
import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-query.atoms";
|
||||||
import { globalChatsQueryParamsAtom } from "./ui.atoms";
|
import { globalChatsQueryParamsAtom } from "./ui.atoms";
|
||||||
|
|
||||||
export const deleteChatMutationAtom = atomWithMutation((get) => {
|
export const deleteChatMutationAtom = atomWithMutation((get) => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { atomWithQuery } from "jotai-tanstack-query";
|
import { atomWithQuery } from "jotai-tanstack-query";
|
||||||
import { activeSearchSpaceIdAtom } from "@/atoms/seach-spaces/seach-space-queries.atom";
|
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||||
import { chatsApiService } from "@/lib/apis/chats-api.service";
|
import { chatsApiService } from "@/lib/apis/chats-api.service";
|
||||||
import { podcastsApiService } from "@/lib/apis/podcasts-api.service";
|
import { podcastsApiService } from "@/lib/apis/podcasts-api.service";
|
||||||
import { getBearerToken } from "@/lib/auth-utils";
|
import { getBearerToken } from "@/lib/auth-utils";
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { atomWithMutation } from "jotai-tanstack-query";
|
import { atomWithMutation } from "jotai-tanstack-query";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { activeSearchSpaceIdAtom } from "@/atoms/seach-spaces/seach-space-queries.atom";
|
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||||
import type {
|
import type {
|
||||||
CreateDocumentRequest,
|
CreateDocumentRequest,
|
||||||
DeleteDocumentRequest,
|
DeleteDocumentRequest,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { atomWithQuery } from "jotai-tanstack-query";
|
import { atomWithQuery } from "jotai-tanstack-query";
|
||||||
import { activeSearchSpaceIdAtom } from "@/atoms/seach-spaces/seach-space-queries.atom";
|
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||||
import type { SearchDocumentsRequest } from "@/contracts/types/document.types";
|
import type { SearchDocumentsRequest } from "@/contracts/types/document.types";
|
||||||
import { documentsApiService } from "@/lib/apis/documents-api.service";
|
import { documentsApiService } from "@/lib/apis/documents-api.service";
|
||||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { atomWithMutation } from "jotai-tanstack-query";
|
import { atomWithMutation } from "jotai-tanstack-query";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { activeSearchSpaceIdAtom } from "@/atoms/seach-spaces/seach-space-queries.atom";
|
|
||||||
import type {
|
import type {
|
||||||
CreateLLMConfigRequest,
|
CreateLLMConfigRequest,
|
||||||
DeleteLLMConfigRequest,
|
DeleteLLMConfigRequest,
|
||||||
|
|
@ -12,6 +11,7 @@ import type {
|
||||||
import { llmConfigApiService } from "@/lib/apis/llm-config-api.service";
|
import { llmConfigApiService } from "@/lib/apis/llm-config-api.service";
|
||||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||||
import { queryClient } from "@/lib/query-client/client";
|
import { queryClient } from "@/lib/query-client/client";
|
||||||
|
import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-query.atoms";
|
||||||
|
|
||||||
export const createLLMConfigMutationAtom = atomWithMutation((get) => {
|
export const createLLMConfigMutationAtom = atomWithMutation((get) => {
|
||||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { atomWithQuery } from "jotai-tanstack-query";
|
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 { llmConfigApiService } from "@/lib/apis/llm-config-api.service";
|
||||||
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
import { cacheKeys } from "@/lib/query-client/cache-keys";
|
||||||
|
import { activeSearchSpaceIdAtom } from "../search-spaces/search-space-query.atoms";
|
||||||
|
|
||||||
export const llmConfigsAtom = atomWithQuery((get) => {
|
export const llmConfigsAtom = atomWithQuery((get) => {
|
||||||
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
const searchSpaceId = get(activeSearchSpaceIdAtom);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { atomWithMutation } from "jotai-tanstack-query";
|
import { atomWithMutation } from "jotai-tanstack-query";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { activeSearchSpaceIdAtom } from "@/atoms/seach-spaces/seach-space-queries.atom";
|
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
|
||||||
import type {
|
import type {
|
||||||
DeletePodcastRequest,
|
DeletePodcastRequest,
|
||||||
GeneratePodcastRequest,
|
GeneratePodcastRequest,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
import { atom } from "jotai";
|
|
||||||
|
|
||||||
export const activeSearchSpaceIdAtom = atom<string | null>(null);
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { atomWithMutation } from "jotai-tanstack-query";
|
||||||
|
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";
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
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.id) {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: cacheKeys.searchSpaces.detail(String(request.id)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
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)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
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";
|
||||||
|
|
||||||
|
export const activeSearchSpaceIdAtom = atom<string | null>(null);
|
||||||
|
|
||||||
|
export const searchSpacesQueryParamsAtom = atom<GetSearchSpacesRequest["queryParams"]>({
|
||||||
|
skip: 0,
|
||||||
|
limit: 10,
|
||||||
|
owned_only: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const searchSpacesAtom = atomWithQuery((get) => {
|
||||||
|
const queryParams = get(searchSpacesQueryParamsAtom);
|
||||||
|
|
||||||
|
return {
|
||||||
|
queryKey: cacheKeys.searchSpaces.withQueryParams(queryParams),
|
||||||
|
staleTime: 5 * 60 * 1000,
|
||||||
|
queryFn: async () => {
|
||||||
|
return searchSpacesApiService.getSearchSpaces({
|
||||||
|
queryParams,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export const communityPromptsAtom = atomWithQuery(() => {
|
||||||
|
return {
|
||||||
|
queryKey: cacheKeys.searchSpaces.communityPrompts,
|
||||||
|
staleTime: 30 * 60 * 1000,
|
||||||
|
queryFn: async () => {
|
||||||
|
return searchSpacesApiService.getCommunityPrompts();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
@ -52,7 +52,6 @@ export function SourceDetailSheet({
|
||||||
const highlightedChunkRef = useRef<HTMLDivElement>(null);
|
const highlightedChunkRef = useRef<HTMLDivElement>(null);
|
||||||
const [summaryOpen, setSummaryOpen] = useState(false);
|
const [summaryOpen, setSummaryOpen] = useState(false);
|
||||||
|
|
||||||
// Add useQuery to fetch document by chunk
|
|
||||||
const {
|
const {
|
||||||
data: document,
|
data: document,
|
||||||
isLoading: isDocumentByChunkFetching,
|
isLoading: isDocumentByChunkFetching,
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,9 @@ import {
|
||||||
BreadcrumbPage,
|
BreadcrumbPage,
|
||||||
BreadcrumbSeparator,
|
BreadcrumbSeparator,
|
||||||
} from "@/components/ui/breadcrumb";
|
} 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";
|
import { authenticatedFetch, getBearerToken } from "@/lib/auth-utils";
|
||||||
|
|
||||||
interface BreadcrumbItemInterface {
|
interface BreadcrumbItemInterface {
|
||||||
|
|
@ -29,10 +31,10 @@ export function DashboardBreadcrumb() {
|
||||||
const segments = pathname.split("/").filter(Boolean);
|
const segments = pathname.split("/").filter(Boolean);
|
||||||
const searchSpaceId = segments[0] === "dashboard" && segments[1] ? segments[1] : null;
|
const searchSpaceId = segments[0] === "dashboard" && segments[1] ? segments[1] : null;
|
||||||
|
|
||||||
// Fetch search space details if we have an ID
|
const { data: searchSpace } = useQuery({
|
||||||
const { searchSpace } = useSearchSpace({
|
queryKey: cacheKeys.searchSpaces.detail(searchSpaceId || ""),
|
||||||
searchSpaceId: searchSpaceId || "",
|
queryFn: () => searchSpacesApiService.getSearchSpace({ id: Number(searchSpaceId) }),
|
||||||
autoFetch: !!searchSpaceId,
|
enabled: !!searchSpaceId,
|
||||||
});
|
});
|
||||||
|
|
||||||
// State to store document title for editor breadcrumb
|
// State to store document title for editor breadcrumb
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,9 @@ import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
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 { authenticatedFetch } from "@/lib/auth-utils";
|
||||||
|
import { useAtomValue } from "jotai";
|
||||||
|
|
||||||
interface SetupPromptStepProps {
|
interface SetupPromptStepProps {
|
||||||
searchSpaceId: number;
|
searchSpaceId: number;
|
||||||
|
|
@ -21,7 +22,7 @@ interface SetupPromptStepProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SetupPromptStep({ searchSpaceId, onComplete }: SetupPromptStepProps) {
|
export function SetupPromptStep({ searchSpaceId, onComplete }: SetupPromptStepProps) {
|
||||||
const { prompts, loading: loadingPrompts } = useCommunityPrompts();
|
const { data: prompts = [], isPending: loadingPrompts } = useAtomValue(communityPromptsAtom);
|
||||||
const [enableCitations, setEnableCitations] = useState(true);
|
const [enableCitations, setEnableCitations] = useState(true);
|
||||||
const [customInstructions, setCustomInstructions] = useState("");
|
const [customInstructions, setCustomInstructions] = useState("");
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,11 @@ import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { type CommunityPrompt, useCommunityPrompts } from "@/hooks/use-community-prompts";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { useSearchSpace } from "@/hooks/use-search-space";
|
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";
|
import { authenticatedFetch } from "@/lib/auth-utils";
|
||||||
|
|
||||||
interface PromptConfigManagerProps {
|
interface PromptConfigManagerProps {
|
||||||
|
|
@ -32,11 +35,12 @@ interface PromptConfigManagerProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PromptConfigManager({ searchSpaceId }: PromptConfigManagerProps) {
|
export function PromptConfigManager({ searchSpaceId }: PromptConfigManagerProps) {
|
||||||
const { searchSpace, loading, fetchSearchSpace } = useSearchSpace({
|
const { data: searchSpace, isLoading: loading, refetch: fetchSearchSpace } = useQuery({
|
||||||
searchSpaceId,
|
queryKey: cacheKeys.searchSpaces.detail(searchSpaceId.toString()),
|
||||||
autoFetch: true,
|
queryFn: () => searchSpacesApiService.getSearchSpace({ id: searchSpaceId }),
|
||||||
|
enabled: !!searchSpaceId,
|
||||||
});
|
});
|
||||||
const { prompts, loading: loadingPrompts } = useCommunityPrompts();
|
const { data: prompts = [], isPending: loadingPrompts } = useAtomValue(communityPromptsAtom);
|
||||||
|
|
||||||
const [enableCitations, setEnableCitations] = useState(true);
|
const [enableCitations, setEnableCitations] = useState(true);
|
||||||
const [customInstructions, setCustomInstructions] = useState("");
|
const [customInstructions, setCustomInstructions] = useState("");
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,10 @@ import {
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/components/ui/dialog";
|
} 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 {
|
interface AppSidebarProviderProps {
|
||||||
searchSpaceId: string;
|
searchSpaceId: string;
|
||||||
|
|
@ -55,11 +58,15 @@ export function AppSidebarProvider({
|
||||||
}, [searchSpaceId]);
|
}, [searchSpaceId]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
searchSpace,
|
data: searchSpace,
|
||||||
loading: isLoadingSearchSpace,
|
isLoading: isLoadingSearchSpace,
|
||||||
error: searchSpaceError,
|
error: searchSpaceError,
|
||||||
fetchSearchSpace,
|
refetch: fetchSearchSpace,
|
||||||
} = useSearchSpace({ searchSpaceId });
|
} = useQuery({
|
||||||
|
queryKey: cacheKeys.searchSpaces.detail(searchSpaceId),
|
||||||
|
queryFn: () => searchSpacesApiService.getSearchSpace({ id: Number(searchSpaceId) }),
|
||||||
|
enabled: !!searchSpaceId,
|
||||||
|
});
|
||||||
|
|
||||||
const { user } = useUser();
|
const { user } = useUser();
|
||||||
|
|
||||||
|
|
|
||||||
94
surfsense_web/contracts/types/search-space.types.ts
Normal file
94
surfsense_web/contracts/types/search-space.types.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
import { z } from "zod";
|
||||||
|
import { paginationQueryParams } from ".";
|
||||||
|
|
||||||
|
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 });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<typeof searchSpace>;
|
||||||
|
export type GetSearchSpacesRequest = z.infer<typeof getSearchSpacesRequest>;
|
||||||
|
export type GetSearchSpacesResponse = z.infer<typeof getSearchSpacesResponse>;
|
||||||
|
export type CreateSearchSpaceRequest = z.infer<typeof createSearchSpaceRequest>;
|
||||||
|
export type CreateSearchSpaceResponse = z.infer<typeof createSearchSpaceResponse>;
|
||||||
|
export type GetCommunityPromptsResponse = z.infer<typeof getCommunityPromptsResponse>;
|
||||||
|
export type GetSearchSpaceRequest = z.infer<typeof getSearchSpaceRequest>;
|
||||||
|
export type GetSearchSpaceResponse = z.infer<typeof getSearchSpaceResponse>;
|
||||||
|
export type UpdateSearchSpaceRequest = z.infer<typeof updateSearchSpaceRequest>;
|
||||||
|
export type UpdateSearchSpaceResponse = z.infer<typeof updateSearchSpaceResponse>;
|
||||||
|
export type DeleteSearchSpaceRequest = z.infer<typeof deleteSearchSpaceRequest>;
|
||||||
|
export type DeleteSearchSpaceResponse = z.infer<typeof deleteSearchSpaceResponse>;
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
export * from "./use-logs";
|
export * from "./use-logs";
|
||||||
export * from "./use-rbac";
|
export * from "./use-rbac";
|
||||||
export * from "./use-search-source-connectors";
|
export * from "./use-search-source-connectors";
|
||||||
export * from "./use-search-space";
|
|
||||||
export * from "./use-user";
|
export * from "./use-user";
|
||||||
|
|
|
||||||
|
|
@ -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<CommunityPrompt[]>([]);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [error, setError] = useState<string | null>(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 };
|
|
||||||
}
|
|
||||||
|
|
@ -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<SearchSpace | null>(null);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [error, setError] = useState<string | null>(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 };
|
|
||||||
}
|
|
||||||
|
|
@ -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<SearchSpace[]>([]);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [error, setError] = useState<string | null>(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 };
|
|
||||||
}
|
|
||||||
128
surfsense_web/lib/apis/search-spaces-api.service.ts
Normal file
128
surfsense_web/lib/apis/search-spaces-api.service.ts
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
import {
|
||||||
|
type CreateSearchSpaceRequest,
|
||||||
|
type DeleteSearchSpaceRequest,
|
||||||
|
type GetSearchSpaceRequest,
|
||||||
|
type GetSearchSpacesRequest,
|
||||||
|
type UpdateSearchSpaceRequest,
|
||||||
|
createSearchSpaceRequest,
|
||||||
|
createSearchSpaceResponse,
|
||||||
|
deleteSearchSpaceRequest,
|
||||||
|
deleteSearchSpaceResponse,
|
||||||
|
getCommunityPromptsResponse,
|
||||||
|
getSearchSpaceRequest,
|
||||||
|
getSearchSpaceResponse,
|
||||||
|
getSearchSpacesRequest,
|
||||||
|
getSearchSpacesResponse,
|
||||||
|
updateSearchSpaceRequest,
|
||||||
|
updateSearchSpaceResponse,
|
||||||
|
} 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);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get community-curated prompts for search space system instructions
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
|
@ -2,6 +2,7 @@ import type { GetChatsRequest } from "@/contracts/types/chat.types";
|
||||||
import type { GetDocumentsRequest } from "@/contracts/types/document.types";
|
import type { GetDocumentsRequest } from "@/contracts/types/document.types";
|
||||||
import type { GetLLMConfigsRequest } from "@/contracts/types/llm-config.types";
|
import type { GetLLMConfigsRequest } from "@/contracts/types/llm-config.types";
|
||||||
import type { GetPodcastsRequest } from "@/contracts/types/podcast.types";
|
import type { GetPodcastsRequest } from "@/contracts/types/podcast.types";
|
||||||
|
import type { GetSearchSpacesRequest } from "@/contracts/types/search-space.types";
|
||||||
|
|
||||||
export const cacheKeys = {
|
export const cacheKeys = {
|
||||||
chats: {
|
chats: {
|
||||||
|
|
@ -33,4 +34,11 @@ export const cacheKeys = {
|
||||||
auth: {
|
auth: {
|
||||||
user: ["auth", "user"] as const,
|
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,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue