2026-03-16 13:35:11 +01:00
|
|
|
import { getToken } from '../context/AuthContext'
|
|
|
|
|
|
|
|
|
|
let _profileId = null
|
|
|
|
|
export function setProfileId(id) { _profileId = id }
|
|
|
|
|
|
|
|
|
|
const BASE = '/api'
|
|
|
|
|
|
|
|
|
|
function hdrs(extra={}) {
|
|
|
|
|
const h = {...extra}
|
|
|
|
|
if (_profileId) h['X-Profile-Id'] = _profileId
|
|
|
|
|
const token = getToken()
|
|
|
|
|
if (token) h['X-Auth-Token'] = token
|
|
|
|
|
return h
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function req(path, opts={}) {
|
|
|
|
|
const res = await fetch(BASE+path, {...opts, headers:hdrs(opts.headers||{})})
|
2026-03-23 08:38:57 +01:00
|
|
|
if (!res.ok) {
|
|
|
|
|
const err = await res.text()
|
|
|
|
|
// Try to parse JSON error with detail field
|
|
|
|
|
try {
|
|
|
|
|
const parsed = JSON.parse(err)
|
|
|
|
|
throw new Error(parsed.detail || err)
|
|
|
|
|
} catch {
|
|
|
|
|
throw new Error(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-03-16 13:35:11 +01:00
|
|
|
return res.json()
|
|
|
|
|
}
|
|
|
|
|
const json=(d)=>({method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(d)})
|
|
|
|
|
const jput=(d)=>({method:'PUT', headers:{'Content-Type':'application/json'},body:JSON.stringify(d)})
|
|
|
|
|
|
|
|
|
|
export const api = {
|
|
|
|
|
// Profiles
|
2026-03-16 16:52:36 +01:00
|
|
|
getActiveProfile: () => req('/profile'),
|
2026-03-16 13:35:11 +01:00
|
|
|
listProfiles: () => req('/profiles'),
|
|
|
|
|
createProfile: (d) => req('/profiles', json(d)),
|
|
|
|
|
updateProfile: (id,d) => req(`/profiles/${id}`, jput(d)),
|
|
|
|
|
deleteProfile: (id) => req(`/profiles/${id}`, {method:'DELETE'}),
|
|
|
|
|
getProfile: () => req('/profile'),
|
|
|
|
|
updateActiveProfile:(d)=> req('/profile', jput(d)),
|
|
|
|
|
|
2026-04-06 19:45:06 +02:00
|
|
|
// Persönliche Referenzwerte (Profil, historisch)
|
|
|
|
|
listReferenceValueTypes: () => req('/reference-value-types'),
|
2026-04-06 21:25:42 +02:00
|
|
|
listReferenceValueMetaEnums: () => req('/reference-value-meta/enums'),
|
2026-04-06 19:45:06 +02:00
|
|
|
listProfileReferenceValues: (typeKey) =>
|
|
|
|
|
req(`/profile-reference-values?type_key=${encodeURIComponent(typeKey)}`),
|
|
|
|
|
createProfileReferenceValue: (d) => req('/profile-reference-values', json(d)),
|
|
|
|
|
updateProfileReferenceValue: (id, d) => req(`/profile-reference-values/${id}`, jput(d)),
|
|
|
|
|
deleteProfileReferenceValue: (id) => req(`/profile-reference-values/${id}`, { method: 'DELETE' }),
|
|
|
|
|
|
2026-04-06 19:51:23 +02:00
|
|
|
// Admin: Referenzwert-Typen (Katalog)
|
|
|
|
|
adminListReferenceValueTypes: () => req('/admin/reference-value-types'),
|
|
|
|
|
adminCreateReferenceValueType: (d) => req('/admin/reference-value-types', json(d)),
|
|
|
|
|
adminUpdateReferenceValueType: (id, d) => req(`/admin/reference-value-types/${id}`, jput(d)),
|
|
|
|
|
adminDeleteReferenceValueType: (id) => req(`/admin/reference-value-types/${id}`, { method: 'DELETE' }),
|
|
|
|
|
|
2026-03-16 13:35:11 +01:00
|
|
|
// Weight
|
|
|
|
|
listWeight: (l=365) => req(`/weight?limit=${l}`),
|
|
|
|
|
upsertWeight: (date,weight,note='') => req('/weight',json({date,weight,note})),
|
|
|
|
|
updateWeight: (id,date,weight,note='') => req(`/weight/${id}`,jput({date,weight,note})),
|
|
|
|
|
deleteWeight: (id) => req(`/weight/${id}`,{method:'DELETE'}),
|
|
|
|
|
weightStats: () => req('/weight/stats'),
|
|
|
|
|
|
|
|
|
|
// Circumferences
|
|
|
|
|
listCirc: (l=100) => req(`/circumferences?limit=${l}`),
|
|
|
|
|
upsertCirc: (d) => req('/circumferences',json(d)),
|
|
|
|
|
updateCirc: (id,d) => req(`/circumferences/${id}`,jput(d)),
|
|
|
|
|
deleteCirc: (id) => req(`/circumferences/${id}`,{method:'DELETE'}),
|
|
|
|
|
|
|
|
|
|
// Caliper
|
|
|
|
|
listCaliper: (l=100) => req(`/caliper?limit=${l}`),
|
|
|
|
|
upsertCaliper: (d) => req('/caliper',json(d)),
|
|
|
|
|
updateCaliper: (id,d) => req(`/caliper/${id}`,jput(d)),
|
|
|
|
|
deleteCaliper: (id) => req(`/caliper/${id}`,{method:'DELETE'}),
|
|
|
|
|
|
|
|
|
|
// Activity
|
|
|
|
|
listActivity: (l=200)=> req(`/activity?limit=${l}`),
|
|
|
|
|
createActivity: (d) => req('/activity',json(d)),
|
|
|
|
|
updateActivity: (id,d) => req(`/activity/${id}`,jput(d)),
|
|
|
|
|
deleteActivity: (id) => req(`/activity/${id}`,{method:'DELETE'}),
|
|
|
|
|
activityStats: () => req('/activity/stats'),
|
2026-03-21 15:08:18 +01:00
|
|
|
listUncategorizedActivities: () => req('/activity/uncategorized'),
|
|
|
|
|
bulkCategorizeActivities: (d) => req('/activity/bulk-categorize', json(d)),
|
2026-03-16 13:35:11 +01:00
|
|
|
importActivityCsv: async(file)=>{
|
|
|
|
|
const fd=new FormData();fd.append('file',file)
|
|
|
|
|
const r=await fetch(`${BASE}/activity/import-csv`,{method:'POST',body:fd,headers:hdrs()})
|
|
|
|
|
const d=await r.json();if(!r.ok)throw new Error(d.detail||JSON.stringify(d));return d
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// Photos
|
|
|
|
|
uploadPhoto: (file,date='')=>{
|
|
|
|
|
const fd=new FormData();fd.append('file',file);fd.append('date',date)
|
|
|
|
|
return fetch(`${BASE}/photos`,{method:'POST',body:fd,headers:hdrs()}).then(r=>r.json())
|
|
|
|
|
},
|
|
|
|
|
listPhotos: () => req('/photos'),
|
2026-03-18 22:48:40 +01:00
|
|
|
photoUrl: (pid) => {
|
|
|
|
|
const token = getToken()
|
|
|
|
|
return `${BASE}/photos/${pid}${token ? `?token=${token}` : ''}`
|
|
|
|
|
},
|
2026-03-16 13:35:11 +01:00
|
|
|
|
|
|
|
|
// Nutrition
|
|
|
|
|
importCsv: async(file)=>{
|
|
|
|
|
const fd=new FormData();fd.append('file',file)
|
|
|
|
|
const r=await fetch(`${BASE}/nutrition/import-csv`,{method:'POST',body:fd,headers:hdrs()})
|
|
|
|
|
const d=await r.json();if(!r.ok)throw new Error(d.detail||JSON.stringify(d));return d
|
|
|
|
|
},
|
|
|
|
|
listNutrition: (l=365) => req(`/nutrition?limit=${l}`),
|
|
|
|
|
nutritionCorrelations: () => req('/nutrition/correlations'),
|
|
|
|
|
nutritionWeekly: (w=16) => req(`/nutrition/weekly?weeks=${w}`),
|
feat: add nutrition entry editing and import history
Features:
- Import history panel showing all CSV imports with date, count, and range
- Edit/delete functionality for nutrition entries (inline editing)
- New backend endpoints: GET /import-history, PUT /{id}, DELETE /{id}
UI Changes:
- Import history displayed under import panel
- "Daten" tab now has edit/delete buttons per entry
- Inline form for editing macros (kcal, protein, fat, carbs)
- Confirmation dialog for deletion
Backend:
- nutrition.py: Added import_history, update_nutrition, delete_nutrition endpoints
- Groups imports by created date to show history
Frontend:
- NutritionPage: New DataTab and ImportHistory components
- api.js: Added nutritionImportHistory, updateNutrition, deleteNutrition
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 08:26:47 +01:00
|
|
|
nutritionImportHistory: () => req('/nutrition/import-history'),
|
2026-03-21 08:37:01 +01:00
|
|
|
getNutritionByDate: (date) => req(`/nutrition/by-date/${date}`),
|
|
|
|
|
createNutrition: (date,kcal,protein,fat,carbs) => req(`/nutrition?date=${date}&kcal=${kcal}&protein_g=${protein}&fat_g=${fat}&carbs_g=${carbs}`,{method:'POST'}),
|
feat: add nutrition entry editing and import history
Features:
- Import history panel showing all CSV imports with date, count, and range
- Edit/delete functionality for nutrition entries (inline editing)
- New backend endpoints: GET /import-history, PUT /{id}, DELETE /{id}
UI Changes:
- Import history displayed under import panel
- "Daten" tab now has edit/delete buttons per entry
- Inline form for editing macros (kcal, protein, fat, carbs)
- Confirmation dialog for deletion
Backend:
- nutrition.py: Added import_history, update_nutrition, delete_nutrition endpoints
- Groups imports by created date to show history
Frontend:
- NutritionPage: New DataTab and ImportHistory components
- api.js: Added nutritionImportHistory, updateNutrition, deleteNutrition
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 08:26:47 +01:00
|
|
|
updateNutrition: (id,kcal,protein,fat,carbs) => req(`/nutrition/${id}?kcal=${kcal}&protein_g=${protein}&fat_g=${fat}&carbs_g=${carbs}`,{method:'PUT'}),
|
|
|
|
|
deleteNutrition: (id) => req(`/nutrition/${id}`,{method:'DELETE'}),
|
2026-03-16 13:35:11 +01:00
|
|
|
|
|
|
|
|
// Stats & AI
|
|
|
|
|
getStats: () => req('/stats'),
|
|
|
|
|
insightTrend: () => req('/insights/trend',{method:'POST'}),
|
2026-03-16 16:52:36 +01:00
|
|
|
listPrompts: () => req('/prompts'),
|
|
|
|
|
runInsight: (slug) => req(`/insights/run/${slug}`,{method:'POST'}),
|
2026-03-16 13:35:11 +01:00
|
|
|
insightPipeline: () => req('/insights/pipeline',{method:'POST'}),
|
|
|
|
|
listInsights: () => req('/insights'),
|
|
|
|
|
latestInsights: () => req('/insights/latest'),
|
2026-03-26 11:40:19 +01:00
|
|
|
deleteInsight: (id) => req(`/insights/${id}`, {method:'DELETE'}),
|
2026-03-18 22:00:35 +01:00
|
|
|
exportZip: async () => {
|
|
|
|
|
const res = await fetch(`${BASE}/export/zip`, {headers: hdrs()})
|
|
|
|
|
if (!res.ok) throw new Error('Export failed')
|
|
|
|
|
const blob = await res.blob()
|
|
|
|
|
const url = window.URL.createObjectURL(blob)
|
|
|
|
|
const a = document.createElement('a')
|
|
|
|
|
a.href = url
|
|
|
|
|
a.download = `mitai-export-${new Date().toISOString().split('T')[0]}.zip`
|
|
|
|
|
document.body.appendChild(a)
|
|
|
|
|
a.click()
|
|
|
|
|
document.body.removeChild(a)
|
|
|
|
|
window.URL.revokeObjectURL(url)
|
|
|
|
|
},
|
|
|
|
|
exportJson: async () => {
|
|
|
|
|
const res = await fetch(`${BASE}/export/json`, {headers: hdrs()})
|
|
|
|
|
if (!res.ok) throw new Error('Export failed')
|
|
|
|
|
const blob = await res.blob()
|
|
|
|
|
const url = window.URL.createObjectURL(blob)
|
|
|
|
|
const a = document.createElement('a')
|
|
|
|
|
a.href = url
|
|
|
|
|
a.download = `mitai-export-${new Date().toISOString().split('T')[0]}.json`
|
|
|
|
|
document.body.appendChild(a)
|
|
|
|
|
a.click()
|
|
|
|
|
document.body.removeChild(a)
|
|
|
|
|
window.URL.revokeObjectURL(url)
|
|
|
|
|
},
|
|
|
|
|
exportCsv: async () => {
|
|
|
|
|
const res = await fetch(`${BASE}/export/csv`, {headers: hdrs()})
|
|
|
|
|
if (!res.ok) throw new Error('Export failed')
|
|
|
|
|
const blob = await res.blob()
|
|
|
|
|
const url = window.URL.createObjectURL(blob)
|
|
|
|
|
const a = document.createElement('a')
|
|
|
|
|
a.href = url
|
|
|
|
|
a.download = `mitai-export-${new Date().toISOString().split('T')[0]}.csv`
|
|
|
|
|
document.body.appendChild(a)
|
|
|
|
|
a.click()
|
|
|
|
|
document.body.removeChild(a)
|
|
|
|
|
window.URL.revokeObjectURL(url)
|
|
|
|
|
},
|
2026-03-16 13:35:11 +01:00
|
|
|
|
|
|
|
|
// Admin
|
|
|
|
|
adminListProfiles: () => req('/admin/profiles'),
|
|
|
|
|
adminCreateProfile: (d) => req('/admin/profiles',json(d)),
|
|
|
|
|
adminDeleteProfile: (id) => req(`/admin/profiles/${id}`,{method:'DELETE'}),
|
|
|
|
|
adminSetPermissions: (id,d) => req(`/admin/profiles/${id}/permissions`,jput(d)),
|
|
|
|
|
changePin: (pin) => req('/auth/pin',json({pin})),
|
2026-03-21 09:55:23 +01:00
|
|
|
register: (name,email,password) => req('/auth/register',json({name,email,password})),
|
|
|
|
|
verifyEmail: (token) => req(`/auth/verify/${token}`),
|
2026-03-21 10:23:38 +01:00
|
|
|
resendVerification: (email) => req('/auth/resend-verification',json({email})),
|
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
|
|
|
|
|
|
|
|
// v9c Subscription System
|
|
|
|
|
// User-facing
|
|
|
|
|
getMySubscription: () => req('/subscription/me'),
|
|
|
|
|
getMyUsage: () => req('/subscription/usage'),
|
|
|
|
|
getMyLimits: () => req('/subscription/limits'),
|
|
|
|
|
redeemCoupon: (code) => req('/coupons/redeem',json({code})),
|
2026-03-21 06:39:52 +01:00
|
|
|
getFeatureUsage: () => req('/features/usage'), // Phase 3: Usage overview
|
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
|
|
|
|
|
|
|
|
// Admin: Features
|
|
|
|
|
listFeatures: () => req('/features'),
|
|
|
|
|
createFeature: (d) => req('/features',json(d)),
|
|
|
|
|
updateFeature: (id,d) => req(`/features/${id}`,jput(d)),
|
|
|
|
|
deleteFeature: (id) => req(`/features/${id}`,{method:'DELETE'}),
|
|
|
|
|
|
|
|
|
|
// Admin: Tiers
|
|
|
|
|
listTiers: () => req('/tiers'),
|
|
|
|
|
createTier: (d) => req('/tiers',json(d)),
|
|
|
|
|
updateTier: (id,d) => req(`/tiers/${id}`,jput(d)),
|
|
|
|
|
deleteTier: (id) => req(`/tiers/${id}`,{method:'DELETE'}),
|
|
|
|
|
|
|
|
|
|
// Admin: Tier Limits (Matrix)
|
|
|
|
|
getTierLimitsMatrix: () => req('/tier-limits'),
|
|
|
|
|
updateTierLimit: (d) => req('/tier-limits',jput(d)),
|
|
|
|
|
updateTierLimitsBatch:(updates) => req('/tier-limits/batch',jput({updates})),
|
|
|
|
|
|
|
|
|
|
// Admin: User Restrictions
|
|
|
|
|
listUserRestrictions: (pid) => req(`/user-restrictions${pid?'?profile_id='+pid:''}`),
|
|
|
|
|
createUserRestriction:(d) => req('/user-restrictions',json(d)),
|
|
|
|
|
updateUserRestriction:(id,d) => req(`/user-restrictions/${id}`,jput(d)),
|
|
|
|
|
deleteUserRestriction:(id) => req(`/user-restrictions/${id}`,{method:'DELETE'}),
|
|
|
|
|
|
|
|
|
|
// Admin: Coupons
|
|
|
|
|
listCoupons: () => req('/coupons'),
|
|
|
|
|
createCoupon: (d) => req('/coupons',json(d)),
|
|
|
|
|
updateCoupon: (id,d) => req(`/coupons/${id}`,jput(d)),
|
|
|
|
|
deleteCoupon: (id) => req(`/coupons/${id}`,{method:'DELETE'}),
|
|
|
|
|
getCouponRedemptions: (id) => req(`/coupons/${id}/redemptions`),
|
|
|
|
|
|
|
|
|
|
// Admin: Access Grants
|
|
|
|
|
listAccessGrants: (pid,active)=> req(`/access-grants${pid?'?profile_id='+pid:''}${active?'&active_only=true':''}`),
|
|
|
|
|
createAccessGrant: (d) => req('/access-grants',json(d)),
|
|
|
|
|
updateAccessGrant: (id,d) => req(`/access-grants/${id}`,jput(d)),
|
|
|
|
|
revokeAccessGrant: (id) => req(`/access-grants/${id}`,{method:'DELETE'}),
|
2026-03-21 13:05:33 +01:00
|
|
|
|
|
|
|
|
// v9d: Training Types
|
|
|
|
|
listTrainingTypes: () => req('/training-types'), // Grouped by category
|
|
|
|
|
listTrainingTypesFlat:() => req('/training-types/flat'), // Flat list
|
|
|
|
|
getTrainingCategories:() => req('/training-types/categories'), // Category metadata
|
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
|
|
|
|
|
|
|
|
// Admin: Training Types (v9d Phase 1b)
|
|
|
|
|
adminListTrainingTypes: () => req('/admin/training-types'),
|
|
|
|
|
adminGetTrainingType: (id) => req(`/admin/training-types/${id}`),
|
|
|
|
|
adminCreateTrainingType: (d) => req('/admin/training-types', json(d)),
|
|
|
|
|
adminUpdateTrainingType: (id,d) => req(`/admin/training-types/${id}`, jput(d)),
|
|
|
|
|
adminDeleteTrainingType: (id) => req(`/admin/training-types/${id}`, {method:'DELETE'}),
|
|
|
|
|
getAbilitiesTaxonomy: () => req('/admin/training-types/taxonomy/abilities'),
|
2026-03-21 19:31:58 +01:00
|
|
|
|
2026-03-23 12:36:39 +01:00
|
|
|
// Admin: Training Type Profiles (Phase 2 #15)
|
|
|
|
|
getProfileStats: () => req('/admin/training-types/profiles/stats'),
|
|
|
|
|
getProfileTemplates: () => req('/admin/training-types/profiles/templates'),
|
|
|
|
|
getProfileTemplate: (key) => req(`/admin/training-types/profiles/templates/${key}`),
|
|
|
|
|
applyProfileTemplate: (id,templateKey) => req(`/admin/training-types/${id}/profile/apply-template`, json({template_key: templateKey})),
|
|
|
|
|
getTrainingParameters: () => req('/evaluation/parameters'),
|
|
|
|
|
batchEvaluateActivities: () => req('/evaluation/batch', {method:'POST'}),
|
|
|
|
|
|
2026-03-21 19:31:58 +01:00
|
|
|
// Admin: Activity Type Mappings (v9d Phase 1b - Learnable System)
|
|
|
|
|
adminListActivityMappings: (profileId, globalOnly) => req(`/admin/activity-mappings${profileId?'?profile_id='+profileId:''}${globalOnly?'?global_only=true':''}`),
|
|
|
|
|
adminGetActivityMapping: (id) => req(`/admin/activity-mappings/${id}`),
|
|
|
|
|
adminCreateActivityMapping: (d) => req('/admin/activity-mappings', json(d)),
|
|
|
|
|
adminUpdateActivityMapping: (id,d) => req(`/admin/activity-mappings/${id}`, jput(d)),
|
|
|
|
|
adminDeleteActivityMapping: (id) => req(`/admin/activity-mappings/${id}`, {method:'DELETE'}),
|
|
|
|
|
adminGetMappingCoverage: () => req('/admin/activity-mappings/stats/coverage'),
|
2026-03-22 08:17:11 +01:00
|
|
|
|
|
|
|
|
// Sleep Module (v9d Phase 2b)
|
|
|
|
|
listSleep: (l=90) => req(`/sleep?limit=${l}`),
|
|
|
|
|
getSleepByDate: (date) => req(`/sleep/by-date/${date}`),
|
|
|
|
|
createSleep: (d) => req('/sleep', json(d)),
|
|
|
|
|
updateSleep: (id,d) => req(`/sleep/${id}`, jput(d)),
|
|
|
|
|
deleteSleep: (id) => req(`/sleep/${id}`, {method:'DELETE'}),
|
|
|
|
|
getSleepStats: (days=7) => req(`/sleep/stats?days=${days}`),
|
|
|
|
|
getSleepDebt: (days=14) => req(`/sleep/debt?days=${days}`),
|
|
|
|
|
getSleepTrend: (days=30) => req(`/sleep/trend?days=${days}`),
|
|
|
|
|
getSleepPhases: (days=30) => req(`/sleep/phases?days=${days}`),
|
2026-03-22 11:49:09 +01:00
|
|
|
|
|
|
|
|
// Sleep Import (v9d Phase 2c)
|
|
|
|
|
importAppleHealthSleep: (file) => {
|
|
|
|
|
const fd = new FormData()
|
|
|
|
|
fd.append('file', file)
|
|
|
|
|
return req('/sleep/import/apple-health', {method:'POST', body:fd})
|
|
|
|
|
},
|
feat: flexible rest days system with JSONB config (v9d Phase 2a)
PROBLEM: Simple full_rest/active_recovery model doesn't support
context-specific rest days (e.g., strength rest but cardio allowed).
SOLUTION: JSONB-based flexible rest day configuration.
## Changes:
**Migration 010:**
- Refactor rest_days.type → rest_config JSONB
- Schema: {focus, rest_from[], allows[], intensity_max}
- Validation function with check constraint
- GIN index for performant JSONB queries
**Backend (routers/rest_days.py):**
- CRUD: list, create (upsert by date), get, update, delete
- Stats: count per week, focus distribution
- Validation: check activity conflicts with rest day config
**Frontend (api.js):**
- 7 new methods: listRestDays, createRestDay, updateRestDay,
deleteRestDay, getRestDaysStats, validateActivity
**Integration:**
- Router registered in main.py
- Ready for weekly planning validation rules
## Next Steps:
- Frontend UI (RestDaysPage with Quick/Custom mode)
- Activity conflict warnings
- Dashboard widget
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 16:20:52 +01:00
|
|
|
|
|
|
|
|
// Rest Days (v9d Phase 2a)
|
|
|
|
|
listRestDays: (l=90) => req(`/rest-days?limit=${l}`),
|
|
|
|
|
createRestDay: (d) => req('/rest-days', json(d)),
|
|
|
|
|
getRestDay: (id) => req(`/rest-days/${id}`),
|
|
|
|
|
updateRestDay: (id,d) => req(`/rest-days/${id}`, jput(d)),
|
|
|
|
|
deleteRestDay: (id) => req(`/rest-days/${id}`, {method:'DELETE'}),
|
|
|
|
|
getRestDaysStats: (weeks=4) => req(`/rest-days/stats?weeks=${weeks}`),
|
|
|
|
|
validateActivity: (date, activityType) => req('/rest-days/validate-activity', json({date, activity_type: activityType})),
|
2026-03-23 14:52:09 +01:00
|
|
|
|
refactor: vitals architecture - separate baseline vs blood pressure
BREAKING CHANGE: vitals_log split into vitals_baseline + blood_pressure_log
**Architektur-Änderung:**
- Baseline-Vitals (langsam veränderlich, 1x täglich morgens)
→ vitals_baseline (RHR, HRV, VO2 Max, SpO2, Atemfrequenz)
- Kontext-abhängige Vitals (mehrfach täglich, situativ)
→ blood_pressure_log (Blutdruck + Kontext-Tagging)
**Migration 015:**
- CREATE TABLE vitals_baseline (once daily, morning measurements)
- CREATE TABLE blood_pressure_log (multiple daily, context-aware)
- Migrate data from vitals_log → new tables
- Rename vitals_log → vitals_log_backup_pre_015 (safety)
- Prepared for future: glucose_log, temperature_log (commented)
**Backend:**
- NEW: routers/vitals_baseline.py (CRUD + Apple Health import)
- NEW: routers/blood_pressure.py (CRUD + Omron import + context)
- UPDATED: main.py (register new routers, remove old vitals)
- UPDATED: insights.py (query new tables, split template vars)
**Frontend:**
- UPDATED: api.js (new endpoints für baseline + BP)
- UPDATED: Analysis.jsx (add {{bp_summary}} variable)
**Nächster Schritt:**
- Frontend: VitalsPage.jsx refactoren (3 Tabs: Morgenmessung, Blutdruck, Import)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 16:02:40 +01:00
|
|
|
// Vitals Baseline (v9d Phase 2d Refactored - once daily, morning)
|
|
|
|
|
listBaseline: (l=90) => req(`/vitals/baseline?limit=${l}`),
|
|
|
|
|
getBaselineByDate: (date) => req(`/vitals/baseline/by-date/${date}`),
|
|
|
|
|
createBaseline: (d) => req('/vitals/baseline', json(d)),
|
|
|
|
|
updateBaseline: (id,d) => req(`/vitals/baseline/${id}`, jput(d)),
|
|
|
|
|
deleteBaseline: (id) => req(`/vitals/baseline/${id}`, {method:'DELETE'}),
|
|
|
|
|
getBaselineStats: (days=30) => req(`/vitals/baseline/stats?days=${days}`),
|
|
|
|
|
importBaselineAppleHealth: (file) => {
|
2026-03-23 15:26:51 +01:00
|
|
|
const fd = new FormData()
|
|
|
|
|
fd.append('file', file)
|
refactor: vitals architecture - separate baseline vs blood pressure
BREAKING CHANGE: vitals_log split into vitals_baseline + blood_pressure_log
**Architektur-Änderung:**
- Baseline-Vitals (langsam veränderlich, 1x täglich morgens)
→ vitals_baseline (RHR, HRV, VO2 Max, SpO2, Atemfrequenz)
- Kontext-abhängige Vitals (mehrfach täglich, situativ)
→ blood_pressure_log (Blutdruck + Kontext-Tagging)
**Migration 015:**
- CREATE TABLE vitals_baseline (once daily, morning measurements)
- CREATE TABLE blood_pressure_log (multiple daily, context-aware)
- Migrate data from vitals_log → new tables
- Rename vitals_log → vitals_log_backup_pre_015 (safety)
- Prepared for future: glucose_log, temperature_log (commented)
**Backend:**
- NEW: routers/vitals_baseline.py (CRUD + Apple Health import)
- NEW: routers/blood_pressure.py (CRUD + Omron import + context)
- UPDATED: main.py (register new routers, remove old vitals)
- UPDATED: insights.py (query new tables, split template vars)
**Frontend:**
- UPDATED: api.js (new endpoints für baseline + BP)
- UPDATED: Analysis.jsx (add {{bp_summary}} variable)
**Nächster Schritt:**
- Frontend: VitalsPage.jsx refactoren (3 Tabs: Morgenmessung, Blutdruck, Import)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 16:02:40 +01:00
|
|
|
return req('/vitals/baseline/import/apple-health', {method:'POST', body:fd})
|
2026-03-23 15:26:51 +01:00
|
|
|
},
|
refactor: vitals architecture - separate baseline vs blood pressure
BREAKING CHANGE: vitals_log split into vitals_baseline + blood_pressure_log
**Architektur-Änderung:**
- Baseline-Vitals (langsam veränderlich, 1x täglich morgens)
→ vitals_baseline (RHR, HRV, VO2 Max, SpO2, Atemfrequenz)
- Kontext-abhängige Vitals (mehrfach täglich, situativ)
→ blood_pressure_log (Blutdruck + Kontext-Tagging)
**Migration 015:**
- CREATE TABLE vitals_baseline (once daily, morning measurements)
- CREATE TABLE blood_pressure_log (multiple daily, context-aware)
- Migrate data from vitals_log → new tables
- Rename vitals_log → vitals_log_backup_pre_015 (safety)
- Prepared for future: glucose_log, temperature_log (commented)
**Backend:**
- NEW: routers/vitals_baseline.py (CRUD + Apple Health import)
- NEW: routers/blood_pressure.py (CRUD + Omron import + context)
- UPDATED: main.py (register new routers, remove old vitals)
- UPDATED: insights.py (query new tables, split template vars)
**Frontend:**
- UPDATED: api.js (new endpoints für baseline + BP)
- UPDATED: Analysis.jsx (add {{bp_summary}} variable)
**Nächster Schritt:**
- Frontend: VitalsPage.jsx refactoren (3 Tabs: Morgenmessung, Blutdruck, Import)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 16:02:40 +01:00
|
|
|
|
|
|
|
|
// Blood Pressure (v9d Phase 2d Refactored - multiple daily, context-aware)
|
|
|
|
|
listBloodPressure: (l=90) => req(`/blood-pressure?limit=${l}`),
|
|
|
|
|
getBPByDate: (date) => req(`/blood-pressure/by-date/${date}`),
|
|
|
|
|
createBloodPressure:(d) => req('/blood-pressure', json(d)),
|
|
|
|
|
updateBloodPressure:(id,d) => req(`/blood-pressure/${id}`, jput(d)),
|
|
|
|
|
deleteBloodPressure:(id) => req(`/blood-pressure/${id}`, {method:'DELETE'}),
|
|
|
|
|
getBPStats: (days=30) => req(`/blood-pressure/stats?days=${days}`),
|
|
|
|
|
importBPOmron: (file) => {
|
2026-03-23 15:26:51 +01:00
|
|
|
const fd = new FormData()
|
|
|
|
|
fd.append('file', file)
|
refactor: vitals architecture - separate baseline vs blood pressure
BREAKING CHANGE: vitals_log split into vitals_baseline + blood_pressure_log
**Architektur-Änderung:**
- Baseline-Vitals (langsam veränderlich, 1x täglich morgens)
→ vitals_baseline (RHR, HRV, VO2 Max, SpO2, Atemfrequenz)
- Kontext-abhängige Vitals (mehrfach täglich, situativ)
→ blood_pressure_log (Blutdruck + Kontext-Tagging)
**Migration 015:**
- CREATE TABLE vitals_baseline (once daily, morning measurements)
- CREATE TABLE blood_pressure_log (multiple daily, context-aware)
- Migrate data from vitals_log → new tables
- Rename vitals_log → vitals_log_backup_pre_015 (safety)
- Prepared for future: glucose_log, temperature_log (commented)
**Backend:**
- NEW: routers/vitals_baseline.py (CRUD + Apple Health import)
- NEW: routers/blood_pressure.py (CRUD + Omron import + context)
- UPDATED: main.py (register new routers, remove old vitals)
- UPDATED: insights.py (query new tables, split template vars)
**Frontend:**
- UPDATED: api.js (new endpoints für baseline + BP)
- UPDATED: Analysis.jsx (add {{bp_summary}} variable)
**Nächster Schritt:**
- Frontend: VitalsPage.jsx refactoren (3 Tabs: Morgenmessung, Blutdruck, Import)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 16:02:40 +01:00
|
|
|
return req('/blood-pressure/import/omron', {method:'POST', body:fd})
|
2026-03-23 15:26:51 +01:00
|
|
|
},
|
2026-03-24 15:35:55 +01:00
|
|
|
|
|
|
|
|
// AI Prompts Management (Issue #28)
|
|
|
|
|
listAdminPrompts: () => req('/prompts'),
|
2026-04-04 17:56:00 +02:00
|
|
|
getPrompt: (id) => req(`/prompts/${id}`),
|
2026-03-24 15:35:55 +01:00
|
|
|
createPrompt: (d) => req('/prompts', json(d)),
|
|
|
|
|
updatePrompt: (id,d) => req(`/prompts/${id}`, jput(d)),
|
|
|
|
|
deletePrompt: (id) => req(`/prompts/${id}`, {method:'DELETE'}),
|
|
|
|
|
duplicatePrompt: (id) => req(`/prompts/${id}/duplicate`, json({})),
|
|
|
|
|
reorderPrompts: (order) => req('/prompts/reorder', jput(order)),
|
|
|
|
|
previewPrompt: (tpl) => req('/prompts/preview', json({template:tpl})),
|
|
|
|
|
generatePrompt: (d) => req('/prompts/generate', json(d)),
|
|
|
|
|
optimizePrompt: (id) => req(`/prompts/${id}/optimize`, json({})),
|
|
|
|
|
listPlaceholders: () => req('/prompts/placeholders'),
|
feat: Pipeline-System Frontend - Admin UI (Issue #28, Phase 2 Part 1)
Implementiert Admin-UI für Pipeline-Konfigurationen:
- Pipeline-Config Dialog mit Module-Auswahl
- Stage-Konfiguration (Stage 1/2/3 Prompts)
- Admin-UI: Zwei Tabs (Prompts + Pipeline-Configs)
- CRUD-Operationen für Pipeline-Configs
- API-Integration: Pipeline-Config Endpoints
**Frontend:**
- components/PipelineConfigModal.jsx (neu): Dialog für Pipeline-Konfiguration
- Module-Auswahl mit Zeiträumen (7 Module)
- Stage 1: Multi-Select für parallele Prompts
- Stage 2: Synthese-Prompt Auswahl
- Stage 3: Optional (Goals)
- Validierung (mind. 1 Modul, mind. 1 Stage-1-Prompt, Stage-2 erforderlich)
- pages/AdminPromptsPage.jsx (erweitert): Tab-Navigation
- Tab 1: Prompts (bestehend)
- Tab 2: Pipeline-Konfigurationen (neu)
- Liste aller Configs mit Status (Aktiv, Standard)
- Aktionen: Bearbeiten, Löschen, Als Standard setzen
- Icons: Star, Edit, Trash2
- utils/api.js (erweitert):
- listPipelineConfigs, createPipelineConfig, updatePipelineConfig
- deletePipelineConfig, setDefaultPipelineConfig
- executePipeline, resetPromptToDefault
**Nächste Schritte:**
- Pipeline-Auswahl in AnalysisPage (User-Seite)
- Mobile-Responsive Design
Issue #28 Progress: Frontend 2/3 (67%) | Design 0/3 | Testing 0/1
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-25 10:01:49 +01:00
|
|
|
resetPromptToDefault: (id) => req(`/prompts/${id}/reset-to-default`, json({})),
|
|
|
|
|
|
|
|
|
|
// Pipeline Configs Management (Issue #28 Phase 2)
|
|
|
|
|
listPipelineConfigs: () => req('/prompts/pipeline-configs'),
|
|
|
|
|
createPipelineConfig: (d) => req('/prompts/pipeline-configs', json(d)),
|
|
|
|
|
updatePipelineConfig: (id,d) => req(`/prompts/pipeline-configs/${id}`, jput(d)),
|
|
|
|
|
deletePipelineConfig: (id) => req(`/prompts/pipeline-configs/${id}`, {method:'DELETE'}),
|
|
|
|
|
setDefaultPipelineConfig: (id) => req(`/prompts/pipeline-configs/${id}/set-default`, json({})),
|
|
|
|
|
|
|
|
|
|
// Pipeline Execution (Issue #28 Phase 2)
|
|
|
|
|
executePipeline: (configId=null) => req('/insights/pipeline' + (configId ? `?config_id=${configId}` : ''), json({})),
|
2026-03-25 14:52:24 +01:00
|
|
|
|
|
|
|
|
// Unified Prompt System (Issue #28 Phase 2)
|
2026-03-26 09:38:58 +01:00
|
|
|
executeUnifiedPrompt: (slug, modules=null, timeframes=null, debug=false, save=false) => {
|
2026-03-25 14:52:24 +01:00
|
|
|
const params = new URLSearchParams({ prompt_slug: slug })
|
2026-03-26 08:01:33 +01:00
|
|
|
if (debug) params.append('debug', 'true')
|
2026-03-26 09:38:58 +01:00
|
|
|
if (save) params.append('save', 'true')
|
2026-03-25 14:52:24 +01:00
|
|
|
const body = {}
|
|
|
|
|
if (modules) body.modules = modules
|
|
|
|
|
if (timeframes) body.timeframes = timeframes
|
|
|
|
|
return req('/prompts/execute?' + params, json(body))
|
|
|
|
|
},
|
|
|
|
|
createUnifiedPrompt: (d) => req('/prompts/unified', json(d)),
|
|
|
|
|
updateUnifiedPrompt: (id,d) => req(`/prompts/unified/${id}`, jput(d)),
|
2026-03-26 09:44:08 +01:00
|
|
|
|
|
|
|
|
// Batch Import/Export
|
|
|
|
|
exportAllPrompts: () => req('/prompts/export-all'),
|
|
|
|
|
importPrompts: (data, overwrite=false) => {
|
|
|
|
|
const params = new URLSearchParams()
|
|
|
|
|
if (overwrite) params.append('overwrite', 'true')
|
|
|
|
|
return req(`/prompts/import?${params}`, json(data))
|
|
|
|
|
},
|
2026-03-26 10:05:11 +01:00
|
|
|
|
|
|
|
|
// Placeholder Export
|
|
|
|
|
exportPlaceholderValues: () => req('/prompts/placeholders/export-values'),
|
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
|
|
|
|
|
|
|
|
// v9e: Goals System (Strategic + Tactical)
|
|
|
|
|
getGoalMode: () => req('/goals/mode'),
|
|
|
|
|
updateGoalMode: (mode) => req('/goals/mode', jput({goal_mode: mode})),
|
2026-03-27 08:38:03 +01:00
|
|
|
|
|
|
|
|
// Focus Areas (v2.0)
|
|
|
|
|
getFocusAreas: () => req('/goals/focus-areas'),
|
|
|
|
|
updateFocusAreas: (d) => req('/goals/focus-areas', jput(d)),
|
|
|
|
|
|
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
|
|
|
listGoals: () => req('/goals/list'),
|
2026-03-27 12:30:59 +01:00
|
|
|
listGoalsGrouped: () => req('/goals/grouped'),
|
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
|
|
|
createGoal: (d) => req('/goals/create', json(d)),
|
|
|
|
|
updateGoal: (id,d) => req(`/goals/${id}`, jput(d)),
|
|
|
|
|
deleteGoal: (id) => req(`/goals/${id}`, {method:'DELETE'}),
|
|
|
|
|
|
2026-03-27 13:58:14 +01:00
|
|
|
// Goal Progress (v2.1)
|
|
|
|
|
listGoalProgress: (id) => req(`/goals/${id}/progress`),
|
|
|
|
|
createGoalProgress: (id,d) => req(`/goals/${id}/progress`, json(d)),
|
|
|
|
|
deleteGoalProgress: (gid,pid) => req(`/goals/${gid}/progress/${pid}`, {method:'DELETE'}),
|
|
|
|
|
|
feat: Phase 1.5 - Flexible Goal System (DB-Registry) Part 1/2
KRITISCHE ARCHITEKTUR-ÄNDERUNG vor Phase 0b:
Ermöglicht dynamische Goal Types ohne Code-Änderungen.
Backend:
- Migration 024: goal_type_definitions Tabelle
→ 8 existierende Typen als Seed-Data migriert
→ Flexible Schema: source_table, aggregation_method, calculation_formula
→ System vs. Custom Types (is_system flag)
- goal_utils.py: Universal Value Fetcher
→ get_current_value_for_goal() ersetzt hardcoded if/elif chain
→ Unterstützt: latest, avg_7d, avg_30d, sum_30d, count_7d, etc.
→ Komplexe Formeln (lean_mass) via calculation_formula JSON
- goals.py: CRUD API für Goal Type Definitions
→ GET /goals/goal-types (public)
→ POST/PUT/DELETE /goals/goal-types (admin-only)
→ Schutz für System-Types (nicht löschbar)
- goals.py: _get_current_value_for_goal_type() delegiert zu Universal Fetcher
Frontend:
- api.js: 4 neue Funktionen (listGoalTypeDefinitions, create, update, delete)
Dokumentation:
- TODO_GOAL_SYSTEM.md: Phase 1.5 hinzugefügt, Roadmap aktualisiert
Part 2/2 (nächster Commit):
- Frontend: Dynamic Goal Types Dropdown
- Admin UI: Goal Type Management Page
- Testing
Warum JETZT (vor Phase 0b)?
- Phase 0b Platzhalter (120+) nutzen Goals für Score-Berechnungen
- Flexible Goals → automatisch in Platzhaltern verfügbar
- Später umbauen = Doppelarbeit (alle Platzhalter anpassen)
Zukünftige Custom Goals möglich:
- 🧘 Meditation (min/Tag)
- 📅 Trainingshäufigkeit (x/Woche)
- 📊 Planabweichung (%)
- 🎯 Ritual-Adherence (%)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 06:45:05 +01:00
|
|
|
// Goal Type Definitions (Phase 1.5)
|
|
|
|
|
listGoalTypeDefinitions: () => req('/goals/goal-types'),
|
|
|
|
|
createGoalType: (d) => req('/goals/goal-types', json(d)),
|
|
|
|
|
updateGoalType: (id,d) => req(`/goals/goal-types/${id}`, jput(d)),
|
|
|
|
|
deleteGoalType: (id) => req(`/goals/goal-types/${id}`, {method:'DELETE'}),
|
2026-03-27 08:05:45 +01:00
|
|
|
getSchemaInfo: () => req('/goals/schema-info'),
|
feat: Phase 1.5 - Flexible Goal System (DB-Registry) Part 1/2
KRITISCHE ARCHITEKTUR-ÄNDERUNG vor Phase 0b:
Ermöglicht dynamische Goal Types ohne Code-Änderungen.
Backend:
- Migration 024: goal_type_definitions Tabelle
→ 8 existierende Typen als Seed-Data migriert
→ Flexible Schema: source_table, aggregation_method, calculation_formula
→ System vs. Custom Types (is_system flag)
- goal_utils.py: Universal Value Fetcher
→ get_current_value_for_goal() ersetzt hardcoded if/elif chain
→ Unterstützt: latest, avg_7d, avg_30d, sum_30d, count_7d, etc.
→ Komplexe Formeln (lean_mass) via calculation_formula JSON
- goals.py: CRUD API für Goal Type Definitions
→ GET /goals/goal-types (public)
→ POST/PUT/DELETE /goals/goal-types (admin-only)
→ Schutz für System-Types (nicht löschbar)
- goals.py: _get_current_value_for_goal_type() delegiert zu Universal Fetcher
Frontend:
- api.js: 4 neue Funktionen (listGoalTypeDefinitions, create, update, delete)
Dokumentation:
- TODO_GOAL_SYSTEM.md: Phase 1.5 hinzugefügt, Roadmap aktualisiert
Part 2/2 (nächster Commit):
- Frontend: Dynamic Goal Types Dropdown
- Admin UI: Goal Type Management Page
- Testing
Warum JETZT (vor Phase 0b)?
- Phase 0b Platzhalter (120+) nutzen Goals für Score-Berechnungen
- Flexible Goals → automatisch in Platzhaltern verfügbar
- Später umbauen = Doppelarbeit (alle Platzhalter anpassen)
Zukünftige Custom Goals möglich:
- 🧘 Meditation (min/Tag)
- 📅 Trainingshäufigkeit (x/Woche)
- 📊 Planabweichung (%)
- 🎯 Ritual-Adherence (%)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 06:45:05 +01:00
|
|
|
|
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
|
|
|
// Training Phases
|
|
|
|
|
listTrainingPhases: () => req('/goals/phases'),
|
|
|
|
|
createTrainingPhase: (d) => req('/goals/phases', json(d)),
|
|
|
|
|
updatePhaseStatus: (id,status) => req(`/goals/phases/${id}/status?status=${status}`, jput({})),
|
|
|
|
|
|
|
|
|
|
// Fitness Tests
|
|
|
|
|
listFitnessTests: () => req('/goals/tests'),
|
|
|
|
|
createFitnessTest: (d) => req('/goals/tests', json(d)),
|
2026-03-27 19:51:18 +01:00
|
|
|
|
|
|
|
|
// Focus Areas (v2.0)
|
|
|
|
|
listFocusAreaDefinitions: (includeInactive=false) => req(`/focus-areas/definitions?include_inactive=${includeInactive}`),
|
|
|
|
|
createFocusAreaDefinition: (d) => req('/focus-areas/definitions', json(d)),
|
|
|
|
|
updateFocusAreaDefinition: (id,d) => req(`/focus-areas/definitions/${id}`, jput(d)),
|
|
|
|
|
deleteFocusAreaDefinition: (id) => req(`/focus-areas/definitions/${id}`, {method:'DELETE'}),
|
|
|
|
|
getUserFocusPreferences: () => req('/focus-areas/user-preferences'),
|
|
|
|
|
updateUserFocusPreferences: (d) => req('/focus-areas/user-preferences', jput(d)),
|
|
|
|
|
getFocusAreaStats: () => req('/focus-areas/stats'),
|
2026-04-06 07:28:19 +02:00
|
|
|
listFocusAreaUsageTypes: () => req('/focus-areas/usage-types'),
|
|
|
|
|
setFocusAreaUsageTypes: (id, usageTypeKeys) =>
|
|
|
|
|
req(`/focus-areas/definitions/${id}/usage-types`, jput({ usage_type_keys: usageTypeKeys })),
|
2026-03-29 07:02:54 +02:00
|
|
|
|
|
|
|
|
// Chart Endpoints (Phase 0c - Phase 1: Nutrition + Recovery)
|
|
|
|
|
// Nutrition Charts (E1-E5)
|
2026-03-29 07:28:56 +02:00
|
|
|
getEnergyBalanceChart: (days=28) => req(`/charts/energy-balance?days=${days}`),
|
|
|
|
|
getProteinAdequacyChart: (days=28) => req(`/charts/protein-adequacy?days=${days}`),
|
|
|
|
|
getNutritionConsistencyChart: (days=28) => req(`/charts/nutrition-consistency?days=${days}`),
|
|
|
|
|
getWeeklyMacroDistributionChart: (weeks=12) => req(`/charts/weekly-macro-distribution?weeks=${weeks}`),
|
|
|
|
|
getNutritionAdherenceScore: (days=28) => req(`/charts/nutrition-adherence-score?days=${days}`),
|
|
|
|
|
getEnergyAvailabilityWarning: (days=14) => req(`/charts/energy-availability-warning?days=${days}`),
|
2026-03-29 07:02:54 +02:00
|
|
|
|
|
|
|
|
// Recovery Charts (R1-R5)
|
|
|
|
|
getRecoveryScoreChart: (days=28) => req(`/charts/recovery-score?days=${days}`),
|
|
|
|
|
getHrvRhrBaselineChart: (days=28) => req(`/charts/hrv-rhr-baseline?days=${days}`),
|
|
|
|
|
getSleepDurationQualityChart: (days=28) => req(`/charts/sleep-duration-quality?days=${days}`),
|
|
|
|
|
getSleepDebtChart: (days=28) => req(`/charts/sleep-debt?days=${days}`),
|
|
|
|
|
getVitalSignsMatrixChart: (days=7) => req(`/charts/vital-signs-matrix?days=${days}`),
|
2026-03-29 20:37:52 +02:00
|
|
|
|
|
|
|
|
// Placeholder Metadata Export (v1.0)
|
|
|
|
|
exportPlaceholdersExtendedJson: () => req('/prompts/placeholders/export-values-extended'),
|
2026-03-16 13:35:11 +01:00
|
|
|
}
|