125 lines
3.9 KiB
JavaScript
125 lines
3.9 KiB
JavaScript
import { Link, Navigate, Outlet, useLocation } from "react-router-dom";
|
|
import { useAuthState } from "react-firebase-hooks/auth";
|
|
import { auth } from "../firebase";
|
|
import { krowSDK } from "@/api/krowSDK";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Collapsible,
|
|
CollapsibleContent,
|
|
CollapsibleTrigger,
|
|
} from "@/components/ui/collapsible";
|
|
import { ChevronDown } from "lucide-react";
|
|
import { useState } from "react";
|
|
|
|
const navLinks = {
|
|
auth: [
|
|
{ path: "/auth/me", title: "Get Me" },
|
|
{ path: "/auth/update-me", title: "Update Me" },
|
|
],
|
|
core: [
|
|
{ path: "/core/send-email", title: "Send Email" },
|
|
{ path: "/core/invoke-llm", title: "Invoke LLM" },
|
|
{ path: "/core/upload-file", title: "Upload File" },
|
|
{ path: "/core/upload-private-file", title: "Upload Private File" },
|
|
{ path: "/core/create-signed-url", title: "Create Signed URL" },
|
|
{ path: "/core/extract-data", title: "Extract Data from File" },
|
|
{ path: "/core/generate-image", title: "Generate Image" },
|
|
],
|
|
entities: [
|
|
{ path: "/entities", title: "Entity API Tester" }
|
|
]
|
|
};
|
|
|
|
|
|
const NavSection = ({ title, links }) => {
|
|
const location = useLocation();
|
|
const [isOpen, setIsOpen] = useState(true);
|
|
|
|
const navLinkClasses = (path) =>
|
|
`flex items-center px-3 py-2 text-sm font-medium rounded-md transition-colors ${
|
|
location.pathname === path
|
|
? "bg-slate-200 text-slate-900"
|
|
: "text-slate-600 hover:bg-slate-100"
|
|
}`;
|
|
|
|
return (
|
|
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
|
|
<CollapsibleTrigger className="w-full">
|
|
<div className="flex items-center justify-between px-3 py-2">
|
|
<h2 className="text-xs font-semibold text-slate-400 uppercase tracking-wider">
|
|
{title}
|
|
</h2>
|
|
<ChevronDown className={`w-4 h-4 text-slate-400 transition-transform ${isOpen ? 'rotate-180' : ''}`} />
|
|
</div>
|
|
</CollapsibleTrigger>
|
|
<CollapsibleContent className="space-y-1">
|
|
{links.map((api) => (
|
|
<Link key={api.path} to={api.path} className={navLinkClasses(api.path)}>
|
|
{api.title}
|
|
</Link>
|
|
))}
|
|
</CollapsibleContent>
|
|
</Collapsible>
|
|
);
|
|
}
|
|
|
|
|
|
const Layout = () => {
|
|
const [user, loading] = useAuthState(auth);
|
|
const location = useLocation();
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="flex items-center justify-center h-screen">
|
|
<div>Loading...</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!user) {
|
|
return <Navigate to="/login" />;
|
|
}
|
|
|
|
const handleLogout = () => {
|
|
krowSDK.auth.logout();
|
|
};
|
|
|
|
return (
|
|
<div className="flex h-screen bg-slate-50">
|
|
{/* Sidebar */}
|
|
<aside className="w-72 bg-white border-r border-slate-200 flex flex-col">
|
|
<div className="p-6 border-b border-slate-200">
|
|
<div className="flex items-center space-x-3">
|
|
<img src="/logo.svg" alt="KROW Logo" className="h-12 w-12" />
|
|
<div>
|
|
<h1 className="text-xl font-bold text-slate-900">KROW</h1>
|
|
<p className="text-xs text-slate-500">API Test Harness ({import.meta.env.VITE_HARNESS_ENVIRONMENT})</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<nav className="flex-1 overflow-y-auto p-4 space-y-2">
|
|
<NavSection title="Auth" links={navLinks.auth} />
|
|
<NavSection title="Core Integrations" links={navLinks.core} />
|
|
<NavSection title="Entities" links={navLinks.entities} />
|
|
</nav>
|
|
|
|
<div className="p-4 border-t border-slate-200">
|
|
<div className="text-sm text-slate-600 truncate mb-2" title={user.email}>{user.email}</div>
|
|
<Button onClick={handleLogout} className="w-full">
|
|
Logout
|
|
</Button>
|
|
</div>
|
|
</aside>
|
|
|
|
{/* Main Content */}
|
|
<main className="flex-1 overflow-y-auto">
|
|
<div className="p-8">
|
|
<Outlet />
|
|
</div>
|
|
</main>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Layout; |