web-client-vite/src/components/Sidebar.tsx
Markov 3a2a32a50e fix: restore all original components (KanbanBoard, ChatPanel, TaskModal, etc.)
Previous migration stubbed most components. Now properly ported from
Next.js with type-only imports and react-router-dom Link/navigation.
2026-02-24 09:33:04 +01:00

113 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState } from "react";
import { Link } from "react-router-dom";
import type { Project } from "@/lib/api";
import { logout } from "@/lib/auth-client";
interface Props {
projects: Project[];
activeSlug?: string;
}
export default function Sidebar({ projects, activeSlug }: Props) {
const [open, setOpen] = useState(false);
const nav = (
<>
<div className="p-4 border-b border-[var(--border)] flex items-center justify-between">
<Link to="/" className="text-lg font-bold tracking-tight">
Team Board
</Link>
{/* Close button on mobile */}
<button
className="md:hidden text-[var(--muted)] hover:text-[var(--fg)] text-xl"
onClick={() => setOpen(false)}
>
</button>
</div>
<nav className="flex-1 overflow-y-auto p-2">
<Link
to="/projects/new"
onClick={() => setOpen(false)}
className="flex items-center gap-2 px-3 py-2 mb-2 rounded text-sm text-[var(--accent)]
hover:bg-[var(--accent)]/10 transition-colors"
>
<span className="text-lg leading-none">+</span>
Новый проект
</Link>
<div className="text-xs uppercase text-[var(--muted)] px-2 py-1 mb-1">Проекты</div>
{projects.map((p) => (
<Link
key={p.id}
to={`/projects/${p.slug}`}
onClick={() => setOpen(false)}
className={`block px-3 py-2 rounded text-sm transition-colors ${
activeSlug === p.slug
? "bg-[var(--accent)]/10 text-[var(--accent)]"
: "hover:bg-white/5 text-[var(--fg)]"
}`}
>
{p.name}
</Link>
))}
</nav>
<div className="p-3 border-t border-[var(--border)] flex items-center justify-between">
<span className="text-xs text-[var(--muted)]">Team Board v0.2</span>
<div className="flex items-center gap-2">
<Link
to="/settings"
onClick={() => setOpen(false)}
className="text-[var(--muted)] hover:text-[var(--fg)] transition-colors cursor-pointer"
title="Настройки"
>
</Link>
<button
onClick={logout}
className="text-[var(--muted)] hover:text-[var(--fg)] transition-colors cursor-pointer"
title="Выйти"
>
🚪
</button>
</div>
</div>
</>
);
return (
<>
{/* Mobile hamburger */}
<button
className="md:hidden fixed top-3 left-3 z-40 p-2 bg-[var(--card)] border border-[var(--border)]
rounded-lg text-[var(--fg)]"
onClick={() => setOpen(true)}
>
<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
<path d="M2 4.5h16M2 10h16M2 15.5h16" stroke="currentColor" strokeWidth="1.5" fill="none" />
</svg>
</button>
{/* Mobile overlay */}
{open && (
<div className="md:hidden fixed inset-0 bg-black/60 z-40" onClick={() => setOpen(false)} />
)}
{/* Sidebar: always visible on desktop, slide-in on mobile */}
<aside
className={`
fixed md:static z-50 top-0 left-0 h-screen w-60 shrink-0
border-r border-[var(--border)] bg-[var(--card)] flex flex-col
transition-transform duration-200
${open ? "translate-x-0" : "-translate-x-full md:translate-x-0"}
`}
>
{nav}
</aside>
</>
);
}