diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 9e75963..1f6227f 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -11,6 +11,7 @@ import LoginScreen from './pages/LoginScreen' import Register from './pages/Register' import Verify from './pages/Verify' import Dashboard from './pages/Dashboard' +import CaptureShell from './layouts/CaptureShell' import CaptureHub from './pages/CaptureHub' import WeightScreen from './pages/WeightScreen' import CircumScreen from './pages/CircumScreen' @@ -42,10 +43,12 @@ import CustomGoalsPage from './pages/CustomGoalsPage' import WorkflowEditorPage from './pages/WorkflowEditorPage' import DesktopSidebar from './components/DesktopSidebar' import { getMainNavItems } from './config/appNav' +import { isCaptureSectionPath } from './config/captureNav' import './app.css' function navItemActive(pathname, item, routerIsActive) { if (item.to.startsWith('/admin')) return pathname.startsWith('/admin') + if (item.to === '/capture' && isCaptureSectionPath(pathname)) return true return routerIsActive } @@ -198,22 +201,24 @@ function AppShell() {
}/> - }/> - }/> - }/> - }/> - }/> + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + }/> - }/> - }/> - }/> }/> - }/> - }/> - }/> }/> }/> - }/> }/> }/> }/> diff --git a/frontend/src/app.css b/frontend/src/app.css index abdb611..88e781d 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -363,6 +363,120 @@ body { font-family: var(--font); background: var(--bg); color: var(--text1); -we } } +/* Erfassung: Sub-Navigation (Mobil = Chips, Desktop = linke Spalte) */ +.capture-shell { + width: 100%; +} + +.capture-shell__layout { + display: flex; + flex-direction: column; + gap: 16px; +} + +.capture-shell__nav { + display: flex; + flex-direction: row; + gap: 6px; + overflow-x: auto; + padding-bottom: 6px; + -ms-overflow-style: none; + scrollbar-width: none; +} + +.capture-shell__nav::-webkit-scrollbar { + display: none; +} + +.capture-shell__nav-item { + flex-shrink: 0; + display: inline-flex; + align-items: center; + gap: 6px; + padding: 7px 12px; + border-radius: 20px; + border: 1.5px solid var(--border2); + background: var(--surface); + color: var(--text2); + font-family: var(--font); + font-size: 13px; + font-weight: 500; + text-decoration: none; + white-space: nowrap; + cursor: pointer; + box-sizing: border-box; +} + +.capture-shell__nav-item:hover { + border-color: var(--accent); + color: var(--text1); +} + +.capture-shell__nav-item--active { + border-color: var(--accent); + background: var(--accent); + color: white; +} + +.capture-shell__nav-item--active:hover { + color: white; +} + +.capture-shell__nav-item--highlight:not(.capture-shell__nav-item--active) { + border-color: #7f77dd88; + background: #7f77dd14; +} + +.capture-shell__nav-icon { + font-size: 15px; + line-height: 1; +} + +.capture-shell__nav-label { + line-height: 1.2; +} + +.capture-shell__main { + min-width: 0; +} + +@media (min-width: 1024px) { + .capture-shell__layout { + flex-direction: row; + align-items: flex-start; + gap: 24px; + } + + .capture-shell__nav-wrap { + flex: 0 0 260px; + max-width: 280px; + position: sticky; + top: 16px; + align-self: flex-start; + } + + .capture-shell__nav { + flex-direction: column; + overflow-x: visible; + overflow-y: auto; + max-height: calc(100vh - 140px); + padding-bottom: 0; + gap: 8px; + } + + .capture-shell__nav-item { + width: 100%; + justify-content: flex-start; + border-radius: 10px; + white-space: normal; + padding: 9px 12px; + } + + .capture-shell__main { + flex: 1; + } +} + .muted { color: var(--text3); font-size: 13px; } .empty-state { text-align: center; padding: 48px 16px; color: var(--text3); } .empty-state h3 { font-size: 16px; color: var(--text2); margin-bottom: 6px; } diff --git a/frontend/src/components/DesktopSidebar.jsx b/frontend/src/components/DesktopSidebar.jsx index e185045..2e99b8a 100644 --- a/frontend/src/components/DesktopSidebar.jsx +++ b/frontend/src/components/DesktopSidebar.jsx @@ -2,9 +2,11 @@ import { NavLink, useLocation } from 'react-router-dom' import { LogOut } from 'lucide-react' import { Avatar } from '../pages/ProfileSelect' import { getMainNavItems } from '../config/appNav' +import { isCaptureSectionPath } from '../config/captureNav' function sidebarLinkActive(pathname, item, routerIsActive) { if (item.to.startsWith('/admin')) return pathname.startsWith('/admin') + if (item.to === '/capture' && isCaptureSectionPath(pathname)) return true return routerIsActive } diff --git a/frontend/src/config/captureNav.js b/frontend/src/config/captureNav.js new file mode 100644 index 0000000..58471c9 --- /dev/null +++ b/frontend/src/config/captureNav.js @@ -0,0 +1,103 @@ +/** + * Erfassungs-Routen: Kachel-Hub + Sub-Navigation (Chip / Seitenleiste). + * Pfade müssen mit den Routes in App.jsx unter CaptureShell übereinstimmen. + */ + +export const CAPTURE_HUB_TILES = [ + { + icon: '⚖️', + label: 'Gewicht', + sub: 'Tägliche Gewichtseingabe', + to: '/weight', + color: '#378ADD', + }, + { + icon: '🪄', + label: 'Assistent', + sub: 'Schritt-für-Schritt Messung (Umfänge & Caliper)', + to: '/wizard', + color: '#7F77DD', + highlight: true, + }, + { + icon: '📏', + label: 'Umfänge', + sub: 'Hals, Brust, Taille, Bauch, Hüfte, Oberschenkel, Wade, Arm', + to: '/circum', + color: '#1D9E75', + }, + { + icon: '📐', + label: 'Caliper', + sub: 'Körperfett per Hautfaltenmessung', + to: '/caliper', + color: '#D85A30', + }, + { + icon: '🍽️', + label: 'Ernährung', + sub: 'FDDB CSV importieren', + to: '/nutrition', + color: '#EF9F27', + }, + { + icon: '🏋️', + label: 'Aktivität', + sub: 'Training manuell oder Apple Health importieren', + to: '/activity', + color: '#D4537E', + }, + { + icon: '🌙', + label: 'Schlaf', + sub: 'Schlafdaten erfassen oder Apple Health importieren', + to: '/sleep', + color: '#7B68EE', + }, + { + icon: '🛌', + label: 'Ruhetage', + sub: 'Kraft-, Cardio-, oder Entspannungs-Ruhetag erfassen', + to: '/rest-days', + color: '#9B59B6', + }, + { + icon: '❤️', + label: 'Vitalwerte', + sub: 'Ruhepuls und HRV morgens erfassen', + to: '/vitals', + color: '#E74C3C', + }, + { + icon: '🎯', + label: 'Eigene Ziele', + sub: 'Fortschritte für individuelle Ziele erfassen', + to: '/custom-goals', + color: '#1D9E75', + }, + { + icon: '📖', + label: 'Messanleitung', + sub: 'Wie und wo genau messen?', + to: '/guide', + color: '#888780', + }, +] + +/** Erster Eintrag: zurück zur Kachel-Übersicht */ +const OVERVIEW_ENTRY = { + icon: '📋', + label: 'Übersicht', + sub: 'Alle Erfassungsarten', + to: '/capture', + color: 'var(--accent)', +} + +/** Reihenfolge für Chip- / Seitenleiste (inkl. Übersicht) */ +export const CAPTURE_SHELL_NAV_ITEMS = [OVERVIEW_ENTRY, ...CAPTURE_HUB_TILES] + +export const CAPTURE_SECTION_PATHS = CAPTURE_SHELL_NAV_ITEMS.map((e) => e.to) + +export function isCaptureSectionPath(pathname) { + return CAPTURE_SECTION_PATHS.includes(pathname) +} diff --git a/frontend/src/layouts/CaptureShell.jsx b/frontend/src/layouts/CaptureShell.jsx new file mode 100644 index 0000000..c28b56d --- /dev/null +++ b/frontend/src/layouts/CaptureShell.jsx @@ -0,0 +1,36 @@ +import { Outlet, NavLink } from 'react-router-dom' +import { CAPTURE_SHELL_NAV_ITEMS } from '../config/captureNav' + +/** + * Erfassung: Mobil Chip-Leiste, Desktop linke Spalte – Wechsel zwischen Masken ohne Hub. + */ +export default function CaptureShell() { + return ( +
+
+ +
+ +
+
+
+ ) +} diff --git a/frontend/src/pages/CaptureHub.jsx b/frontend/src/pages/CaptureHub.jsx index 5888fdb..562c3bc 100644 --- a/frontend/src/pages/CaptureHub.jsx +++ b/frontend/src/pages/CaptureHub.jsx @@ -1,86 +1,6 @@ import { useNavigate } from 'react-router-dom' import { ChevronRight } from 'lucide-react' - -const ENTRIES = [ - { - icon: '⚖️', - label: 'Gewicht', - sub: 'Tägliche Gewichtseingabe', - to: '/weight', - color: '#378ADD', - }, - { - icon: '🪄', - label: 'Assistent', - sub: 'Schritt-für-Schritt Messung (Umfänge & Caliper)', - to: '/wizard', - color: '#7F77DD', - highlight: true, - }, - { - icon: '📏', - label: 'Umfänge', - sub: 'Hals, Brust, Taille, Bauch, Hüfte, Oberschenkel, Wade, Arm', - to: '/circum', - color: '#1D9E75', - }, - { - icon: '📐', - label: 'Caliper', - sub: 'Körperfett per Hautfaltenmessung', - to: '/caliper', - color: '#D85A30', - }, - { - icon: '🍽️', - label: 'Ernährung', - sub: 'FDDB CSV importieren', - to: '/nutrition', - color: '#EF9F27', - }, - { - icon: '🏋️', - label: 'Aktivität', - sub: 'Training manuell oder Apple Health importieren', - to: '/activity', - color: '#D4537E', - }, - { - icon: '🌙', - label: 'Schlaf', - sub: 'Schlafdaten erfassen oder Apple Health importieren', - to: '/sleep', - color: '#7B68EE', - }, - { - icon: '🛌', - label: 'Ruhetage', - sub: 'Kraft-, Cardio-, oder Entspannungs-Ruhetag erfassen', - to: '/rest-days', - color: '#9B59B6', - }, - { - icon: '❤️', - label: 'Vitalwerte', - sub: 'Ruhepuls und HRV morgens erfassen', - to: '/vitals', - color: '#E74C3C', - }, - { - icon: '🎯', - label: 'Eigene Ziele', - sub: 'Fortschritte für individuelle Ziele erfassen', - to: '/custom-goals', - color: '#1D9E75', - }, - { - icon: '📖', - label: 'Messanleitung', - sub: 'Wie und wo genau messen?', - to: '/guide', - color: '#888780', - }, -] +import { CAPTURE_HUB_TILES } from '../config/captureNav' export default function CaptureHub() { const nav = useNavigate() @@ -88,7 +8,7 @@ export default function CaptureHub() {

Erfassen

- {ENTRIES.map(e => ( + {CAPTURE_HUB_TILES.map(e => (