2026-03-16 13:35:11 +01:00
|
|
|
import { useEffect } from 'react'
|
2026-04-05 07:58:38 +02:00
|
|
|
import { BrowserRouter, Routes, Route, NavLink, useLocation } from 'react-router-dom'
|
|
|
|
|
import { LogOut } from 'lucide-react'
|
2026-03-16 13:35:11 +01:00
|
|
|
import { ProfileProvider, useProfile } from './context/ProfileContext'
|
|
|
|
|
import { AuthProvider, useAuth } from './context/AuthContext'
|
|
|
|
|
import { setProfileId } from './utils/api'
|
|
|
|
|
import { Avatar } from './pages/ProfileSelect'
|
|
|
|
|
import SetupScreen from './pages/SetupScreen'
|
|
|
|
|
import { ResetPassword } from './pages/PasswordRecovery'
|
|
|
|
|
import LoginScreen from './pages/LoginScreen'
|
2026-03-21 09:55:23 +01:00
|
|
|
import Register from './pages/Register'
|
|
|
|
|
import Verify from './pages/Verify'
|
2026-03-16 13:35:11 +01:00
|
|
|
import Dashboard from './pages/Dashboard'
|
2026-04-05 10:17:47 +02:00
|
|
|
import CaptureShell from './layouts/CaptureShell'
|
2026-03-16 13:35:11 +01:00
|
|
|
import CaptureHub from './pages/CaptureHub'
|
|
|
|
|
import WeightScreen from './pages/WeightScreen'
|
|
|
|
|
import CircumScreen from './pages/CircumScreen'
|
|
|
|
|
import CaliperScreen from './pages/CaliperScreen'
|
|
|
|
|
import MeasureWizard from './pages/MeasureWizard'
|
|
|
|
|
import History from './pages/History'
|
|
|
|
|
import NutritionPage from './pages/NutritionPage'
|
|
|
|
|
import ActivityPage from './pages/ActivityPage'
|
|
|
|
|
import Analysis from './pages/Analysis'
|
|
|
|
|
import SettingsPage from './pages/SettingsPage'
|
|
|
|
|
import GuidePage from './pages/GuidePage'
|
feat: add Tier Limits Matrix Editor (Admin UI)
Phase 3 Frontend - First Component: Matrix Editor
New page: AdminTierLimitsPage
- Displays Tier x Feature matrix (editable table)
- Inline editing for all limit values
- Visual feedback for changes (highlighted cells)
- Batch save with validation
- Category grouping (data, ai, export, integration)
- Legend: ∞ = unlimited (NULL), ❌ = disabled (0), 1-999 = limit
- Responsive table with sticky column headers
Features:
- GET /api/tier-limits - Load matrix
- PUT /api/tier-limits/batch - Save all changes
- Change tracking (shows unsaved count)
- Reset button to discard changes
- Success/error messages
API helpers added (api.js):
- v9c subscription endpoints (user + admin)
- listFeatures, listTiers, getTierLimitsMatrix
- updateTierLimit, updateTierLimitsBatch
- listCoupons, redeemCoupon
- User restrictions, access grants
Navigation:
- Link in AdminPanel (Settings Page)
- Route: /admin/tier-limits
Ready for testing on Dev!
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 15:21:52 +01:00
|
|
|
import AdminTierLimitsPage from './pages/AdminTierLimitsPage'
|
2026-03-20 06:35:13 +01:00
|
|
|
import AdminFeaturesPage from './pages/AdminFeaturesPage'
|
|
|
|
|
import AdminTiersPage from './pages/AdminTiersPage'
|
2026-03-20 07:53:47 +01:00
|
|
|
import AdminCouponsPage from './pages/AdminCouponsPage'
|
2026-03-20 07:59:49 +01:00
|
|
|
import AdminUserRestrictionsPage from './pages/AdminUserRestrictionsPage'
|
feat: admin CRUD for training types + distribution chart in ActivityPage
Backend (v9d Phase 1b):
- Migration 006: Add abilities JSONB column + descriptions
- admin_training_types.py: Full CRUD endpoints for training types
- List, Get, Create, Update, Delete
- Abilities taxonomy endpoint (5 dimensions: koordinativ, konditionell, kognitiv, psychisch, taktisch)
- Validation: Cannot delete types in use
- Register admin_training_types router in main.py
Frontend:
- AdminTrainingTypesPage: Full CRUD UI
- Create/edit form with all fields (category, subcategory, names, icon, descriptions, sort_order)
- List grouped by category with color coding
- Delete with usage check
- Note about abilities mapping coming in v9f
- Add TrainingTypeDistribution to ActivityPage stats tab
- Add admin link in AdminPanel (v9d section)
- Update api.js with admin training types methods
Notes:
- Abilities mapping UI deferred to v9f (flexible prompt system)
- Placeholders (abilities column) in place for future AI analysis
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 15:32:32 +01:00
|
|
|
import AdminTrainingTypesPage from './pages/AdminTrainingTypesPage'
|
2026-03-21 19:31:58 +01:00
|
|
|
import AdminActivityMappingsPage from './pages/AdminActivityMappingsPage'
|
2026-03-23 11:53:58 +01:00
|
|
|
import AdminTrainingProfiles from './pages/AdminTrainingProfiles'
|
2026-03-24 15:35:55 +01:00
|
|
|
import AdminPromptsPage from './pages/AdminPromptsPage'
|
2026-03-27 06:51:46 +01:00
|
|
|
import AdminGoalTypesPage from './pages/AdminGoalTypesPage'
|
2026-03-27 19:51:18 +01:00
|
|
|
import AdminFocusAreasPage from './pages/AdminFocusAreasPage'
|
2026-03-20 10:31:04 +01:00
|
|
|
import SubscriptionPage from './pages/SubscriptionPage'
|
2026-03-22 08:17:11 +01:00
|
|
|
import SleepPage from './pages/SleepPage'
|
2026-03-22 16:33:32 +01:00
|
|
|
import RestDaysPage from './pages/RestDaysPage'
|
2026-03-23 14:52:09 +01:00
|
|
|
import VitalsPage from './pages/VitalsPage'
|
feat: Phase 0a - Minimal Goal System (Strategic + Tactical)
- Strategic Layer: Goal modes (weight_loss, strength, endurance, recomposition, health)
- Tactical Layer: Concrete goal targets with progress tracking
- Training phases (manual + auto-detection framework)
- Fitness tests (standardized performance tracking)
Backend:
- Migration 022: goal_mode in profiles, goals, training_phases, fitness_tests tables
- New router: routers/goals.py with full CRUD for goals, phases, tests
- API endpoints: /api/goals/* (mode, list, create, update, delete)
Frontend:
- GoalsPage: Goal mode selector + goal management UI
- Dashboard: Goals preview card with link
- API integration: goal mode, CRUD operations, progress calculation
Basis for 120+ placeholders and goal-aware analyses (Phase 0b)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 16:20:35 +01:00
|
|
|
import GoalsPage from './pages/GoalsPage'
|
2026-03-27 15:32:15 +01:00
|
|
|
import CustomGoalsPage from './pages/CustomGoalsPage'
|
2026-04-04 17:56:00 +02:00
|
|
|
import WorkflowEditorPage from './pages/WorkflowEditorPage'
|
2026-04-05 07:58:38 +02:00
|
|
|
import DesktopSidebar from './components/DesktopSidebar'
|
|
|
|
|
import { getMainNavItems } from './config/appNav'
|
2026-04-05 10:17:47 +02:00
|
|
|
import { isCaptureSectionPath } from './config/captureNav'
|
2026-03-16 13:35:11 +01:00
|
|
|
import './app.css'
|
|
|
|
|
|
2026-04-05 07:58:38 +02:00
|
|
|
function navItemActive(pathname, item, routerIsActive) {
|
|
|
|
|
if (item.to.startsWith('/admin')) return pathname.startsWith('/admin')
|
2026-04-05 10:17:47 +02:00
|
|
|
if (item.to === '/capture' && isCaptureSectionPath(pathname)) return true
|
2026-04-05 07:58:38 +02:00
|
|
|
return routerIsActive
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Nav({ isAdmin }) {
|
|
|
|
|
const items = getMainNavItems(isAdmin)
|
|
|
|
|
const loc = useLocation()
|
2026-03-16 13:35:11 +01:00
|
|
|
return (
|
|
|
|
|
<nav className="bottom-nav">
|
2026-04-05 07:58:38 +02:00
|
|
|
{items.map((item) => (
|
|
|
|
|
<NavLink
|
|
|
|
|
key={item.to}
|
|
|
|
|
to={item.to}
|
|
|
|
|
end={!!item.end}
|
|
|
|
|
className={({ isActive }) =>
|
|
|
|
|
'nav-item' +
|
|
|
|
|
(navItemActive(loc.pathname, item, isActive) ? ' active' : '')
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<item.Icon size={20} strokeWidth={2} />
|
|
|
|
|
<span>{item.shortLabel || item.label}</span>
|
2026-03-16 13:35:11 +01:00
|
|
|
</NavLink>
|
|
|
|
|
))}
|
|
|
|
|
</nav>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function AppShell() {
|
2026-04-05 07:58:38 +02:00
|
|
|
const { session, loading: authLoading, needsSetup, logout, isAdmin } = useAuth()
|
|
|
|
|
const { activeProfile, loading: profileLoading } = useProfile()
|
2026-03-16 13:35:11 +01:00
|
|
|
|
2026-03-21 13:05:33 +01:00
|
|
|
const handleLogout = () => {
|
|
|
|
|
if (confirm('Wirklich abmelden?')) {
|
|
|
|
|
logout()
|
|
|
|
|
window.location.href = '/'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 13:35:11 +01:00
|
|
|
useEffect(()=>{
|
|
|
|
|
if (session?.profile_id) {
|
|
|
|
|
setProfileId(session.profile_id)
|
|
|
|
|
localStorage.setItem('mitai-jinkendo_active_profile', session.profile_id)
|
|
|
|
|
}
|
|
|
|
|
}, [session?.profile_id])
|
|
|
|
|
|
2026-03-21 09:55:23 +01:00
|
|
|
// Handle public pages (register, verify, reset-password)
|
2026-03-16 13:35:11 +01:00
|
|
|
const urlParams = new URLSearchParams(window.location.search)
|
2026-03-21 09:55:23 +01:00
|
|
|
const currentPath = window.location.pathname
|
|
|
|
|
|
|
|
|
|
// Register page
|
|
|
|
|
if (currentPath === '/register') return (
|
|
|
|
|
<div style={{minHeight:'100vh',background:'var(--bg)',padding:24}}>
|
|
|
|
|
<Register/>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Verify email page
|
|
|
|
|
if (currentPath === '/verify') return (
|
|
|
|
|
<div style={{minHeight:'100vh',background:'var(--bg)',padding:24}}>
|
|
|
|
|
<Verify/>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Password reset page
|
|
|
|
|
const resetToken = urlParams.get('reset-password') || (currentPath === '/reset-password' ? urlParams.get('token') : null)
|
2026-03-16 13:35:11 +01:00
|
|
|
if (resetToken) return (
|
|
|
|
|
<div style={{minHeight:'100vh',display:'flex',alignItems:'center',justifyContent:'center',
|
|
|
|
|
background:'var(--bg)',padding:24}}>
|
|
|
|
|
<div style={{width:'100%',maxWidth:360}}>
|
|
|
|
|
<div style={{textAlign:'center',marginBottom:20}}>
|
|
|
|
|
<div style={{fontSize:28,fontWeight:800,color:'var(--accent)'}}>Mitai Jinkendo</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="card" style={{padding:20}}>
|
|
|
|
|
<ResetPassword token={resetToken} onDone={()=>window.location.href='/'}/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Auth loading
|
|
|
|
|
if (authLoading) return (
|
|
|
|
|
<div style={{minHeight:'100vh',display:'flex',alignItems:'center',justifyContent:'center'}}>
|
|
|
|
|
<div className="spinner" style={{width:32,height:32}}/>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// First run
|
|
|
|
|
if (needsSetup) return <SetupScreen/>
|
|
|
|
|
|
|
|
|
|
// Need to log in
|
|
|
|
|
if (!session) return <LoginScreen/>
|
|
|
|
|
|
|
|
|
|
// Profile loading
|
|
|
|
|
if (profileLoading) return (
|
|
|
|
|
<div style={{minHeight:'100vh',display:'flex',alignItems:'center',justifyContent:'center'}}>
|
|
|
|
|
<div className="spinner" style={{width:32,height:32}}/>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="app-shell">
|
2026-04-05 07:58:38 +02:00
|
|
|
<DesktopSidebar
|
|
|
|
|
isAdmin={isAdmin}
|
|
|
|
|
activeProfile={activeProfile}
|
|
|
|
|
sessionProfile={session?.profile}
|
|
|
|
|
onLogout={handleLogout}
|
|
|
|
|
/>
|
|
|
|
|
<div className="app-shell__column">
|
|
|
|
|
<header className="app-header app-header--mobile">
|
|
|
|
|
<span className="app-logo">Mitai Jinkendo</span>
|
|
|
|
|
<div style={{ display: 'flex', gap: 12, alignItems: 'center' }}>
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={handleLogout}
|
|
|
|
|
title="Abmelden"
|
|
|
|
|
style={{
|
|
|
|
|
background: 'none',
|
|
|
|
|
border: 'none',
|
|
|
|
|
cursor: 'pointer',
|
|
|
|
|
padding: 6,
|
|
|
|
|
display: 'flex',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
color: 'var(--text2)',
|
|
|
|
|
transition: 'color 0.15s'
|
|
|
|
|
}}
|
|
|
|
|
onMouseEnter={(e) => {
|
|
|
|
|
e.currentTarget.style.color = '#D85A30'
|
|
|
|
|
}}
|
|
|
|
|
onMouseLeave={(e) => {
|
|
|
|
|
e.currentTarget.style.color = 'var(--text2)'
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<LogOut size={18} />
|
|
|
|
|
</button>
|
|
|
|
|
<NavLink to="/settings" style={{ textDecoration: 'none' }}>
|
|
|
|
|
{activeProfile ? (
|
|
|
|
|
<Avatar profile={activeProfile} size={30} />
|
|
|
|
|
) : (
|
|
|
|
|
<div
|
|
|
|
|
style={{
|
|
|
|
|
width: 30,
|
|
|
|
|
height: 30,
|
|
|
|
|
borderRadius: '50%',
|
|
|
|
|
background: 'var(--accent)'
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</NavLink>
|
|
|
|
|
</div>
|
|
|
|
|
</header>
|
|
|
|
|
<main className="app-main">
|
2026-03-16 13:35:11 +01:00
|
|
|
<Routes>
|
|
|
|
|
<Route path="/" element={<Dashboard/>}/>
|
2026-04-05 10:17:47 +02:00
|
|
|
<Route element={<CaptureShell />}>
|
|
|
|
|
<Route path="/capture" element={<CaptureHub />} />
|
|
|
|
|
<Route path="/wizard" element={<MeasureWizard />} />
|
|
|
|
|
<Route path="/weight" element={<WeightScreen />} />
|
|
|
|
|
<Route path="/circum" element={<CircumScreen />} />
|
|
|
|
|
<Route path="/caliper" element={<CaliperScreen />} />
|
|
|
|
|
<Route path="/sleep" element={<SleepPage />} />
|
|
|
|
|
<Route path="/rest-days" element={<RestDaysPage />} />
|
|
|
|
|
<Route path="/vitals" element={<VitalsPage />} />
|
|
|
|
|
<Route path="/custom-goals" element={<CustomGoalsPage />} />
|
|
|
|
|
<Route path="/nutrition" element={<NutritionPage />} />
|
|
|
|
|
<Route path="/activity" element={<ActivityPage />} />
|
|
|
|
|
<Route path="/guide" element={<GuidePage />} />
|
|
|
|
|
</Route>
|
2026-03-16 13:35:11 +01:00
|
|
|
<Route path="/history" element={<History/>}/>
|
feat: Phase 0a - Minimal Goal System (Strategic + Tactical)
- Strategic Layer: Goal modes (weight_loss, strength, endurance, recomposition, health)
- Tactical Layer: Concrete goal targets with progress tracking
- Training phases (manual + auto-detection framework)
- Fitness tests (standardized performance tracking)
Backend:
- Migration 022: goal_mode in profiles, goals, training_phases, fitness_tests tables
- New router: routers/goals.py with full CRUD for goals, phases, tests
- API endpoints: /api/goals/* (mode, list, create, update, delete)
Frontend:
- GoalsPage: Goal mode selector + goal management UI
- Dashboard: Goals preview card with link
- API integration: goal mode, CRUD operations, progress calculation
Basis for 120+ placeholders and goal-aware analyses (Phase 0b)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 16:20:35 +01:00
|
|
|
<Route path="/goals" element={<GoalsPage/>}/>
|
2026-03-16 13:35:11 +01:00
|
|
|
<Route path="/analysis" element={<Analysis/>}/>
|
|
|
|
|
<Route path="/settings" element={<SettingsPage/>}/>
|
feat: add Tier Limits Matrix Editor (Admin UI)
Phase 3 Frontend - First Component: Matrix Editor
New page: AdminTierLimitsPage
- Displays Tier x Feature matrix (editable table)
- Inline editing for all limit values
- Visual feedback for changes (highlighted cells)
- Batch save with validation
- Category grouping (data, ai, export, integration)
- Legend: ∞ = unlimited (NULL), ❌ = disabled (0), 1-999 = limit
- Responsive table with sticky column headers
Features:
- GET /api/tier-limits - Load matrix
- PUT /api/tier-limits/batch - Save all changes
- Change tracking (shows unsaved count)
- Reset button to discard changes
- Success/error messages
API helpers added (api.js):
- v9c subscription endpoints (user + admin)
- listFeatures, listTiers, getTierLimitsMatrix
- updateTierLimit, updateTierLimitsBatch
- listCoupons, redeemCoupon
- User restrictions, access grants
Navigation:
- Link in AdminPanel (Settings Page)
- Route: /admin/tier-limits
Ready for testing on Dev!
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 15:21:52 +01:00
|
|
|
<Route path="/admin/tier-limits" element={<AdminTierLimitsPage/>}/>
|
2026-03-20 06:35:13 +01:00
|
|
|
<Route path="/admin/features" element={<AdminFeaturesPage/>}/>
|
|
|
|
|
<Route path="/admin/tiers" element={<AdminTiersPage/>}/>
|
2026-03-20 07:53:47 +01:00
|
|
|
<Route path="/admin/coupons" element={<AdminCouponsPage/>}/>
|
2026-03-20 07:59:49 +01:00
|
|
|
<Route path="/admin/user-restrictions" element={<AdminUserRestrictionsPage/>}/>
|
feat: admin CRUD for training types + distribution chart in ActivityPage
Backend (v9d Phase 1b):
- Migration 006: Add abilities JSONB column + descriptions
- admin_training_types.py: Full CRUD endpoints for training types
- List, Get, Create, Update, Delete
- Abilities taxonomy endpoint (5 dimensions: koordinativ, konditionell, kognitiv, psychisch, taktisch)
- Validation: Cannot delete types in use
- Register admin_training_types router in main.py
Frontend:
- AdminTrainingTypesPage: Full CRUD UI
- Create/edit form with all fields (category, subcategory, names, icon, descriptions, sort_order)
- List grouped by category with color coding
- Delete with usage check
- Note about abilities mapping coming in v9f
- Add TrainingTypeDistribution to ActivityPage stats tab
- Add admin link in AdminPanel (v9d section)
- Update api.js with admin training types methods
Notes:
- Abilities mapping UI deferred to v9f (flexible prompt system)
- Placeholders (abilities column) in place for future AI analysis
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 15:32:32 +01:00
|
|
|
<Route path="/admin/training-types" element={<AdminTrainingTypesPage/>}/>
|
2026-03-21 19:31:58 +01:00
|
|
|
<Route path="/admin/activity-mappings" element={<AdminActivityMappingsPage/>}/>
|
2026-03-23 11:53:58 +01:00
|
|
|
<Route path="/admin/training-profiles" element={<AdminTrainingProfiles/>}/>
|
2026-03-24 15:35:55 +01:00
|
|
|
<Route path="/admin/prompts" element={<AdminPromptsPage/>}/>
|
2026-03-27 06:51:46 +01:00
|
|
|
<Route path="/admin/goal-types" element={<AdminGoalTypesPage/>}/>
|
2026-03-27 19:51:18 +01:00
|
|
|
<Route path="/admin/focus-areas" element={<AdminFocusAreasPage/>}/>
|
2026-04-04 17:56:00 +02:00
|
|
|
<Route path="/workflow-editor/:id" element={<WorkflowEditorPage/>}/>
|
2026-03-20 10:31:04 +01:00
|
|
|
<Route path="/subscription" element={<SubscriptionPage/>}/>
|
2026-03-16 13:35:11 +01:00
|
|
|
</Routes>
|
2026-04-05 07:58:38 +02:00
|
|
|
</main>
|
|
|
|
|
</div>
|
|
|
|
|
<Nav isAdmin={isAdmin} />
|
2026-03-16 13:35:11 +01:00
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default function App() {
|
|
|
|
|
return (
|
|
|
|
|
<AuthProvider>
|
|
|
|
|
<ProfileProvider>
|
|
|
|
|
<BrowserRouter>
|
|
|
|
|
<AppShell/>
|
|
|
|
|
</BrowserRouter>
|
|
|
|
|
</ProfileProvider>
|
|
|
|
|
</AuthProvider>
|
|
|
|
|
)
|
|
|
|
|
}
|