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 datetime import date, datetime, timedelta
from decimal import Decimal
import traceback
from db import get_db, get_cursor, r2d
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.
"""
with get_db() as conn:
cur = get_cursor(conn)
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
""")
try:
with get_db() as conn:
cur = get_cursor(conn)
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")
def create_goal_type_definition(

View File

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