feat: new home web

This commit is contained in:
2026-04-03 00:02:09 +08:00
parent 35511eb877
commit 7e691a8100
27 changed files with 1691 additions and 54 deletions

View File

@@ -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 }) =>