Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
"streamdown": "^2.0.1",
"supermemory": "^3.14.0",
"tailwind-merge": "^3.4.0",
"typewriter-effect": "^2.22.0",
"zod": "^4.3.4",
"zustand": "^5.0.9"
},
Expand Down
55 changes: 1 addition & 54 deletions src/app/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { DashboardLayout } from "@/components/layout/DashboardLayout";
import { ItemPanelContent } from "@/components/workspace-canvas/ItemPanelContent";
import { SidebarProvider, useSidebar } from "@/components/ui/sidebar";
import { MobileWarning } from "@/components/ui/MobileWarning";
import { AuthPageBackground } from "@/components/auth/AuthPageBackground";
import { AnonymousSessionHandler, SidebarCoordinator } from "@/components/layout/SessionHandler";
// import { OnboardingVideoDialog } from "@/components/onboarding/OnboardingVideoDialog";
// import { useOnboardingStatus } from "@/hooks/user/use-onboarding-status";
import { PdfEngineWrapper } from "@/components/pdf/PdfEngineWrapper";
Expand Down Expand Up @@ -429,59 +429,6 @@ export function DashboardPage() {
);
}

// Wrapper for sidebar provider
export function SidebarCoordinator({ children }: { children: React.ReactNode }) {
return (
<SidebarProvider defaultOpen={true}>
{children}
</SidebarProvider>
);
}

// Component to handle anonymous users - redirect to guest-setup if no session
export function AnonymousSessionHandler({ children }: { children: React.ReactNode }) {
const { data: session, isPending } = useSession();
const router = useRouter();

useEffect(() => {
// If no session and not loading, redirect to guest-setup
if (!isPending && !session) {
router.replace("/guest-setup");
return;
}
}, [session, isPending, router]);

// Show loading while checking session
if (isPending) {
return (
<main className="relative flex min-h-screen flex-col items-center justify-center p-4 md:p-6 overflow-hidden">
{/* Background with grid and cards - same as auth page */}
<AuthPageBackground />

{/* Content */}
<div className="relative z-10 text-center space-y-6 max-w-md">
<div className="flex justify-center">
<Loader2 className="w-10 h-10 animate-spin text-white" />
</div>
<h1 className="text-3xl md:text-4xl font-semibold tracking-tight text-white">
Setting up your workspace...
</h1>
<p className="text-sm text-white/70">
This will only take a moment
</p>
</div>
</main>
);
}

// Don't render dashboard if no session (will redirect)
if (!session) {
return null;
}

return <>{children}</>;
}

