"use client";
import { motion, type Variants } from "framer-motion";
import { AlertCircle, Loader2, Plus, Search, Trash2 } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import { toast } from "sonner";
import { Logo } from "@/components/Logo";
import { ThemeTogglerComponent } from "@/components/theme/theme-toggle";
import { UserDropdown } from "@/components/UserDropdown";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Spotlight } from "@/components/ui/spotlight";
import { Tilt } from "@/components/ui/tilt";
import { useSearchSpaces } from "@/hooks/use-search-spaces";
import { apiClient } from "@/lib/api";
interface User {
id: string;
email: string;
is_active: boolean;
is_superuser: boolean;
is_verified: boolean;
}
/**
* Formats a date string into a readable format
* @param dateString - The date string to format
* @returns Formatted date string (e.g., "Jan 1, 2023")
*/
const formatDate = (dateString: string): string => {
return new Date(dateString).toLocaleDateString("en-US", {
year: "numeric",
month: "short",
day: "numeric",
});
};
/**
* Loading screen component with animation
*/
const LoadingScreen = () => {
return (
Loading
Fetching your search spaces...
This may take a moment
);
};
/**
* Error screen component with animation
*/
const ErrorScreen = ({ message }: { message: string }) => {
const router = useRouter();
return (
Something went wrong
Error Details
{message}
router.refresh()}>
Try Again
router.push("/")}>Go Home
);
};
const DashboardPage = () => {
// Animation variants
const containerVariants: Variants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1,
},
},
};
const itemVariants: Variants = {
hidden: { y: 20, opacity: 0 },
visible: {
y: 0,
opacity: 1,
transition: {
type: "spring",
stiffness: 300,
damping: 24,
},
},
};
const { searchSpaces, loading, error, refreshSearchSpaces } = useSearchSpaces();
// User state management
const [user, setUser] = useState(null);
const [isLoadingUser, setIsLoadingUser] = useState(true);
const [userError, setUserError] = useState(null);
// Fetch user details
useEffect(() => {
const fetchUser = async () => {
try {
if (typeof window === "undefined") return;
try {
const userData = await apiClient.get("users/me");
setUser(userData);
setUserError(null);
} catch (error) {
console.error("Error fetching user:", error);
setUserError(error instanceof Error ? error.message : "Unknown error occurred");
} finally {
setIsLoadingUser(false);
}
} catch (error) {
console.error("Error in fetchUser:", error);
setIsLoadingUser(false);
}
};
fetchUser();
}, []);
// Create user object for UserDropdown
const customUser = {
name: user?.email ? user.email.split("@")[0] : "User",
email:
user?.email ||
(isLoadingUser ? "Loading..." : userError ? "Error loading user" : "Unknown User"),
avatar: "/icon-128.png", // Default avatar
};
if (loading) return ;
if (error) return ;
const handleDeleteSearchSpace = async (id: number) => {
// Send DELETE request to the API
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) {
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 (
SurfSense Dashboard
Welcome to your SurfSense dashboard.
Your Search Spaces
Create Search Space
{searchSpaces &&
searchSpaces.length > 0 &&
searchSpaces.map((space) => (
Delete Search Space
Are you sure you want to delete "{space.name}"? This
action cannot be undone. All documents, chats, and podcasts in
this search space will be permanently deleted.
Cancel
handleDeleteSearchSpace(space.id)}
className="bg-destructive hover:bg-destructive/90"
>
Delete
{space.name}
{space.description}
{/* {space.title} */}
Created {formatDate(space.created_at)}
))}
{searchSpaces.length === 0 && (
No search spaces found
Create your first search space to get started
Create Search Space
)}
{searchSpaces.length > 0 && (
)}
);
};
export default DashboardPage;