Files
GearBox/src/client/components/BottomTabBar.tsx
Jean-Luc Makiola 8d7a668da4
All checks were successful
CI / ci (push) Successful in 1m23s
CI / e2e (push) Has been skipped
CI / deploy (push) Successful in 1m20s
fix: resolve lint errors from phase 32/33/34 execution
Auto-fixed formatting issues and removed unused imports introduced
by background execution agents across currency, i18n, and sharing code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 18:32:32 +02:00

108 lines
2.9 KiB
TypeScript

import { Link, useMatchRoute } from "@tanstack/react-router";
import { motion } from "framer-motion";
import { useTranslation } from "react-i18next";
import { useAuth } from "../hooks/useAuth";
import { LucideIcon } from "../lib/iconData";
import { useUIStore } from "../stores/uiStore";
interface TabItemProps {
icon: string;
label: string;
isActive: boolean;
}
function TabItemWrapper({ icon, label, isActive }: TabItemProps) {
const activeClass = "text-gray-900";
const inactiveClass = "text-gray-400";
const colorClass = isActive ? activeClass : inactiveClass;
return (
<span
className={`flex flex-col items-center gap-0.5 py-2 px-4 ${colorClass}`}
>
<LucideIcon name={icon} size={20} />
<span className="text-xs">{label}</span>
</span>
);
}
export function BottomTabBar() {
const { t } = useTranslation();
const { data: auth } = useAuth();
const isAuthenticated = !!auth?.user;
const openCatalogSearch = useUIStore((s) => s.openCatalogSearch);
const openAuthPrompt = useUIStore((s) => s.openAuthPrompt);
const matchRoute = useMatchRoute();
const isHome = !!matchRoute({ to: "/" });
const isCollection = !!matchRoute({ to: "/collection", fuzzy: true });
const isSetups = !!matchRoute({ to: "/setups", fuzzy: true });
return (
<motion.div
initial={{ y: 20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ duration: 0.2, ease: "easeOut" }}
className="fixed bottom-0 left-0 right-0 md:hidden z-20 bg-white border-t border-gray-100 pb-[env(safe-area-inset-bottom)]"
>
<div className="flex justify-around">
{/* Home tab — always a Link */}
<Link to="/">
<TabItemWrapper
icon="house"
label={t("nav.home")}
isActive={isHome}
/>
</Link>
{/* Collection tab — Link if authenticated, button if anonymous */}
{isAuthenticated ? (
<Link to="/collection">
<TabItemWrapper
icon="package"
label={t("nav.collection")}
isActive={isCollection}
/>
</Link>
) : (
<button type="button" onClick={openAuthPrompt}>
<TabItemWrapper
icon="package"
label={t("nav.collection")}
isActive={isCollection}
/>
</button>
)}
{/* Setups tab — Link if authenticated, button if anonymous */}
{isAuthenticated ? (
<Link to="/setups">
<TabItemWrapper
icon="layers"
label={t("nav.setups")}
isActive={isSetups}
/>
</Link>
) : (
<button type="button" onClick={openAuthPrompt}>
<TabItemWrapper
icon="layers"
label={t("nav.setups")}
isActive={isSetups}
/>
</button>
)}
{/* Search tab — always a button, opens CatalogSearchOverlay */}
<button type="button" onClick={() => openCatalogSearch("collection")}>
<TabItemWrapper
icon="search"
label={t("nav.search")}
isActive={false}
/>
</button>
</div>
</motion.div>
);
}