organize seach space apis

This commit is contained in:
thierryverse 2025-11-12 16:22:30 +02:00
parent 43362a383e
commit 78ab020d80
15 changed files with 154 additions and 42 deletions

View file

@ -55,8 +55,8 @@ import {
SelectValue,
} from "@/components/ui/select";
import { useAtom, useAtomValue } from "jotai";
import { activeSearchSpaceChatsAtom } from "@/atoms/chats/queries/active-search-space-chats.query.atom";
import { deleteChatMutationAtom } from "@/atoms/chats/mutations/delete-chat.mutation.atom";
import { activeSearchSpaceChatsAtom } from "@/atoms/chats/chat-queries.atom";
import { deleteChatMutationAtom } from "@/atoms/chats/chat-mutations.atom";
export interface Chat {
created_at: string;

View file

@ -27,9 +27,9 @@ import {
} from "@/components/ui/sidebar";
import { useLLMPreferences } from "@/hooks/use-llm-configs";
import { cn } from "@/lib/utils";
import { activeChatIdAtom } from "@/atoms/chats/queries/active-chat.query.atom";
import { chatUIAtom } from "@/atoms/chats/active-chat.atom";
import { activeSearchSpaceIdAtom } from "@/atoms/seach-spaces/active-seach-space.atom";
import { activeChatIdAtom } from "@/atoms/chats/chat-queries.atom";
import { chatUIAtom } from "@/atoms/chats/chat-uis.atom";
import { activeSearchSpaceIdAtom } from "@/atoms/seach-spaces/seach-space-queries.atom";
export function DashboardClientLayout({
children,

View file

@ -236,7 +236,7 @@ const DashboardPage = () => {
{searchSpaces &&
searchSpaces.length > 0 &&
searchSpaces.map((space) => (
<motion.div key={space.id} variants={itemVariants} className="aspect-[4/3]">
<motion.div key={space.id} variants={itemVariants} className="aspect-4/3">
<Tilt
rotationFactor={6}
isRevese

View file

@ -1,6 +1,6 @@
import { atomWithMutation } from "jotai-tanstack-query";
import { deleteChat } from "@/lib/apis/chat-apis";
import { activeSearchSpaceIdAtom } from "../../seach-spaces/active-seach-space.atom";
import { deleteChat } from "@/lib/apis/chats.api";
import { activeSearchSpaceIdAtom } from "../seach-spaces/seach-space-queries.atom";
import { queryClient } from "@/lib/query-client/client";
import { cacheKeys } from "@/lib/query-client/cache-keys";
import { toast } from "sonner";

View file

@ -2,9 +2,13 @@ import { atom } from "jotai";
import { atomWithQuery } from "jotai-tanstack-query";
import type { ChatDetails } from "@/app/dashboard/[search_space_id]/chats/chats-client";
import type { PodcastItem } from "@/app/dashboard/[search_space_id]/podcasts/podcasts-client";
import { fetchChatDetails } from "@/lib/apis/chat-apis";
import { getPodcastByChatId } from "@/lib/apis/podcast-apis";
import {
fetchChatDetails,
fetchChatsBySearchSpace,
} from "@/lib/apis/chats.api";
import { getPodcastByChatId } from "@/lib/apis/podcasts.api";
import { cacheKeys } from "@/lib/query-client/cache-keys";
import { activeSearchSpaceIdAtom } from "@/atoms/seach-spaces/seach-space-queries.atom";
type ActiveChatState = {
chatId: string | null;
@ -38,3 +42,23 @@ export const activeChatAtom = atomWithQuery<ActiveChatState>((get) => {
},
};
});
export const activeSearchSpaceChatsAtom = atomWithQuery((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
const authToken = localStorage.getItem("surfsense_bearer_token");
return {
queryKey: cacheKeys.activeSearchSpace.chats(searchSpaceId ?? ""),
enabled: !!searchSpaceId && !!authToken,
queryFn: async () => {
if (!authToken) {
throw new Error("No authentication token found");
}
if (!searchSpaceId) {
throw new Error("No search space id found");
}
return fetchChatsBySearchSpace(searchSpaceId, authToken);
},
};
});

View file

@ -1,24 +0,0 @@
import { atomWithQuery } from "jotai-tanstack-query";
import { fetchChatsBySearchSpace } from "@/lib/apis/chat-apis";
import { activeSearchSpaceIdAtom } from "../../seach-spaces/active-seach-space.atom";
import { cacheKeys } from "@/lib/query-client/cache-keys";
export const activeSearchSpaceChatsAtom = atomWithQuery((get) => {
const searchSpaceId = get(activeSearchSpaceIdAtom);
const authToken = localStorage.getItem("surfsense_bearer_token");
return {
queryKey: cacheKeys.activeSearchSpace.chats(searchSpaceId ?? ""),
enabled: !!searchSpaceId && !!authToken,
queryFn: async () => {
if (!authToken) {
throw new Error("No authentication token found");
}
if (!searchSpaceId) {
throw new Error("No search space id found");
}
return fetchChatsBySearchSpace(searchSpaceId, authToken);
},
};
});

View file

@ -1,3 +1,3 @@
import { atom } from "jotai";
export const activeSearchSpaceIdAtom = atom<string | null>(null);
export const activeSearchSpaceIdAtom = atom<string | null>(null);

View file

@ -2,10 +2,10 @@
import { useAtom, useAtomValue } from "jotai";
import { LoaderIcon, PanelRight, TriangleAlert } from "lucide-react";
import { toast } from "sonner";
import { generatePodcast } from "@/lib/apis/podcast-apis";
import { generatePodcast } from "@/lib/apis/podcasts.api";
import { cn } from "@/lib/utils";
import { activeChatAtom, activeChatIdAtom } from "@/atoms/chats/queries/active-chat.query.atom";
import { chatUIAtom } from "@/atoms/chats/active-chat.atom";
import { activeChatAtom, activeChatIdAtom } from "@/atoms/chats/chat-queries.atom";
import { chatUIAtom } from "@/atoms/chats/chat-uis.atom";
import { ChatPanelView } from "./ChatPanelView";
export interface GeneratePodcastRequest {

View file

@ -5,8 +5,8 @@ import { AlertCircle, Play, RefreshCw, Sparkles } from "lucide-react";
import { motion } from "motion/react";
import { useCallback } from "react";
import { cn } from "@/lib/utils";
import { activeChatAtom } from "@/atoms/chats/queries/active-chat.query.atom";
import { chatUIAtom } from "@/atoms/chats/active-chat.atom";
import { activeChatAtom } from "@/atoms/chats/chat-queries.atom";
import { chatUIAtom } from "@/atoms/chats/chat-uis.atom";
import { getPodcastStalenessMessage, isPodcastStale } from "../PodcastUtils";
import type { GeneratePodcastRequest } from "./ChatPanelContainer";
import { ConfigModal } from "./ConfigModal";

View file

@ -4,7 +4,7 @@ import { useAtomValue } from "jotai";
import { Pencil } from "lucide-react";
import { useCallback, useContext, useState } from "react";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { activeChatAtom } from "@/atoms/chats/queries/active-chat.query.atom";
import { activeChatAtom } from "@/atoms/chats/chat-queries.atom";
import type { GeneratePodcastRequest } from "./ChatPanelContainer";
interface ConfigModalProps {

View file

@ -13,7 +13,7 @@ import {
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";
import { useSearchSpace } from "@/hooks/use-search-space";
import { fetchChatDetails } from "@/lib/apis/chat-apis";
import { fetchChatDetails } from "@/lib/apis/chats.api";
interface BreadcrumbItemInterface {
label: string;

View file

@ -0,0 +1,112 @@
export const fetchSearchSpaces = async () => {
try {
const response = await fetch(
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces`,
{
headers: {
Authorization: `Bearer ${localStorage.getItem(
"surfsense_bearer_token"
)}`,
},
method: "GET",
}
);
if (!response.ok) {
throw new Error("Not authenticated");
}
const data = await response.json();
return data;
} catch (err: any) {
console.error("Error fetching search spaces:", err);
return null;
}
};
export const handleDeleteSearchSpace = async (id: number) => {
try {
const response = await fetch(
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces/${id}`,
{
method: "DELETE",
headers: {
Authorization: `Bearer ${localStorage.getItem(
"surfsense_bearer_token"
)}`,
},
}
);
if (!response.ok) {
throw new Error("Failed to delete search space");
}
} catch (error) {
console.error("Error deleting search space:", error);
return;
}
};
export const handleCreateSearchSpace = async (data: {
name: string;
description: string;
}) => {
try {
const response = await fetch(
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${localStorage.getItem(
"surfsense_bearer_token"
)}`,
},
body: JSON.stringify(data),
}
);
if (!response.ok) {
throw new Error("Failed to create search space");
}
const result = await response.json();
return result;
} catch (error) {
console.error("Error creating search space:", error);
throw error;
}
};
export const fetchSearchSpace = async (searchSpaceId: string) => {
try {
const response = await fetch(
`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces/${searchSpaceId}`,
{
headers: {
Authorization: `Bearer ${localStorage.getItem(
"surfsense_bearer_token"
)}`,
},
method: "GET",
}
);
if (response.status === 401) {
// Clear token and redirect to home
localStorage.removeItem("surfsense_bearer_token");
window.location.href = "/";
throw new Error("Unauthorized: Redirecting to login page");
}
if (!response.ok) {
throw new Error(`Failed to fetch search space: ${response.status}`);
}
const data = await response.json();
return data;
} catch (err: any) {
console.error("Error fetching search space:", err);
}
};