chore: Updated UserRead schema to include pages_limit and pages_used fields

- Expanded installation options in README to include SurfSense Cloud as a new method.
- Updated UserRead schema to include pages_limit and pages_used fields.
- Added AnnouncementBanner component to the dashboard layout for improved user notifications.
- Refactored DashboardPage to utilize useUser hook for user state management.
- Integrated page usage display in AppSidebar to show user-specific page limits and usage.
- Removed deprecated apiClient code and replaced it with hooks for better API interaction.
This commit is contained in:
DESKTOP-RTLN3BA\$punk 2025-11-03 22:34:37 -08:00
parent a3a5b13f48
commit 4dd7e8fc1f
13 changed files with 456 additions and 339 deletions

View file

@ -13,24 +13,7 @@ import {
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { apiClient } from "@/lib/api";
interface Chat {
created_at: string;
id: number;
type: string;
title: string;
messages: string[];
search_space_id: number;
}
interface SearchSpace {
created_at: string;
id: number;
name: string;
description: string;
user_id: string;
}
import { useChats, useSearchSpace, useUser } from "@/hooks";
interface AppSidebarProviderProps {
searchSpaceId: string;
@ -58,21 +41,25 @@ export function AppSidebarProvider({
}: AppSidebarProviderProps) {
const t = useTranslations("dashboard");
const tCommon = useTranslations("common");
const [recentChats, setRecentChats] = useState<
{
name: string;
url: string;
icon: string;
id: number;
search_space_id: number;
actions: { name: string; icon: string; onClick: () => void }[];
}[]
>([]);
const [searchSpace, setSearchSpace] = useState<SearchSpace | null>(null);
const [isLoadingChats, setIsLoadingChats] = useState(true);
const [isLoadingSearchSpace, setIsLoadingSearchSpace] = useState(true);
const [chatError, setChatError] = useState<string | null>(null);
const [searchSpaceError, setSearchSpaceError] = useState<string | null>(null);
// Use the new hooks
const {
chats,
loading: isLoadingChats,
error: chatError,
fetchChats: fetchRecentChats,
deleteChat,
} = useChats({ searchSpaceId, limit: 5, skip: 0 });
const {
searchSpace,
loading: isLoadingSearchSpace,
error: searchSpaceError,
fetchSearchSpace,
} = useSearchSpace({ searchSpaceId });
const { user } = useUser();
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
const [chatToDelete, setChatToDelete] = useState<{ id: number; name: string } | null>(null);
const [isDeleting, setIsDeleting] = useState(false);
@ -83,95 +70,32 @@ export function AppSidebarProvider({
setIsClient(true);
}, []);
// Memoized fetch function for chats
const fetchRecentChats = useCallback(async () => {
try {
// Only run on client-side
if (typeof window === "undefined") return;
const chats: Chat[] = await apiClient.get<Chat[]>(
`api/v1/chats?limit=5&skip=0&search_space_id=${searchSpaceId}`
);
// Sort chats by created_at in descending order (newest first)
const sortedChats = chats.sort(
(a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()
);
// Transform API response to the format expected by AppSidebar
const formattedChats = sortedChats.map((chat) => ({
name: chat.title || `Chat ${chat.id}`,
url: `/dashboard/${chat.search_space_id}/researcher/${chat.id}`,
icon: "MessageCircleMore",
id: chat.id,
search_space_id: chat.search_space_id,
actions: [
{
name: "Delete",
icon: "Trash2",
onClick: () => {
setChatToDelete({ id: chat.id, name: chat.title || `Chat ${chat.id}` });
setShowDeleteDialog(true);
},
},
],
}));
setRecentChats(formattedChats);
setChatError(null);
} catch (error) {
console.error("Error fetching chats:", error);
setChatError(error instanceof Error ? error.message : "Unknown error occurred");
setRecentChats([]);
} finally {
setIsLoadingChats(false);
}
}, [searchSpaceId]);
// Memoized fetch function for search space
const fetchSearchSpace = useCallback(async () => {
try {
// Only run on client-side
if (typeof window === "undefined") return;
const data: SearchSpace = await apiClient.get<SearchSpace>(
`api/v1/searchspaces/${searchSpaceId}`
);
setSearchSpace(data);
setSearchSpaceError(null);
} catch (error) {
console.error("Error fetching search space:", error);
setSearchSpaceError(error instanceof Error ? error.message : "Unknown error occurred");
} finally {
setIsLoadingSearchSpace(false);
}
}, [searchSpaceId]);
// Retry function
const retryFetch = useCallback(() => {
setChatError(null);
setSearchSpaceError(null);
setIsLoadingChats(true);
setIsLoadingSearchSpace(true);
fetchRecentChats();
fetchSearchSpace();
}, [fetchRecentChats, fetchSearchSpace]);
// Fetch recent chats
useEffect(() => {
fetchRecentChats();
// Set up a refresh interval (every 5 minutes)
const intervalId = setInterval(fetchRecentChats, 5 * 60 * 1000);
// Clean up interval on component unmount
return () => clearInterval(intervalId);
}, [fetchRecentChats]);
// Fetch search space details
useEffect(() => {
fetchSearchSpace();
}, [fetchSearchSpace]);
// Transform API response to the format expected by AppSidebar
const recentChats = useMemo(() => {
return chats.map((chat) => ({
name: chat.title || `Chat ${chat.id}`,
url: `/dashboard/${chat.search_space_id}/researcher/${chat.id}`,
icon: "MessageCircleMore",
id: chat.id,
search_space_id: chat.search_space_id,
actions: [
{
name: "Delete",
icon: "Trash2",
onClick: () => {
setChatToDelete({ id: chat.id, name: chat.title || `Chat ${chat.id}` });
setShowDeleteDialog(true);
},
},
],
}));
}, [chats]);
// Handle delete chat with better error handling
const handleDeleteChat = useCallback(async () => {
@ -179,11 +103,7 @@ export function AppSidebarProvider({
try {
setIsDeleting(true);
await apiClient.delete(`api/v1/chats/${chatToDelete.id}`);
// Update local state
setRecentChats((prev) => prev.filter((chat) => chat.id !== chatToDelete.id));
await deleteChat(chatToDelete.id);
} catch (error) {
console.error("Error deleting chat:", error);
// You could show a toast notification here
@ -192,7 +112,7 @@ export function AppSidebarProvider({
setShowDeleteDialog(false);
setChatToDelete(null);
}
}, [chatToDelete]);
}, [chatToDelete, deleteChat]);
// Memoized fallback chats
const fallbackChats = useMemo(() => {
@ -260,14 +180,34 @@ export function AppSidebarProvider({
tCommon,
]);
// Prepare page usage data
const pageUsage = user
? {
pagesUsed: user.pages_used,
pagesLimit: user.pages_limit,
}
: undefined;
// Show loading state if not client-side
if (!isClient) {
return <AppSidebar navSecondary={navSecondary} navMain={navMain} RecentChats={[]} />;
return (
<AppSidebar
navSecondary={navSecondary}
navMain={navMain}
RecentChats={[]}
pageUsage={pageUsage}
/>
);
}
return (
<>
<AppSidebar navSecondary={updatedNavSecondary} navMain={navMain} RecentChats={displayChats} />
<AppSidebar
navSecondary={updatedNavSecondary}
navMain={navMain}
RecentChats={displayChats}
pageUsage={pageUsage}
/>
{/* Delete Confirmation Dialog */}
<Dialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>