feat(mobile): add context-sensitive MindSift logo menu, slide-over file drawer, and larger touch targets
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 58s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 58s
This commit is contained in:
1 parent
cb0999325f
commit
212cb32bb0
8 files changed
+943
-111
No files matched your search
+137
-9
@@ -44,6 +44,8 @@ import { UserSettingsView } from './features/settings/UserSettingsView';
|
||||
import { AdminSettingsView } from './features/settings/AdminSettingsView';
|
||||
import { HelpView } from './features/help/HelpView';
|
||||
import { GroupsView } from './features/teams/GroupsView';
|
||||
import { useMobileActions } from './context/MobileActionContext';
|
||||
import { MobileHeaderMenu } from './components/MobileHeaderMenu';
|
||||
|
||||
type ActiveTab =
|
||||
| 'triage'
|
||||
@@ -129,6 +131,107 @@ export const App: React.FC = () => {
|
||||
// Mobile Navigation & Quick-Action State
|
||||
const [showMobileCapture, setShowMobileCapture] = useState(false);
|
||||
const [showMobileProfile, setShowMobileProfile] = useState(false);
|
||||
const { isMenuOpen, setIsMenuOpen, closeMenu, setGroups } = useMobileActions();
|
||||
|
||||
useEffect(() => {
|
||||
closeMenu();
|
||||
if (activeTab === 'triage') {
|
||||
setGroups([
|
||||
{
|
||||
id: 'triage-actions',
|
||||
title: 'Schnellaktionen',
|
||||
items: [
|
||||
{
|
||||
id: 'new-thought',
|
||||
label: 'Neuen Gedanken erfassen',
|
||||
icon: <Plus className="w-4 h-4" />,
|
||||
variant: 'accent',
|
||||
onClick: () => setShowMobileCapture(true),
|
||||
},
|
||||
{
|
||||
id: 'refresh-deck',
|
||||
label: 'Inbox aktualisieren',
|
||||
icon: <RotateCcw className="w-4 h-4" />,
|
||||
badge: stats.inboxCount,
|
||||
onClick: () => refreshAll(),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'triage-nav',
|
||||
title: 'Bereiche wechseln',
|
||||
items: [
|
||||
{
|
||||
id: 'goto-tasks',
|
||||
label: 'Aufgaben & Kanban',
|
||||
icon: <CheckSquare className="w-4 h-4" />,
|
||||
badge: stats.tasksOpenCount,
|
||||
onClick: () => setActiveTab('tasks'),
|
||||
},
|
||||
{
|
||||
id: 'goto-workspace',
|
||||
label: 'Notizen & Workspace',
|
||||
icon: <FileText className="w-4 h-4" />,
|
||||
badge: stats.docsCount,
|
||||
onClick: () => setActiveTab('workspace'),
|
||||
},
|
||||
{
|
||||
id: 'goto-lists',
|
||||
label: 'Einkaufslisten',
|
||||
icon: <ShoppingCart className="w-4 h-4" />,
|
||||
badge: stats.listsCount,
|
||||
onClick: () => setActiveTab('lists'),
|
||||
},
|
||||
{
|
||||
id: 'goto-groups',
|
||||
label: 'Teams & Gruppen',
|
||||
icon: <Users className="w-4 h-4" />,
|
||||
onClick: () => setActiveTab('groups'),
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
} else if (
|
||||
activeTab === 'groups' ||
|
||||
activeTab === 'user_settings' ||
|
||||
activeTab === 'admin_settings' ||
|
||||
activeTab === 'help'
|
||||
) {
|
||||
setGroups([
|
||||
{
|
||||
id: 'subview-nav',
|
||||
title: 'Navigation',
|
||||
items: [
|
||||
{
|
||||
id: 'back-triage',
|
||||
label: 'Zurück zur Triage (Startseite)',
|
||||
icon: <Layers className="w-4 h-4" />,
|
||||
variant: 'accent',
|
||||
onClick: () => setActiveTab('triage'),
|
||||
},
|
||||
{
|
||||
id: 'goto-tasks',
|
||||
label: 'Zu den Aufgaben',
|
||||
icon: <CheckSquare className="w-4 h-4" />,
|
||||
onClick: () => setActiveTab('tasks'),
|
||||
},
|
||||
{
|
||||
id: 'goto-workspace',
|
||||
label: 'Zum Workspace',
|
||||
icon: <FileText className="w-4 h-4" />,
|
||||
onClick: () => setActiveTab('workspace'),
|
||||
},
|
||||
{
|
||||
id: 'goto-lists',
|
||||
label: 'Zu den Listen',
|
||||
icon: <ShoppingCart className="w-4 h-4" />,
|
||||
onClick: () => setActiveTab('lists'),
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
}
|
||||
}, [activeTab, stats.inboxCount, stats.tasksOpenCount, stats.docsCount, stats.listsCount]);
|
||||
|
||||
// Mobile Web-App (PWA) Installation State
|
||||
const [isStandaloneApp, setIsStandaloneApp] = useState<boolean>(() => {
|
||||
@@ -409,27 +512,50 @@ export const App: React.FC = () => {
|
||||
const isServerAdmin = user.role === 'admin';
|
||||
const roleLabel = isServerAdmin ? 'Server-Admin' : 'Mitglied';
|
||||
|
||||
const activeTabLabelMap: Record<ActiveTab, string> = {
|
||||
triage: 'Triage',
|
||||
tasks: 'Aufgaben',
|
||||
workspace: 'Workspace',
|
||||
lists: 'Listen',
|
||||
groups: 'Teams & Gruppen',
|
||||
user_settings: 'Einstellungen',
|
||||
admin_settings: 'Administration',
|
||||
help: 'Hilfe',
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-canvas text-main flex flex-col font-sans selection:bg-accent/30">
|
||||
{/* 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 */}
|
||||
{/* Mobile Header: Floating Bar with Logo Action Dropdown Left & 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');
|
||||
triggerHaptic(12);
|
||||
setIsMenuOpen((prev) => !prev);
|
||||
}}
|
||||
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)"
|
||||
className={`min-h-[44px] px-2.5 -ml-2 rounded-xl flex items-center gap-2.5 cursor-pointer select-none active:scale-95 transition-all ${
|
||||
isMenuOpen ? 'bg-panel border border-accent/40 shadow-sm' : 'hover:bg-panel/60'
|
||||
}`}
|
||||
title="Menü & Ansichts-Optionen öffnen"
|
||||
>
|
||||
<div className="w-8 h-8 bg-accent text-on-accent rounded-xl shadow-sm flex items-center justify-center">
|
||||
<div className="w-8 h-8 bg-accent text-on-accent rounded-xl shadow-sm flex items-center justify-center flex-shrink-0">
|
||||
<Brain className="w-4 h-4" />
|
||||
</div>
|
||||
<span className="font-bold text-base text-main tracking-tight">
|
||||
{authStatus.instanceName || 'MindSift'}
|
||||
</span>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="font-bold text-base text-main tracking-tight">
|
||||
{authStatus.instanceName || 'MindSift'}
|
||||
</span>
|
||||
<span className="text-[11px] font-semibold text-accent bg-accent/15 px-1.5 py-0.5 rounded-md">
|
||||
{activeTabLabelMap[activeTab]}
|
||||
</span>
|
||||
<ChevronDown
|
||||
className={`w-4 h-4 text-muted transition-transform duration-150 ${
|
||||
isMenuOpen ? 'rotate-180 text-accent' : ''
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div className="flex items-center gap-1.5">
|
||||
@@ -715,6 +841,8 @@ export const App: React.FC = () => {
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<MobileHeaderMenu activeTabLabel={activeTabLabelMap[activeTab]} />
|
||||
|
||||
{/* Main Container */}
|
||||
<main className="flex-1 max-w-6xl w-full mx-auto px-3 sm:px-4 pt-3 pb-24 sm:pb-6">
|
||||
<ViewErrorBoundary onReset={() => setActiveTab('triage')}>
|
||||
|
||||
Reference in new issue
Block a user