feat: new home web
This commit is contained in:
@@ -5,6 +5,7 @@ import { useAuth } from '../lib/auth';
|
||||
import { apiFetch } from '../lib/api';
|
||||
import ThemeToggle from '../components/ThemeToggle';
|
||||
import SettingsDialog from '../components/SettingsDialog';
|
||||
import ConfirmDialog from '../components/ConfirmDialog';
|
||||
|
||||
type LayoutContext = { onOpenSettings: () => void };
|
||||
export function useLayoutContext() { return useOutletContext<LayoutContext>(); }
|
||||
@@ -16,6 +17,7 @@ type ProjectSummary = {
|
||||
|
||||
function UserDropdown({ user, logout, onOpenSettings }: { user: { name: string; email: string }; logout: () => void; onOpenSettings: () => void }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [confirmLogout, setConfirmLogout] = useState(false);
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const initials = user.name.split(' ').map(w => w[0]).join('').toUpperCase().slice(0, 2);
|
||||
|
||||
@@ -74,7 +76,7 @@ function UserDropdown({ user, logout, onOpenSettings }: { user: { name: string;
|
||||
Settings
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { setOpen(false); logout(); }}
|
||||
onClick={() => { setOpen(false); setConfirmLogout(true); }}
|
||||
className="flex items-center gap-2.5 w-full px-3 py-2 rounded-lg text-[13px] text-text-muted hover:text-danger hover:bg-danger-muted transition-colors mx-1"
|
||||
style={{ width: 'calc(100% - 8px)' }}
|
||||
>
|
||||
@@ -86,6 +88,15 @@ function UserDropdown({ user, logout, onOpenSettings }: { user: { name: string;
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<ConfirmDialog
|
||||
open={confirmLogout}
|
||||
onConfirm={() => { setConfirmLogout(false); logout(); }}
|
||||
onCancel={() => setConfirmLogout(false)}
|
||||
title="Sign Out"
|
||||
description="Are you sure you want to sign out?"
|
||||
confirmText="Sign Out"
|
||||
variant="warning"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -100,7 +111,7 @@ function ProjectSidebar() {
|
||||
queryFn: () => apiFetch<ProjectSummary[]>('/projects'),
|
||||
});
|
||||
|
||||
const isProjectsRoot = location.pathname === '/';
|
||||
const isProjectsRoot = location.pathname === '/dashboard';
|
||||
|
||||
return (
|
||||
<aside className="hidden lg:flex w-[240px] shrink-0 flex-col border-r border-border-default bg-bg-sidebar">
|
||||
@@ -112,7 +123,7 @@ function ProjectSidebar() {
|
||||
{/* Project list */}
|
||||
<nav className="flex-1 overflow-y-auto px-2 py-2 space-y-0.5">
|
||||
<NavLink
|
||||
to="/"
|
||||
to="/dashboard"
|
||||
end
|
||||
className={`flex items-center gap-2 px-2.5 py-[7px] rounded-lg text-[13px] font-medium transition-all duration-150 ${
|
||||
isProjectsRoot
|
||||
@@ -141,7 +152,7 @@ function ProjectSidebar() {
|
||||
{projects?.map((p) => (
|
||||
<NavLink
|
||||
key={p.id}
|
||||
to={`/projects/${p.id}`}
|
||||
to={`/dashboard/projects/${p.id}`}
|
||||
className={`flex items-center gap-2 px-2.5 py-[7px] rounded-lg text-[13px] transition-all duration-150 group ${
|
||||
activeProjectId === p.id
|
||||
? 'bg-accent-muted text-accent font-medium'
|
||||
@@ -222,12 +233,13 @@ export default function Layout() {
|
||||
</svg>
|
||||
</button>
|
||||
<Link to="/" className="flex items-center gap-2.5">
|
||||
<div className="w-7 h-7 rounded-lg bg-accent flex items-center justify-center shadow-sm">
|
||||
<svg className="w-[14px] h-[14px] text-white" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
|
||||
<div className="w-8 h-8 rounded-lg flex items-center justify-center shadow-sm"
|
||||
style={{ background: 'linear-gradient(135deg, var(--fox-amber), var(--fox-orange))' }}>
|
||||
<svg className="w-4 h-4 text-white" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" />
|
||||
</svg>
|
||||
</div>
|
||||
<span className="font-semibold text-[15px] text-text-primary tracking-[-0.01em]">Agent Fox</span>
|
||||
<span className="font-bold text-lg text-text-primary tracking-tight" style={{ fontFamily: 'var(--font-heading)' }}>AgentFox</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -248,17 +260,18 @@ export default function Layout() {
|
||||
|
||||
{/* Mobile sidebar */}
|
||||
<aside className={`fixed inset-y-0 left-0 z-50 w-[260px] bg-bg-sidebar border-r border-border-default flex flex-col transition-transform duration-200 lg:hidden ${mobileMenuOpen ? 'translate-x-0' : '-translate-x-full'}`}>
|
||||
<div className="px-4 h-14 flex items-center gap-2.5 border-b border-border-default">
|
||||
<div className="w-7 h-7 rounded-lg bg-accent flex items-center justify-center shadow-sm">
|
||||
<svg className="w-[14px] h-[14px] text-white" viewBox="0 0 20 20" fill="currentColor">
|
||||
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
|
||||
<Link to="/" className="px-4 h-14 flex items-center gap-2.5 border-b border-border-default">
|
||||
<div className="w-8 h-8 rounded-lg flex items-center justify-center shadow-sm"
|
||||
style={{ background: 'linear-gradient(135deg, var(--fox-amber), var(--fox-orange))' }}>
|
||||
<svg className="w-4 h-4 text-white" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" />
|
||||
</svg>
|
||||
</div>
|
||||
<span className="font-semibold text-[15px] text-text-primary tracking-[-0.01em]">Agent Fox</span>
|
||||
</div>
|
||||
<span className="font-bold text-lg text-text-primary tracking-tight" style={{ fontFamily: 'var(--font-heading)' }}>AgentFox</span>
|
||||
</Link>
|
||||
<nav className="flex-1 overflow-y-auto px-2.5 py-3 space-y-0.5">
|
||||
<NavLink
|
||||
to="/"
|
||||
to="/dashboard"
|
||||
end
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
className={({ isActive }) =>
|
||||
|
||||
Reference in New Issue
Block a user