feat(20-02): wire FabMenu and CatalogSearchOverlay into root layout

- Replace old single-action FAB with FabMenu component
- Add CatalogSearchOverlay to root layout
- FAB now visible on all authenticated non-public routes
- Detect setups page for conditional New Setup menu item
- Remove unused openAddPanel reference
This commit is contained in:
2026-04-06 08:04:10 +02:00
parent 720460852c
commit e13f9584fa

View File

@@ -10,8 +10,10 @@ import {
import { useState } from "react"; import { useState } from "react";
import "../app.css"; import "../app.css";
import { CandidateForm } from "../components/CandidateForm"; import { CandidateForm } from "../components/CandidateForm";
import { CatalogSearchOverlay } from "../components/CatalogSearchOverlay";
import { ConfirmDialog } from "../components/ConfirmDialog"; import { ConfirmDialog } from "../components/ConfirmDialog";
import { ExternalLinkDialog } from "../components/ExternalLinkDialog"; import { ExternalLinkDialog } from "../components/ExternalLinkDialog";
import { FabMenu } from "../components/FabMenu";
import { ItemForm } from "../components/ItemForm"; import { ItemForm } from "../components/ItemForm";
import { OnboardingWizard } from "../components/OnboardingWizard"; import { OnboardingWizard } from "../components/OnboardingWizard";
import { SlideOutPanel } from "../components/SlideOutPanel"; import { SlideOutPanel } from "../components/SlideOutPanel";
@@ -80,7 +82,6 @@ function RootLayout() {
// Item panel state // Item panel state
const panelMode = useUIStore((s) => s.panelMode); const panelMode = useUIStore((s) => s.panelMode);
const editingItemId = useUIStore((s) => s.editingItemId); const editingItemId = useUIStore((s) => s.editingItemId);
const openAddPanel = useUIStore((s) => s.openAddPanel);
const closePanel = useUIStore((s) => s.closePanel); const closePanel = useUIStore((s) => s.closePanel);
// Candidate panel state // Candidate panel state
@@ -144,16 +145,6 @@ function RootLayout() {
? { linkTo: "/" } ? { linkTo: "/" }
: { linkTo: "/" }; : { linkTo: "/" };
// FAB visibility: only show on /collection route when gear tab is active
const collectionSearch = matchRoute({ to: "/collection" }) as
| { tab?: string }
| false;
const showFab =
isCollection &&
(!collectionSearch ||
!(collectionSearch as Record<string, string>).tab ||
(collectionSearch as Record<string, string>).tab === "gear");
// Show loading while checking auth // Show loading while checking auth
if (authLoading) { if (authLoading) {
return ( return (
@@ -168,6 +159,10 @@ function RootLayout() {
const isPublicRoute = const isPublicRoute =
location.pathname.startsWith("/users/") || location.pathname === "/login"; location.pathname.startsWith("/users/") || location.pathname === "/login";
// FAB visibility: show on all authenticated, non-public routes
const isSetupsPage = !!matchRoute({ to: "/setups", fuzzy: true });
const showFab = isAuthenticated && !isPublicRoute;
if (!isAuthenticated && !isPublicRoute) { if (!isAuthenticated && !isPublicRoute) {
window.location.href = "/login"; window.location.href = "/login";
return ( return (
@@ -253,29 +248,11 @@ function RootLayout() {
/> />
)} )}
{/* Floating Add Button - only on collection gear tab */} {/* Floating Action Button */}
{showFab && isAuthenticated && ( {showFab && <FabMenu isSetupsPage={isSetupsPage} />}
<button
type="button" {/* Catalog Search Overlay */}
onClick={openAddPanel} <CatalogSearchOverlay />
className="fixed bottom-6 right-6 z-20 w-14 h-14 bg-gray-700 hover:bg-gray-800 text-white rounded-full shadow-lg hover:shadow-xl transition-all flex items-center justify-center"
title="Add new item"
>
<svg
className="w-6 h-6"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 4v16m8-8H4"
/>
</svg>
</button>
)}
{/* Onboarding Wizard */} {/* Onboarding Wizard */}
{showWizard && ( {showWizard && (