feat: mobile tool workshop with list-detail pattern

- Add mobile viewport detection to ToolWorkshopPage
- Implement mobile list view with MobileListView component
- Implement mobile detail view with MobileDetailView component
- Implement mobile edit view with MobileEditView component
- Add MobileFAB for creating new tool types
- Fix IconName type issues in mobile components
- TypeScript check passes, build succeeds
This commit is contained in:
Alex Blank
2026-05-25 12:18:24 +02:00
parent 437ad840ef
commit e8d5b16acc
16 changed files with 1356 additions and 3853 deletions
+69 -22
View File
@@ -1,40 +1,87 @@
import { NavLink } from "react-router-dom";
import { useState } from "react";
import { NavLink, useLocation } from "react-router-dom";
import { Icon } from "./icon";
import { ToolsBottomSheet } from "./tools-bottom-sheet";
import type { IconName } from "../utils/icons";
interface MobileNavProps {
sessionCount?: number;
}
const MOBILE_NAV_ITEMS: { to: string; label: string; icon: IconName }[] = [
interface NavItem {
to: string;
label: string;
icon: IconName;
isGroup?: boolean;
}
const MOBILE_NAV_ITEMS: NavItem[] = [
{ to: "/", label: "Home", icon: "dashboard" },
{ to: "/projects", label: "Projects", icon: "projects" },
{ to: "/sessions", label: "Sessions", icon: "terminal" },
{ to: "/tool-workshop", label: "Tools", icon: "settings" },
{ to: "/tools", label: "Tools", icon: "settings", isGroup: true },
{ to: "/settings", label: "Settings", icon: "settings" },
];
export const MobileNav: React.FC<MobileNavProps> = ({ sessionCount }) => {
const location = useLocation();
const [toolsSheetOpen, setToolsSheetOpen] = useState(false);
const isToolsActive =
location.pathname === "/tool-workshop" ||
location.pathname === "/config-profiles";
const handleNavClick = (item: NavItem) => {
if (item.isGroup) {
setToolsSheetOpen(true);
}
};
return (
<nav className="mobile-nav" role="navigation" aria-label="Mobile navigation">
{MOBILE_NAV_ITEMS.map((item) => (
<NavLink
key={item.to}
to={item.to}
className={({ isActive }) =>
`mobile-nav-item ${isActive ? "active" : ""}`
<>
<nav className="mobile-nav" role="navigation" aria-label="Mobile navigation">
{MOBILE_NAV_ITEMS.map((item) => {
if (item.isGroup) {
return (
<button
key={item.to}
className={`mobile-nav-item ${isToolsActive ? "active" : ""}`}
onClick={() => handleNavClick(item)}
type="button"
>
<div className="mobile-nav-icon-wrapper">
<Icon name={item.icon} size="md" />
</div>
<span className="mobile-nav-label">{item.label}</span>
</button>
);
}
end={item.to === "/"}
>
<div className="mobile-nav-icon-wrapper">
<Icon name={item.icon} size="md" />
{item.to === "/sessions" && sessionCount ? (
<span className="mobile-nav-badge">{sessionCount}</span>
) : null}
</div>
<span className="mobile-nav-label">{item.label}</span>
</NavLink>
))}
</nav>
return (
<NavLink
key={item.to}
to={item.to}
className={({ isActive }) =>
`mobile-nav-item ${isActive ? "active" : ""}`
}
end={item.to === "/"}
>
<div className="mobile-nav-icon-wrapper">
<Icon name={item.icon} size="md" />
{item.to === "/sessions" && sessionCount ? (
<span className="mobile-nav-badge">{sessionCount}</span>
) : null}
</div>
<span className="mobile-nav-label">{item.label}</span>
</NavLink>
);
})}
</nav>
<ToolsBottomSheet
isOpen={toolsSheetOpen}
onClose={() => setToolsSheetOpen(false)}
/>
</>
);
};