feat(mobile): center bottom + FAB, floating header with left logo & right avatar, long-press triage sheet, and large swipeable queue
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m1s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m1s
This commit is contained in:
1 parent
90a6062c7a
commit
c02c7d20de
2 files changed
+585
-190
No files matched your search
+86
-73
@@ -330,24 +330,44 @@ export const App: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-canvas text-main flex flex-col font-sans selection:bg-accent/30">
|
||||
{/* Top Header Bar */}
|
||||
<header className="sticky top-0 z-30 bg-panel border-b border-border shadow-sm">
|
||||
{/* Mobile Header: Centered Logo & Instance Title */}
|
||||
<div className="sm:hidden flex items-center justify-center relative h-11 px-4">
|
||||
<div
|
||||
{/* Top Header Bar: Floating on mobile, solid panel on desktop */}
|
||||
<header className="sticky top-0 z-30 bg-canvas/85 backdrop-blur-md sm:bg-panel sm:border-b sm:border-border sm:shadow-sm">
|
||||
{/* Mobile Header: Floating Bar with Larger Logo Left (Triage Home) & Profile Avatar Right */}
|
||||
<div className="sm:hidden flex items-center justify-between h-14 px-4 pt-1">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
triggerHaptic(10);
|
||||
setActiveTab('triage');
|
||||
}}
|
||||
className="flex items-center gap-2 cursor-pointer select-none"
|
||||
className="h-11 px-2 -ml-2 rounded-xl flex items-center gap-2.5 cursor-pointer select-none active:scale-95 transition-transform"
|
||||
title="Zur Startseite (Triage)"
|
||||
>
|
||||
<div className="w-6 h-6 bg-accent text-on-accent rounded-btn flex items-center justify-center">
|
||||
<Brain className="w-3.5 h-3.5" />
|
||||
<div className="w-8 h-8 bg-accent text-on-accent rounded-xl shadow-sm flex items-center justify-center">
|
||||
<Brain className="w-4 h-4" />
|
||||
</div>
|
||||
<span className="font-bold text-sm text-main tracking-tight">
|
||||
<span className="font-bold text-base text-main tracking-tight">
|
||||
{authStatus.instanceName || 'MindSift'}
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
triggerHaptic(10);
|
||||
setShowMobileProfile(true);
|
||||
}}
|
||||
className={`w-11 h-11 -mr-1.5 rounded-full flex items-center justify-center transition-transform active:scale-95 cursor-pointer ${
|
||||
activeTab === 'user_settings' || activeTab === 'admin_settings' || activeTab === 'help'
|
||||
? 'ring-2 ring-accent'
|
||||
: ''
|
||||
}`}
|
||||
title="Profil & Einstellungen"
|
||||
>
|
||||
<div className="w-8 h-8 rounded-full bg-accent text-on-accent text-xs font-bold shadow-sm flex items-center justify-center">
|
||||
{user.displayName ? user.displayName.slice(0, 1).toUpperCase() : 'U'}
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Desktop Header: 3-column Layout (Logo, InstantCaptureBar, Profile Pill) */}
|
||||
@@ -648,113 +668,106 @@ export const App: React.FC = () => {
|
||||
</ViewErrorBoundary>
|
||||
</main>
|
||||
|
||||
{/* MOBILE BOTTOM NAVIGATION BAR */}
|
||||
<nav className="sm:hidden fixed bottom-0 left-0 right-0 z-40 bg-panel/95 backdrop-blur-md border-t border-border px-3 py-1 flex items-center justify-around pb-[calc(0.4rem+env(safe-area-inset-bottom))] shadow-panel">
|
||||
{/* Tab 1: Triage */}
|
||||
{/* MOBILE BOTTOM NAVIGATION BAR (5 equal columns -> + FAB is dead-center) */}
|
||||
<nav className="sm:hidden fixed bottom-0 left-0 right-0 z-40 bg-panel/95 backdrop-blur-md border-t border-border px-2 pt-1.5 pb-[calc(0.5rem+env(safe-area-inset-bottom))] grid grid-cols-5 items-center shadow-panel">
|
||||
{/* Col 1: Triage */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
triggerHaptic(10);
|
||||
setActiveTab('triage');
|
||||
}}
|
||||
className={`p-2 rounded-btn flex flex-col items-center justify-center transition-colors relative cursor-pointer ${
|
||||
activeTab === 'triage' ? 'text-accent' : 'text-muted hover:text-main'
|
||||
className={`min-h-[48px] rounded-xl flex flex-col items-center justify-center gap-0.5 transition-colors relative cursor-pointer active:scale-95 ${
|
||||
activeTab === 'triage' ? 'text-accent font-semibold' : 'text-muted hover:text-main'
|
||||
}`}
|
||||
title="Triage"
|
||||
>
|
||||
<Layers className="w-5 h-5" />
|
||||
{stats.inboxCount > 0 && (
|
||||
<span className="absolute -top-0.5 -right-0.5 min-w-[15px] h-[15px] px-0.5 rounded-full text-[9px] font-bold leading-none bg-accent text-on-accent flex items-center justify-center shadow-xs pointer-events-none">
|
||||
{stats.inboxCount}
|
||||
</span>
|
||||
)}
|
||||
<div className="relative">
|
||||
<Layers className="w-5 h-5" />
|
||||
{stats.inboxCount > 0 && (
|
||||
<span className="absolute -top-1.5 -right-2.5 min-w-[16px] h-[16px] px-1 rounded-full text-[9px] font-bold leading-none bg-accent text-on-accent flex items-center justify-center shadow-xs pointer-events-none">
|
||||
{stats.inboxCount}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<span className="text-[10px] leading-tight">Triage</span>
|
||||
</button>
|
||||
|
||||
{/* Tab 2: Aufgaben */}
|
||||
{/* Col 2: Aufgaben */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
triggerHaptic(10);
|
||||
setActiveTab('tasks');
|
||||
}}
|
||||
className={`p-2 rounded-btn flex flex-col items-center justify-center transition-colors relative cursor-pointer ${
|
||||
activeTab === 'tasks' ? 'text-accent' : 'text-muted hover:text-main'
|
||||
className={`min-h-[48px] rounded-xl flex flex-col items-center justify-center gap-0.5 transition-colors relative cursor-pointer active:scale-95 ${
|
||||
activeTab === 'tasks' ? 'text-accent font-semibold' : 'text-muted hover:text-main'
|
||||
}`}
|
||||
title="Aufgaben"
|
||||
>
|
||||
<CheckSquare className="w-5 h-5" />
|
||||
{stats.tasksOpenCount > 0 && (
|
||||
<span className="absolute -top-0.5 -right-0.5 min-w-[15px] h-[15px] px-0.5 rounded-full text-[9px] font-bold leading-none bg-accent text-on-accent flex items-center justify-center shadow-xs pointer-events-none">
|
||||
{stats.tasksOpenCount}
|
||||
</span>
|
||||
)}
|
||||
<div className="relative">
|
||||
<CheckSquare className="w-5 h-5" />
|
||||
{stats.tasksOpenCount > 0 && (
|
||||
<span className="absolute -top-1.5 -right-2.5 min-w-[16px] h-[16px] px-1 rounded-full text-[9px] font-bold leading-none bg-accent text-on-accent flex items-center justify-center shadow-xs pointer-events-none">
|
||||
{stats.tasksOpenCount}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<span className="text-[10px] leading-tight">Aufgaben</span>
|
||||
</button>
|
||||
|
||||
{/* CENTER ACTION: + Quick-Add FAB */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
triggerHaptic(15);
|
||||
setShowMobileCapture(true);
|
||||
}}
|
||||
className="w-12 h-12 -mt-5 rounded-full bg-accent text-on-accent shadow-lg flex items-center justify-center active:scale-90 transition-transform border-4 border-panel cursor-pointer"
|
||||
title="Schnell erfassen (+)"
|
||||
>
|
||||
<Plus className="w-6 h-6 stroke-[2.5]" />
|
||||
</button>
|
||||
{/* Col 3 (EXACT CENTER): + Quick-Add FAB */}
|
||||
<div className="flex items-center justify-center">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
triggerHaptic(15);
|
||||
setShowMobileCapture(true);
|
||||
}}
|
||||
className="w-14 h-14 -mt-6 rounded-full bg-accent text-on-accent shadow-lg flex items-center justify-center active:scale-90 transition-transform border-4 border-panel cursor-pointer"
|
||||
title="Schnell erfassen (+)"
|
||||
>
|
||||
<Plus className="w-7 h-7 stroke-[2.5]" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Tab 3: Workspace */}
|
||||
{/* Col 4: Workspace */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
triggerHaptic(10);
|
||||
setActiveTab('workspace');
|
||||
}}
|
||||
className={`p-2 rounded-btn flex flex-col items-center justify-center transition-colors relative cursor-pointer ${
|
||||
activeTab === 'workspace' ? 'text-accent' : 'text-muted hover:text-main'
|
||||
className={`min-h-[48px] rounded-xl flex flex-col items-center justify-center gap-0.5 transition-colors relative cursor-pointer active:scale-95 ${
|
||||
activeTab === 'workspace' ? 'text-accent font-semibold' : 'text-muted hover:text-main'
|
||||
}`}
|
||||
title="Workspace"
|
||||
>
|
||||
<FileText className="w-5 h-5" />
|
||||
{(stats.docsTodosOpenCount ?? 0) > 0 && (
|
||||
<span className="absolute -top-0.5 -right-0.5 min-w-[15px] h-[15px] px-0.5 rounded-full text-[9px] font-bold leading-none bg-accent text-on-accent flex items-center justify-center shadow-xs pointer-events-none">
|
||||
{stats.docsTodosOpenCount}
|
||||
</span>
|
||||
)}
|
||||
<div className="relative">
|
||||
<FileText className="w-5 h-5" />
|
||||
{(stats.docsTodosOpenCount ?? 0) > 0 && (
|
||||
<span className="absolute -top-1.5 -right-2.5 min-w-[16px] h-[16px] px-1 rounded-full text-[9px] font-bold leading-none bg-accent text-on-accent flex items-center justify-center shadow-xs pointer-events-none">
|
||||
{stats.docsTodosOpenCount}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<span className="text-[10px] leading-tight">Workspace</span>
|
||||
</button>
|
||||
|
||||
{/* Tab 4: Listen */}
|
||||
{/* Col 5: Listen */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
triggerHaptic(10);
|
||||
setActiveTab('lists');
|
||||
}}
|
||||
className={`p-2 rounded-btn flex flex-col items-center justify-center transition-colors relative cursor-pointer ${
|
||||
activeTab === 'lists' ? 'text-accent' : 'text-muted hover:text-main'
|
||||
className={`min-h-[48px] rounded-xl flex flex-col items-center justify-center gap-0.5 transition-colors relative cursor-pointer active:scale-95 ${
|
||||
activeTab === 'lists' ? 'text-accent font-semibold' : 'text-muted hover:text-main'
|
||||
}`}
|
||||
title="Listen"
|
||||
>
|
||||
<ShoppingCart className="w-5 h-5" />
|
||||
</button>
|
||||
|
||||
{/* User Profile Avatar */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
triggerHaptic(10);
|
||||
setShowMobileProfile(true);
|
||||
}}
|
||||
className={`p-1.5 rounded-btn flex items-center justify-center transition-colors cursor-pointer ${
|
||||
activeTab === 'user_settings' || activeTab === 'admin_settings' || activeTab === 'help'
|
||||
? 'ring-2 ring-accent'
|
||||
: 'opacity-85 hover:opacity-100'
|
||||
}`}
|
||||
title="Profil & Einstellungen"
|
||||
>
|
||||
<div className="w-6 h-6 rounded-full bg-accent text-on-accent text-[11px] font-bold flex items-center justify-center">
|
||||
{user.displayName ? user.displayName.slice(0, 1).toUpperCase() : 'U'}
|
||||
</div>
|
||||
<span className="text-[10px] leading-tight">Listen</span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
|
||||
Reference in new issue
Block a user