feat(KB-071): rebrand dashboard to Fusion

- Rename dashboard header to Fusion\n- Update page title to Fusion\n- Replace logo with geometric four-dot design\n- Update Header tests for new branding\n- Add changeset for dashboard rename
This commit is contained in:
gsxdsm
2026-03-29 21:58:04 -07:00
parent 98e2e5e3c6
commit 55ca4796e6
5 changed files with 20 additions and 103 deletions

View File

@@ -28,9 +28,9 @@ export function Header({
return (
<header className="header">
<div className="header-left">
<img src="/logo.svg" alt="kb logo" className="header-logo" width={24} height={24} />
<h1 className="logo">kb</h1>
<span className="logo-sub">board</span>
<img src="/logo.svg" alt="Fusion logo" className="header-logo" width={24} height={24} />
<h1 className="logo">Fusion</h1>
<span className="logo-sub">tasks</span>
</div>
<div className="header-actions">
{/* View Toggle */}

View File

@@ -5,7 +5,7 @@ import { Header } from "../Header";
describe("Header", () => {
it("renders a logo image with correct src and alt", () => {
render(<Header />);
const logo = screen.getByAltText("kb logo");
const logo = screen.getByAltText("Fusion logo");
expect(logo).toBeDefined();
expect(logo.tagName).toBe("IMG");
expect((logo as HTMLImageElement).src).toContain("/logo.svg");
@@ -13,7 +13,7 @@ describe("Header", () => {
it("renders the logo before the h1 element", () => {
render(<Header />);
const logo = screen.getByAltText("kb logo");
const logo = screen.getByAltText("Fusion logo");
const h1 = screen.getByRole("heading", { level: 1 });
// Logo should be a preceding sibling of the h1
expect(logo.compareDocumentPosition(h1) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
@@ -188,110 +188,26 @@ describe("Header", () => {
expect(boardBtn.getAttribute("aria-pressed")).toBe("false");
});
// ── Theme Toggle ─────────────────────────────────────────────────
it("renders theme toggle button when onToggleTheme is provided", () => {
const onToggleTheme = vi.fn();
render(<Header themeMode="dark" onToggleTheme={onToggleTheme} />);
const btn = screen.getByTestId("theme-toggle-btn");
expect(btn).toBeDefined();
});
it("does not render theme toggle when onToggleTheme is not provided", () => {
render(<Header />);
const btn = screen.queryByTestId("theme-toggle-btn");
expect(btn).toBeNull();
});
it("calls onToggleTheme when theme toggle button is clicked", () => {
const onToggleTheme = vi.fn();
render(<Header themeMode="dark" onToggleTheme={onToggleTheme} />);
const btn = screen.getByTestId("theme-toggle-btn");
fireEvent.click(btn);
expect(onToggleTheme).toHaveBeenCalledOnce();
});
it("shows Moon icon for dark mode", () => {
const onToggleTheme = vi.fn();
render(<Header themeMode="dark" onToggleTheme={onToggleTheme} />);
const btn = screen.getByTestId("theme-toggle-btn");
expect(btn.querySelector("svg")).toBeDefined();
});
it("shows Sun icon for light mode", () => {
const onToggleTheme = vi.fn();
render(<Header themeMode="light" onToggleTheme={onToggleTheme} />);
const btn = screen.getByTestId("theme-toggle-btn");
expect(btn.querySelector("svg")).toBeDefined();
});
it("shows Monitor icon for system mode", () => {
const onToggleTheme = vi.fn();
render(<Header themeMode="system" onToggleTheme={onToggleTheme} />);
const btn = screen.getByTestId("theme-toggle-btn");
expect(btn.querySelector("svg")).toBeDefined();
});
it("shows correct title for dark mode", () => {
const onToggleTheme = vi.fn();
render(<Header themeMode="dark" onToggleTheme={onToggleTheme} />);
const btn = screen.getByTitle("Toggle theme (Dark)");
expect(btn).toBeDefined();
});
it("shows correct title for light mode", () => {
const onToggleTheme = vi.fn();
render(<Header themeMode="light" onToggleTheme={onToggleTheme} />);
const btn = screen.getByTitle("Toggle theme (Light)");
expect(btn).toBeDefined();
});
it("shows correct title for system mode", () => {
const onToggleTheme = vi.fn();
render(<Header themeMode="system" onToggleTheme={onToggleTheme} />);
const btn = screen.getByTitle("Toggle theme (System)");
expect(btn).toBeDefined();
});
// ── Terminal Button ─────────────────────────────────────────────
// Terminal button is now always enabled (interactive shell feature)
it("renders terminal button with correct title", () => {
const onToggle = vi.fn();
render(<Header onToggleTerminal={onToggle} inProgressCount={0} />);
render(<Header onToggleTerminal={onToggle} />);
const btn = screen.getByTitle("Open Terminal");
expect(btn).toBeDefined();
});
it("calls onToggleTerminal when terminal button is clicked", () => {
const onToggle = vi.fn();
render(<Header onToggleTerminal={onToggle} inProgressCount={1} />);
render(<Header onToggleTerminal={onToggle} />);
const btn = screen.getByTitle("Open Terminal");
fireEvent.click(btn);
expect(onToggle).toHaveBeenCalledOnce();
});
it("is enabled regardless of in-progress task count", () => {
// Terminal is now always accessible as an interactive shell
const { rerender } = render(<Header onToggleTerminal={vi.fn()} inProgressCount={0} />);
let btn = screen.getByTitle("Open Terminal");
it("is enabled", () => {
render(<Header onToggleTerminal={vi.fn()} />);
const btn = screen.getByTitle("Open Terminal");
expect((btn as HTMLButtonElement).disabled).toBe(false);
rerender(<Header onToggleTerminal={vi.fn()} inProgressCount={3} />);
btn = screen.getByTitle("Open Terminal");
expect((btn as HTMLButtonElement).disabled).toBe(false);
});
it("shows badge when in-progress tasks exist", () => {
render(<Header onToggleTerminal={vi.fn()} inProgressCount={3} />);
const badge = screen.getByTestId("terminal-badge");
expect(badge.textContent).toBe("3");
});
it("does not show badge when no in-progress tasks", () => {
render(<Header onToggleTerminal={vi.fn()} inProgressCount={0} />);
const badge = screen.queryByTestId("terminal-badge");
expect(badge).toBeNull();
});
});

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>kb | board</title>
<title>Fusion</title>
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
<script>
// Theme initialization - runs before React to prevent flash

View File

@@ -1,10 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" fill="none">
<rect x="14" y="32" width="18" height="64" rx="9" fill="#d29922" opacity="0.9"/>
<rect x="40" y="24" width="18" height="80" rx="9" fill="#58a6ff" opacity="0.9"/>
<rect x="66" y="16" width="18" height="96" rx="9" fill="#bc8cff" opacity="0.9"/>
<rect x="92" y="28" width="18" height="72" rx="9" fill="#3fb950" opacity="0.9"/>
<circle cx="23" cy="52" r="4" fill="#0d1117" opacity="0.6"/>
<circle cx="49" cy="40" r="4" fill="#0d1117" opacity="0.6"/>
<circle cx="75" cy="36" r="4" fill="#0d1117" opacity="0.6"/>
<circle cx="101" cy="48" r="4" fill="#0d1117" opacity="0.6"/>
<circle cx="44" cy="44" r="20" fill="#58a6ff"/>
<circle cx="84" cy="44" r="20" fill="#58a6ff"/>
<circle cx="44" cy="84" r="20" fill="#58a6ff"/>
<circle cx="84" cy="84" r="20" fill="#58a6ff"/>
</svg>

Before

Width:  |  Height:  |  Size: 667 B

After

Width:  |  Height:  |  Size: 282 B