feat(mobile): native-feel sticky top bar + bottom tab nav
Build & Push Docker Images / test-backend (push) Successful in 22s
Build & Push Docker Images / test-frontend (push) Failing after 23s
Build & Push Docker Images / build-backend (push) Has been skipped
Build & Push Docker Images / build-frontend (push) Has been skipped

Replaces the floating pill nav with persistent mobile chrome that
frames scrolling content the way iOS/Android native apps do.

New components:
- TopBar: brand left, contextual action right (Admin pill for admins,
  bell for saved, Login button when logged out)
- BottomNav: 4-tab fixed nav (Search, Scan [elevated center FAB],
  Saved, Profile). Saved/Profile disabled when logged out, tapping
  opens LoginModal
- icons.jsx: 5 stroke-based SVG icons (Search, Scan, Bell, User, Cog)
  plus Logo; replaces emoji in nav chrome

Behavior changes:
- App.jsx: tracks publicScreen ('home'|'scan'|'search') so BottomNav
  can drive both top-level view + inner screen
- PublicView: screen state lifted to App via props (back-compat
  defaults preserved so existing call sites don't break)
- App.css: old mobile .view-switcher block hidden (replaced by
  BottomNav); .app padding bumped to clear the fixed bottom bar

Desktop (>768px) unchanged — existing centered pill nav still works,
new components hidden via display:none.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ichitux
2026-06-28 12:52:46 +02:00
parent 5b75321406
commit 1e928e5a93
8 changed files with 405 additions and 24 deletions
+66
View File
@@ -0,0 +1,66 @@
import { IconBell, IconCog, IconUser, IconLogo } from './icons';
import './TopBar.css';
function TopBar({
currentUser,
isAdmin,
onShowSaved,
onLoginRequest,
onAdminClick,
authChecked,
}) {
// ponytail: action slot priority — admin pill > bell (logged-in) > login button (logged-out).
// ponytail: hidden on desktop via .top-bar { display: none } above 768px.
function renderAction() {
if (isAdmin) {
return (
<button
type="button"
className="topbar-action topbar-action--admin"
onClick={onAdminClick}
aria-label="Admin panel"
>
<IconCog size={18} />
<span>Admin</span>
</button>
);
}
if (currentUser) {
return (
<button
type="button"
className="topbar-action"
onClick={onShowSaved}
aria-label="Saved notifications"
>
<IconBell size={22} />
</button>
);
}
if (authChecked) {
return (
<button
type="button"
className="topbar-action topbar-action--login"
onClick={onLoginRequest}
>
<IconUser size={18} />
<span>Login</span>
</button>
);
}
return <span className="topbar-action topbar-action--placeholder" />;
}
return (
<header className="top-bar">
<div className="topbar-brand">
<IconLogo size={22} className="topbar-brand-icon" />
<span className="topbar-brand-text">FarmaFinder</span>
</div>
<div className="topbar-actions">{renderAction()}</div>
</header>
);
}
export default TopBar;