export function DashboardShell() {
return (
<>
Expand Down
44 changes: 42 additions & 2 deletions src/app/home/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,45 @@
"use client";

import { SEO } from "@/components/seo/SEO";
import { DashboardShell } from "../dashboard/page";
import { MobileWarning } from "@/components/ui/MobileWarning";
import { WorkspaceProvider, useWorkspaceContext } from "@/contexts/WorkspaceContext";
import { HomeLayout } from "@/components/layout/HomeLayout";
import { HomeContent } from "@/components/home/HomeContent";
import { AnonymousSessionHandler, SidebarCoordinator } from "@/components/layout/SessionHandler";
import { useUIStore } from "@/lib/stores/ui-store";

// Home page content component
function HomePageContent() {
const { switchWorkspace } = useWorkspaceContext();
const showCreateModal = useUIStore((state) => state.showCreateWorkspaceModal);
const setShowCreateModal = useUIStore((state) => state.setShowCreateWorkspaceModal);

return (
<HomeLayout
onWorkspaceSwitch={switchWorkspace}
showCreateModal={showCreateModal}
setShowCreateModal={setShowCreateModal}
>
<HomeContent />
</HomeLayout>
);
}

// Main shell component for home page
export function HomeShell() {
return (
<>
<MobileWarning />
<AnonymousSessionHandler>
<WorkspaceProvider>
<SidebarCoordinator>
<HomePageContent />
</SidebarCoordinator>
</WorkspaceProvider>
</AnonymousSessionHandler>
</>
);
}

export default function HomePage() {
return (
Expand All @@ -11,7 +51,7 @@ export default function HomePage() {
url="https://thinkex.app/home"
canonical="https://thinkex.app/home"
/>
<DashboardShell />
<HomeShell />
</>
);
}
180 changes: 180 additions & 0 deletions src/components/home/FeaturedWorkspaces.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
"use client";

import { ChevronLeft, ChevronRight } from "lucide-react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { useCallback, useEffect, useRef, useState } from "react";
import { toast } from "sonner";

// Featured workspace templates with current topics
const FEATURED_WORKSPACES = [
{
id: "featured-1",
name: "AI & Machine Learning",
description: "Explore GPT, LLMs, and AI trends",
color: "hsl(217, 91%, 60%)",
},
{
id: "featured-2",
name: "Climate Science",
description: "Study climate change and solutions",
color: "hsl(142, 76%, 36%)",
},
{
id: "featured-3",
name: "Web3 & Blockchain",
description: "Learn crypto, DeFi, and NFTs",
color: "hsl(280, 100%, 70%)",
},
{
id: "featured-4",
name: "Quantum Computing",
description: "Understand quantum mechanics",
color: "hsl(24, 95%, 53%)",
},
{
id: "featured-5",
name: "Space Exploration",
description: "Mars missions and astronomy",
color: "hsl(200, 100%, 50%)",
},
{
id: "featured-6",
name: "Neuroscience",
description: "Brain science and cognition",
color: "hsl(320, 70%, 60%)",
},
{
id: "featured-7",
name: "Sustainable Energy",
description: "Solar, wind, and green tech",
color: "hsl(120, 60%, 45%)",
},
{
id: "featured-8",
name: "Biotechnology",
description: "CRISPR, gene therapy, and more",
color: "hsl(15, 85%, 55%)",
},
];

export function FeaturedWorkspaces() {
const scrollRef = useRef<HTMLDivElement>(null);
const [canScrollLeft, setCanScrollLeft] = useState(false);
const [canScrollRight, setCanScrollRight] = useState(true);

const checkScrollability = () => {
if (!scrollRef.current) return;
const { scrollLeft, scrollWidth, clientWidth } = scrollRef.current;
setCanScrollLeft(scrollLeft > 0);
setCanScrollRight(scrollLeft < scrollWidth - clientWidth - 10);
};

useEffect(() => {
checkScrollability();
// Check on window resize
window.addEventListener("resize", checkScrollability);
return () => window.removeEventListener("resize", checkScrollability);
}, []);

const scroll = (direction: "left" | "right") => {
if (!scrollRef.current) return;
const scrollAmount = scrollRef.current.clientWidth * 0.8;
scrollRef.current.scrollBy({
left: direction === "left" ? -scrollAmount : scrollAmount,
behavior: "smooth",
});
};

const handleWorkspaceClick = useCallback((workspaceName: string) => {
toast.info("Coming soon", {
description: `Template workspace "${workspaceName}" will be available soon!`,
});
}, []);

return (
<div className="w-full">
<div className="flex items-center justify-between mb-4">
<h2 className="text-lg font-normal text-muted-foreground">Featured workspaces</h2>
<div className="flex gap-2">
<Button
variant="ghost"
size="icon"
className="h-8 w-8"
onClick={() => scroll("left")}
disabled={!canScrollLeft}
>
<ChevronLeft className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon"
className="h-8 w-8"
onClick={() => scroll("right")}
disabled={!canScrollRight}
>
<ChevronRight className="h-4 w-4" />
</Button>
</div>
</div>

<div
ref={scrollRef}
onScroll={checkScrollability}
className="flex gap-4 overflow-x-auto scrollbar-thin scrollbar-thumb-gray-600 scrollbar-track-transparent pb-2"
style={{
scrollbarWidth: "thin",
overscrollBehaviorX: "contain",
overscrollBehaviorY: "auto"
}}
>
{FEATURED_WORKSPACES.map((workspace) => (
<div
key={workspace.id}
role="button"
tabIndex={0}
onClick={() => handleWorkspaceClick(workspace.name)}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
handleWorkspaceClick(workspace.name);
}
}}
className={cn(
"flex-shrink-0 w-64 px-6 py-8 rounded-md border border-sidebar-border/50 backdrop-blur-sm",
"hover:border-primary/40 hover:shadow-lg hover:shadow-primary/5",
"transition-all duration-300 cursor-pointer",
"flex flex-col items-start justify-between",
"aspect-[3.5/1]"
)}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
style={{
backgroundColor: `${workspace.color}08`,
}}
>
<div className="flex flex-col gap-4 w-full">
<div
className="flex-shrink-0 w-12 h-12 rounded-md flex items-center justify-center"
style={{
backgroundColor: `${workspace.color}20`,
}}
>
<div
className="w-6 h-6 rounded"
style={{
backgroundColor: workspace.color,
}}
/>
</div>
<div className="min-w-0 w-full">
<h3 className="font-medium text-lg text-foreground truncate w-full mb-1">
{workspace.name}
</h3>
<p className="text-sm text-muted-foreground">{workspace.description}</p>
</div>
</div>
</div>
))}
</div>
</div>
);
}
33 changes: 33 additions & 0 deletions src/components/home/HomeContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"use client";

import { HomePromptInput } from "./HomePromptInput";
import { FeaturedWorkspaces } from "./FeaturedWorkspaces";
import { WorkspaceGrid } from "./WorkspaceGrid";

export function HomeContent() {
return (
<div className="flex flex-col h-full w-full overflow-hidden">
{/* Main content area */}
<div className="flex-1 flex flex-col items-center justify-start px-6 pt-[10vh] pb-8 overflow-y-auto">
{/* Hero Section */}
<div className="text-center mb-12 w-full">
<h1 className="text-3xl md:text-4xl font-medium text-foreground mb-6">
What's on your mind?
</h1>
<div className="flex justify-center">
<HomePromptInput />
</div>
</div>

{/* Content Sections */}
<div className="w-full max-w-6xl space-y-12">
{/* Featured Workspaces */}
<FeaturedWorkspaces />

{/* Your Workspaces */}
<WorkspaceGrid />
</div>
</div>
</div>
);
}
Loading