Goalsystem V1 #50

Merged
Lars merged 51 commits from develop into main 2026-03-27 17:40:51 +01:00
2 changed files with 63 additions and 29 deletions
Showing only changes of commit bbee44ecdc - Show all commits

View File

@ -14,6 +14,7 @@ from pydantic import BaseModel
from typing import Optional, List from typing import Optional, List
from datetime import date, datetime, timedelta from datetime import date, datetime, timedelta
from decimal import Decimal from decimal import Decimal
import traceback
from db import get_db, get_cursor, r2d from db import get_db, get_cursor, r2d
from auth import require_auth from auth import require_auth
@ -499,24 +500,51 @@ def list_goal_type_definitions(session: dict = Depends(require_auth)):
Public endpoint - returns all available goal types for dropdown. Public endpoint - returns all available goal types for dropdown.
""" """
with get_db() as conn: try:
cur = get_cursor(conn) with get_db() as conn:
cur.execute(""" cur = get_cursor(conn)
SELECT id, type_key, label_de, label_en, unit, icon, category,
source_table, source_column, aggregation_method,
calculation_formula, description, is_system,
created_at, updated_at
FROM goal_type_definitions
WHERE is_active = true
ORDER BY
CASE
WHEN is_system = true THEN 0
ELSE 1
END,
label_de
""")
return [r2d(row) for row in cur.fetchall()] # Check if table exists first
cur.execute("""
SELECT EXISTS (
SELECT FROM information_schema.tables
WHERE table_name = 'goal_type_definitions'
)
""")
table_exists = cur.fetchone()[0]
if not table_exists:
print("[ERROR] goal_type_definitions table does not exist!")
raise HTTPException(
status_code=500,
detail="Goal Types Tabelle existiert nicht. Migration 024 muss ausgeführt werden."
)
cur.execute("""
SELECT id, type_key, label_de, label_en, unit, icon, category,
source_table, source_column, aggregation_method,
calculation_formula, description, is_system,
created_at, updated_at
FROM goal_type_definitions
WHERE is_active = true
ORDER BY
CASE
WHEN is_system = true THEN 0
ELSE 1
END,
label_de
""")
return [r2d(row) for row in cur.fetchall()]
except HTTPException:
raise
except Exception as e:
print(f"[ERROR] list_goal_type_definitions failed: {e}")
print(traceback.format_exc())
raise HTTPException(
status_code=500,
detail=f"Fehler beim Laden der Goal Types: {str(e)}"
)
@router.post("/goal-types") @router.post("/goal-types")
def create_goal_type_definition( def create_goal_type_definition(

View File

@ -84,21 +84,23 @@ export default function GoalsPage() {
// Convert types array to map for quick lookup // Convert types array to map for quick lookup
const typesMap = {} const typesMap = {}
typesData.forEach(type => { if (typesData && Array.isArray(typesData)) {
typesMap[type.type_key] = { typesData.forEach(type => {
label: type.label_de, typesMap[type.type_key] = {
unit: type.unit, label: type.label_de,
icon: type.icon || '📊', unit: type.unit,
category: type.category, icon: type.icon || '📊',
is_system: type.is_system category: type.category,
} is_system: type.is_system
}) }
})
}
setGoalTypes(typesData) setGoalTypes(typesData || [])
setGoalTypesMap(typesMap) setGoalTypesMap(typesMap)
} catch (err) { } catch (err) {
console.error('Failed to load goals:', err) console.error('Failed to load goals:', err)
setError('Fehler beim Laden der Ziele') setError(`Fehler beim Laden: ${err.message || err.toString()}`)
} finally { } finally {
setLoading(false) setLoading(false)
} }
@ -121,8 +123,12 @@ export default function GoalsPage() {
} }
const handleCreateGoal = () => { const handleCreateGoal = () => {
if (goalTypes.length === 0) {
setError('Keine Goal Types verfügbar. Bitte Admin kontaktieren.')
return
}
setEditingGoal(null) setEditingGoal(null)
const firstType = goalTypes.length > 0 ? goalTypes[0].type_key : 'weight' const firstType = goalTypes[0].type_key
setFormData({ setFormData({
goal_type: firstType, goal_type: firstType,
is_primary: goals.length === 0, // First goal is primary by default is_primary: goals.length === 0, // First goal is primary by default