Compare commits

..

No commits in common. "426efd4b83017b26602e8b4cbc5db666712fc455" and "a9a4c78a0e013702b8d69835ec2851366eaeb998" have entirely different histories.

5 changed files with 126 additions and 128 deletions

View File

@ -142,8 +142,8 @@ def list_training_styles(
query = """ query = """
SELECT ts.*, ps.name as parent_style_name SELECT ts.*, ps.name as parent_style_name
FROM style_directions ts FROM training_styles ts
LEFT JOIN style_directions ps ON ts.parent_style_id = ps.id LEFT JOIN training_styles ps ON ts.parent_style_id = ps.id
""" """
params = [] params = []
@ -173,7 +173,7 @@ def create_training_style(data: dict, session=Depends(require_auth)):
cur = get_cursor(conn) cur = get_cursor(conn)
cur.execute(""" cur.execute("""
INSERT INTO style_directions (name, abbreviation, description, parent_style_id, sort_order, status) INSERT INTO training_styles (name, abbreviation, description, parent_style_id, sort_order, status)
VALUES (%s, %s, %s, %s, %s, %s) VALUES (%s, %s, %s, %s, %s, %s)
RETURNING id RETURNING id
""", ( """, (
@ -190,8 +190,8 @@ def create_training_style(data: dict, session=Depends(require_auth)):
cur.execute(""" cur.execute("""
SELECT ts.*, ps.name as parent_style_name SELECT ts.*, ps.name as parent_style_name
FROM style_directions ts FROM training_styles ts
LEFT JOIN style_directions ps ON ts.parent_style_id = ps.id LEFT JOIN training_styles ps ON ts.parent_style_id = ps.id
WHERE ts.id = %s WHERE ts.id = %s
""", (style_id,)) """, (style_id,))
return r2d(cur.fetchone()) return r2d(cur.fetchone())
@ -208,7 +208,7 @@ def update_training_style(style_id: int, data: dict, session=Depends(require_aut
cur = get_cursor(conn) cur = get_cursor(conn)
cur.execute(""" cur.execute("""
UPDATE style_directions SET UPDATE training_styles SET
name = %s, name = %s,
abbreviation = %s, abbreviation = %s,
description = %s, description = %s,
@ -231,8 +231,8 @@ def update_training_style(style_id: int, data: dict, session=Depends(require_aut
cur.execute(""" cur.execute("""
SELECT ts.*, ps.name as parent_style_name SELECT ts.*, ps.name as parent_style_name
FROM style_directions ts FROM training_styles ts
LEFT JOIN style_directions ps ON ts.parent_style_id = ps.id LEFT JOIN training_styles ps ON ts.parent_style_id = ps.id
WHERE ts.id = %s WHERE ts.id = %s
""", (style_id,)) """, (style_id,))
return r2d(cur.fetchone()) return r2d(cur.fetchone())
@ -247,7 +247,7 @@ def delete_training_style(style_id: int, session=Depends(require_auth)):
with get_db() as conn: with get_db() as conn:
cur = get_cursor(conn) cur = get_cursor(conn)
cur.execute("DELETE FROM style_directions WHERE id = %s", (style_id,)) cur.execute("DELETE FROM training_styles WHERE id = %s", (style_id,))
conn.commit() conn.commit()
return {"ok": True} return {"ok": True}
@ -830,7 +830,7 @@ def delete_target_group(target_group_id: int, session=Depends(require_auth)):
# Check if assigned to training styles (M:N) # Check if assigned to training styles (M:N)
cur.execute(""" cur.execute("""
SELECT COUNT(*) as count SELECT COUNT(*) as count
FROM style_direction_target_groups FROM training_style_target_groups
WHERE target_group_id = %s WHERE target_group_id = %s
""", (target_group_id,)) """, (target_group_id,))
style_count = cur.fetchone()['count'] style_count = cur.fetchone()['count']
@ -854,14 +854,14 @@ def delete_target_group(target_group_id: int, session=Depends(require_auth)):
@router.get("/training-style-target-groups") @router.get("/training-style-target-groups")
def list_training_style_target_groups( def list_training_style_target_groups(
style_direction_id: Optional[int] = Query(default=None), training_style_id: Optional[int] = Query(default=None),
target_group_id: Optional[int] = Query(default=None), target_group_id: Optional[int] = Query(default=None),
is_primary: Optional[bool] = Query(default=None), is_primary: Optional[bool] = Query(default=None),
session=Depends(require_auth) session=Depends(require_auth)
): ):
"""List M:N assignments between style directions and target groups. """List M:N assignments between training styles and target groups.
Returns enriched data with style_direction_name, target_group_name, Returns enriched data with training_style_name, target_group_name,
focus_area_name for easy display in Matrix UI. focus_area_name for easy display in Matrix UI.
""" """
with get_db() as conn: with get_db() as conn:
@ -869,41 +869,41 @@ def list_training_style_target_groups(
query = """ query = """
SELECT SELECT
sdtg.id, tstg.id,
sdtg.style_direction_id, tstg.training_style_id,
sdtg.target_group_id, tstg.target_group_id,
sdtg.is_primary, tstg.is_primary,
sdtg.created_at, tstg.created_at,
sd.name as style_direction_name, ts.name as training_style_name,
sd.focus_area_id, ts.focus_area_id,
fa.name as focus_area_name, fa.name as focus_area_name,
tg.name as target_group_name, tg.name as target_group_name,
tg.min_age, tg.min_age,
tg.max_age tg.max_age
FROM style_direction_target_groups sdtg FROM training_style_target_groups tstg
LEFT JOIN style_directions sd ON sdtg.style_direction_id = sd.id LEFT JOIN training_styles ts ON tstg.training_style_id = ts.id
LEFT JOIN focus_areas fa ON sd.focus_area_id = fa.id LEFT JOIN focus_areas fa ON ts.focus_area_id = fa.id
LEFT JOIN target_groups tg ON sdtg.target_group_id = tg.id LEFT JOIN target_groups tg ON tstg.target_group_id = tg.id
""" """
params = [] params = []
where = [] where = []
if style_direction_id is not None: if training_style_id is not None:
where.append("sdtg.style_direction_id = %s") where.append("tstg.training_style_id = %s")
params.append(style_direction_id) params.append(training_style_id)
if target_group_id is not None: if target_group_id is not None:
where.append("sdtg.target_group_id = %s") where.append("tstg.target_group_id = %s")
params.append(target_group_id) params.append(target_group_id)
if is_primary is not None: if is_primary is not None:
where.append("sdtg.is_primary = %s") where.append("tstg.is_primary = %s")
params.append(is_primary) params.append(is_primary)
if where: if where:
query += " WHERE " + " AND ".join(where) query += " WHERE " + " AND ".join(where)
query += " ORDER BY fa.sort_order, sd.sort_order, tg.sort_order" query += " ORDER BY fa.sort_order, ts.sort_order, tg.sort_order"
cur.execute(query, params) cur.execute(query, params)
rows = cur.fetchall() rows = cur.fetchall()
@ -912,7 +912,7 @@ def list_training_style_target_groups(
@router.post("/training-style-target-groups") @router.post("/training-style-target-groups")
def create_training_style_target_group(data: dict, session=Depends(require_auth)): def create_training_style_target_group(data: dict, session=Depends(require_auth)):
"""Assign target group to style direction (admin only). """Assign target group to training style (admin only).
Uses UPSERT logic - if assignment exists, updates is_primary flag. Uses UPSERT logic - if assignment exists, updates is_primary flag.
""" """
@ -920,25 +920,25 @@ def create_training_style_target_group(data: dict, session=Depends(require_auth)
if role not in ['admin', 'superadmin']: if role not in ['admin', 'superadmin']:
raise HTTPException(403, "Nur Admins dürfen Zuordnungen erstellen") raise HTTPException(403, "Nur Admins dürfen Zuordnungen erstellen")
style_direction_id = data.get('style_direction_id') training_style_id = data.get('training_style_id')
target_group_id = data.get('target_group_id') target_group_id = data.get('target_group_id')
if not style_direction_id or not target_group_id: if not training_style_id or not target_group_id:
raise HTTPException(400, "style_direction_id und target_group_id sind Pflichtfelder") raise HTTPException(400, "training_style_id und target_group_id sind Pflichtfelder")
with get_db() as conn: with get_db() as conn:
cur = get_cursor(conn) cur = get_cursor(conn)
# Upsert logic # Upsert logic
cur.execute(""" cur.execute("""
INSERT INTO style_direction_target_groups INSERT INTO training_style_target_groups
(style_direction_id, target_group_id, is_primary) (training_style_id, target_group_id, is_primary)
VALUES (%s, %s, %s) VALUES (%s, %s, %s)
ON CONFLICT (style_direction_id, target_group_id) ON CONFLICT (training_style_id, target_group_id)
DO UPDATE SET is_primary = EXCLUDED.is_primary DO UPDATE SET is_primary = EXCLUDED.is_primary
RETURNING id RETURNING id
""", ( """, (
style_direction_id, training_style_id,
target_group_id, target_group_id,
data.get('is_primary', False) data.get('is_primary', False)
)) ))
@ -949,20 +949,20 @@ def create_training_style_target_group(data: dict, session=Depends(require_auth)
# Return enriched record # Return enriched record
cur.execute(""" cur.execute("""
SELECT SELECT
sdtg.id, tstg.id,
sdtg.style_direction_id, tstg.training_style_id,
sdtg.target_group_id, tstg.target_group_id,
sdtg.is_primary, tstg.is_primary,
sdtg.created_at, tstg.created_at,
sd.name as style_direction_name, ts.name as training_style_name,
sd.focus_area_id, ts.focus_area_id,
fa.name as focus_area_name, fa.name as focus_area_name,
tg.name as target_group_name tg.name as target_group_name
FROM style_direction_target_groups sdtg FROM training_style_target_groups tstg
LEFT JOIN style_directions sd ON sdtg.style_direction_id = sd.id LEFT JOIN training_styles ts ON tstg.training_style_id = ts.id
LEFT JOIN focus_areas fa ON sd.focus_area_id = fa.id LEFT JOIN focus_areas fa ON ts.focus_area_id = fa.id
LEFT JOIN target_groups tg ON sdtg.target_group_id = tg.id LEFT JOIN target_groups tg ON tstg.target_group_id = tg.id
WHERE sdtg.id = %s WHERE tstg.id = %s
""", (assignment_id,)) """, (assignment_id,))
return r2d(cur.fetchone()) return r2d(cur.fetchone())
@ -986,7 +986,7 @@ def update_training_style_target_group(
cur = get_cursor(conn) cur = get_cursor(conn)
cur.execute(""" cur.execute("""
UPDATE style_direction_target_groups UPDATE training_style_target_groups
SET is_primary = %s SET is_primary = %s
WHERE id = %s WHERE id = %s
""", ( """, (
@ -999,20 +999,20 @@ def update_training_style_target_group(
# Return enriched record # Return enriched record
cur.execute(""" cur.execute("""
SELECT SELECT
sdtg.id, tstg.id,
sdtg.style_direction_id, tstg.training_style_id,
sdtg.target_group_id, tstg.target_group_id,
sdtg.is_primary, tstg.is_primary,
sdtg.created_at, tstg.created_at,
sd.name as style_direction_name, ts.name as training_style_name,
sd.focus_area_id, ts.focus_area_id,
fa.name as focus_area_name, fa.name as focus_area_name,
tg.name as target_group_name tg.name as target_group_name
FROM style_direction_target_groups sdtg FROM training_style_target_groups tstg
LEFT JOIN style_directions sd ON sdtg.style_direction_id = sd.id LEFT JOIN training_styles ts ON tstg.training_style_id = ts.id
LEFT JOIN focus_areas fa ON sd.focus_area_id = fa.id LEFT JOIN focus_areas fa ON ts.focus_area_id = fa.id
LEFT JOIN target_groups tg ON sdtg.target_group_id = tg.id LEFT JOIN target_groups tg ON tstg.target_group_id = tg.id
WHERE sdtg.id = %s WHERE tstg.id = %s
""", (assignment_id,)) """, (assignment_id,))
return r2d(cur.fetchone()) return r2d(cur.fetchone())
@ -1027,7 +1027,7 @@ def delete_training_style_target_group(assignment_id: int, session=Depends(requi
with get_db() as conn: with get_db() as conn:
cur = get_cursor(conn) cur = get_cursor(conn)
cur.execute("DELETE FROM style_direction_target_groups WHERE id = %s", (assignment_id,)) cur.execute("DELETE FROM training_style_target_groups WHERE id = %s", (assignment_id,))
conn.commit() conn.commit()
return {"ok": True} return {"ok": True}
@ -1038,7 +1038,7 @@ def get_training_styles_hierarchy(
status: Optional[str] = Query(default='active'), status: Optional[str] = Query(default='active'),
session=Depends(require_auth) session=Depends(require_auth)
): ):
"""Get hierarchical structure: Focus Areas → Style Directions → Target Groups. """Get hierarchical structure: Focus Areas → Training Styles → Target Groups.
Returns nested structure for Tree-View rendering in Admin UI. Returns nested structure for Tree-View rendering in Admin UI.
""" """
@ -1058,25 +1058,25 @@ def get_training_styles_hierarchy(
cur.execute(fa_query, fa_params) cur.execute(fa_query, fa_params)
focus_areas = [r2d(r) for r in cur.fetchall()] focus_areas = [r2d(r) for r in cur.fetchall()]
# For each focus area, get style directions with their target groups # For each focus area, get training styles with their target groups
for fa in focus_areas: for fa in focus_areas:
sd_query = """ ts_query = """
SELECT * FROM style_directions SELECT * FROM training_styles
WHERE focus_area_id = %s WHERE focus_area_id = %s
""" """
sd_params = [fa['id']] ts_params = [fa['id']]
if status: if status:
sd_query += " AND status = %s" ts_query += " AND status = %s"
sd_params.append(status) ts_params.append(status)
sd_query += " ORDER BY sort_order, name" ts_query += " ORDER BY sort_order, name"
cur.execute(sd_query, sd_params) cur.execute(ts_query, ts_params)
style_directions = [r2d(r) for r in cur.fetchall()] training_styles = [r2d(r) for r in cur.fetchall()]
# For each style direction, get assigned target groups # For each training style, get assigned target groups
for sd in style_directions: for ts in training_styles:
cur.execute(""" cur.execute("""
SELECT SELECT
tg.id, tg.id,
@ -1084,16 +1084,16 @@ def get_training_styles_hierarchy(
tg.description, tg.description,
tg.min_age, tg.min_age,
tg.max_age, tg.max_age,
sdtg.is_primary, tstg.is_primary,
sdtg.id as assignment_id tstg.id as assignment_id
FROM style_direction_target_groups sdtg FROM training_style_target_groups tstg
LEFT JOIN target_groups tg ON sdtg.target_group_id = tg.id LEFT JOIN target_groups tg ON tstg.target_group_id = tg.id
WHERE sdtg.style_direction_id = %s WHERE tstg.training_style_id = %s
ORDER BY tg.sort_order, tg.name ORDER BY tg.sort_order, tg.name
""", (sd['id'],)) """, (ts['id'],))
sd['target_groups'] = [r2d(r) for r in cur.fetchall()] ts['target_groups'] = [r2d(r) for r in cur.fetchall()]
fa['style_directions'] = style_directions fa['training_styles'] = training_styles
return focus_areas return focus_areas

View File

@ -18,7 +18,7 @@ MODULE_VERSIONS = {
"import_wiki": "0.1.0", "import_wiki": "0.1.0",
"admin": "1.0.0", "admin": "1.0.0",
"membership": "1.0.0", "membership": "1.0.0",
"catalogs": "1.4.0", # Updated: Backend SQL Queries für renamed tables (Migration 010+011) "catalogs": "1.3.0", # Updated: M:N Zielgruppen-Zuordnung (Migration 009)
} }
CHANGELOG = [ CHANGELOG = [
@ -32,9 +32,7 @@ CHANGELOG = [
"DB: Neue Tabelle training_types mit Seed-Daten (Breitensport, Leistungssport, Wettkampf)", "DB: Neue Tabelle training_types mit Seed-Daten (Breitensport, Leistungssport, Wettkampf)",
"DB: Neue Junction-Tabelle exercise_training_types (M:N)", "DB: Neue Junction-Tabelle exercise_training_types (M:N)",
"Architektur: Fokusbereich → Stilrichtung → Trainingsstil → Zielgruppe (alle M:N)", "Architektur: Fokusbereich → Stilrichtung → Trainingsstil → Zielgruppe (alle M:N)",
"Backend: Alle SQL Queries aktualisiert auf neue Tabellennamen (style_directions, style_direction_target_groups)", "Phase 1: Nur Datenbank-Migrationen - Backend/Frontend-Updates folgen",
"Backend: API Parameter umbenannt (training_style_id → style_direction_id)",
"Backend: CRUD Endpoints für training_types hinzugefügt",
] ]
}, },
{ {

View File

@ -61,7 +61,7 @@ export default function AdminCatalogsPage() {
const data = await api.listFocusAreas() const data = await api.listFocusAreas()
setFocusAreas(data) setFocusAreas(data)
} else if (activeTab === 'training-styles') { } else if (activeTab === 'training-styles') {
const data = await api.listStyleDirections() const data = await api.listTrainingStyles()
setTrainingStyles(data) setTrainingStyles(data)
} else if (activeTab === 'training-characters') { } else if (activeTab === 'training-characters') {
const data = await api.listTrainingCharacters() const data = await api.listTrainingCharacters()
@ -85,13 +85,13 @@ export default function AdminCatalogsPage() {
setProfiles(profs) setProfiles(profs)
setFocusAreas(areas) setFocusAreas(areas)
} else if (activeTab === 'hierarchy') { } else if (activeTab === 'hierarchy') {
const data = await api.getStyleDirectionsHierarchy() const data = await api.getTrainingStylesHierarchy()
setHierarchyData(data) setHierarchyData(data)
} else if (activeTab === 'target-groups-matrix') { } else if (activeTab === 'target-groups-matrix') {
const [styles, groups, assigns] = await Promise.all([ const [styles, groups, assigns] = await Promise.all([
api.listStyleDirections(), api.listTrainingStyles(),
api.listTargetGroups(), api.listTargetGroups(),
api.listStyleDirectionTargetGroups() api.listTrainingStyleTargetGroups()
]) ])
setTrainingStyles(styles) setTrainingStyles(styles)
setTargetGroups(groups) setTargetGroups(groups)
@ -135,10 +135,10 @@ export default function AdminCatalogsPage() {
} }
} }
// Style Directions (formerly Training Styles) // Training Styles
async function createStyleDirection() { async function createTrainingStyle() {
try { try {
await api.createStyleDirection(newTS) await api.createTrainingStyle(newTS)
setNewTS({ name: '', description: '', parent_style_id: null }) setNewTS({ name: '', description: '', parent_style_id: null })
loadData() loadData()
} catch (e) { } catch (e) {
@ -146,9 +146,9 @@ export default function AdminCatalogsPage() {
} }
} }
async function updateStyleDirection(id, data) { async function updateTrainingStyle(id, data) {
try { try {
await api.updateStyleDirection(id, data) await api.updateTrainingStyle(id, data)
setEditingTS(null) setEditingTS(null)
loadData() loadData()
} catch (e) { } catch (e) {
@ -156,10 +156,10 @@ export default function AdminCatalogsPage() {
} }
} }
async function deleteStyleDirection(id) { async function deleteTrainingStyle(id) {
if (!confirm('Stilrichtung wirklich löschen?')) return if (!confirm('Trainingsstil wirklich löschen?')) return
try { try {
await api.deleteStyleDirection(id) await api.deleteTrainingStyle(id)
loadData() loadData()
} catch (e) { } catch (e) {
setError(e.message) setError(e.message)
@ -1046,14 +1046,14 @@ export default function AdminCatalogsPage() {
<div> <div>
<h3 style={{ margin: 0 }}>{fa.name}</h3> <h3 style={{ margin: 0 }}>{fa.name}</h3>
<p style={{ margin: '4px 0 0 0', fontSize: '14px', color: 'var(--text2)' }}> <p style={{ margin: '4px 0 0 0', fontSize: '14px', color: 'var(--text2)' }}>
{fa.style_directions?.length || 0} Trainingsstil(e) {fa.training_styles?.length || 0} Trainingsstil(e)
</p> </p>
</div> </div>
</div> </div>
{expandedNodes.has(`fa-${fa.id}`) && fa.style_directions && fa.style_directions.length > 0 && ( {expandedNodes.has(`fa-${fa.id}`) && fa.training_styles && fa.training_styles.length > 0 && (
<div style={{ marginLeft: '40px', marginTop: '12px', borderLeft: '2px solid var(--border)', paddingLeft: '16px' }}> <div style={{ marginLeft: '40px', marginTop: '12px', borderLeft: '2px solid var(--border)', paddingLeft: '16px' }}>
{fa.style_directions.map(ts => ( {fa.training_styles.map(ts => (
<div key={ts.id} style={{ marginBottom: '12px' }}> <div key={ts.id} style={{ marginBottom: '12px' }}>
<div <div
style={{ style={{
@ -1175,7 +1175,7 @@ export default function AdminCatalogsPage() {
</td> </td>
{targetGroups.map(tg => { {targetGroups.map(tg => {
const assignment = assignments.find( const assignment = assignments.find(
a => a.style_direction_id === ts.id && a.target_group_id === tg.id a => a.training_style_id === ts.id && a.target_group_id === tg.id
) )
const isAssigned = !!assignment const isAssigned = !!assignment
@ -1187,10 +1187,10 @@ export default function AdminCatalogsPage() {
onChange={async () => { onChange={async () => {
try { try {
if (isAssigned) { if (isAssigned) {
await api.deleteStyleDirectionTargetGroup(assignment.id) await api.deleteTrainingStyleTargetGroup(assignment.id)
} else { } else {
await api.createStyleDirectionTargetGroup({ await api.createTrainingStyleTargetGroup({
style_direction_id: ts.id, training_style_id: ts.id,
target_group_id: tg.id, target_group_id: tg.id,
is_primary: false is_primary: false
}) })

View File

@ -257,27 +257,27 @@ export async function deleteFocusArea(id) {
return request(`/api/focus-areas/${id}`, { method: 'DELETE' }) return request(`/api/focus-areas/${id}`, { method: 'DELETE' })
} }
// Style Directions (formerly Training Styles) // Training Styles
export async function listStyleDirections(filters = {}) { export async function listTrainingStyles(filters = {}) {
const query = new URLSearchParams(filters).toString() const query = new URLSearchParams(filters).toString()
return request(`/api/training-styles${query ? '?' + query : ''}`) return request(`/api/training-styles${query ? '?' + query : ''}`)
} }
export async function createStyleDirection(data) { export async function createTrainingStyle(data) {
return request('/api/training-styles', { return request('/api/training-styles', {
method: 'POST', method: 'POST',
body: JSON.stringify(data) body: JSON.stringify(data)
}) })
} }
export async function updateStyleDirection(id, data) { export async function updateTrainingStyle(id, data) {
return request(`/api/training-styles/${id}`, { return request(`/api/training-styles/${id}`, {
method: 'PUT', method: 'PUT',
body: JSON.stringify(data) body: JSON.stringify(data)
}) })
} }
export async function deleteStyleDirection(id) { export async function deleteTrainingStyle(id) {
return request(`/api/training-styles/${id}`, { method: 'DELETE' }) return request(`/api/training-styles/${id}`, { method: 'DELETE' })
} }
@ -394,31 +394,31 @@ export async function deleteTargetGroup(id) {
return request(`/api/target-groups/${id}`, { method: 'DELETE' }) return request(`/api/target-groups/${id}`, { method: 'DELETE' })
} }
// Style Direction → Target Groups (M:N Assignments) // Training Style → Target Groups (M:N Assignments)
export async function listStyleDirectionTargetGroups(filters = {}) { export async function listTrainingStyleTargetGroups(filters = {}) {
const query = new URLSearchParams(filters).toString() const query = new URLSearchParams(filters).toString()
return request(`/api/training-style-target-groups${query ? '?' + query : ''}`) return request(`/api/training-style-target-groups${query ? '?' + query : ''}`)
} }
export async function createStyleDirectionTargetGroup(data) { export async function createTrainingStyleTargetGroup(data) {
return request('/api/training-style-target-groups', { return request('/api/training-style-target-groups', {
method: 'POST', method: 'POST',
body: JSON.stringify(data) body: JSON.stringify(data)
}) })
} }
export async function updateStyleDirectionTargetGroup(id, data) { export async function updateTrainingStyleTargetGroup(id, data) {
return request(`/api/training-style-target-groups/${id}`, { return request(`/api/training-style-target-groups/${id}`, {
method: 'PUT', method: 'PUT',
body: JSON.stringify(data) body: JSON.stringify(data)
}) })
} }
export async function deleteStyleDirectionTargetGroup(id) { export async function deleteTrainingStyleTargetGroup(id) {
return request(`/api/training-style-target-groups/${id}`, { method: 'DELETE' }) return request(`/api/training-style-target-groups/${id}`, { method: 'DELETE' })
} }
export async function getStyleDirectionsHierarchy(filters = {}) { export async function getTrainingStylesHierarchy(filters = {}) {
const query = new URLSearchParams(filters).toString() const query = new URLSearchParams(filters).toString()
return request(`/api/training-styles/hierarchy${query ? '?' + query : ''}`) return request(`/api/training-styles/hierarchy${query ? '?' + query : ''}`)
} }
@ -528,10 +528,10 @@ export const api = {
createFocusArea, createFocusArea,
updateFocusArea, updateFocusArea,
deleteFocusArea, deleteFocusArea,
listStyleDirections, listTrainingStyles,
createStyleDirection, createTrainingStyle,
updateStyleDirection, updateTrainingStyle,
deleteStyleDirection, deleteTrainingStyle,
listTrainingCharacters, listTrainingCharacters,
createTrainingCharacter, createTrainingCharacter,
updateTrainingCharacter, updateTrainingCharacter,
@ -551,11 +551,11 @@ export const api = {
createTargetGroup, createTargetGroup,
updateTargetGroup, updateTargetGroup,
deleteTargetGroup, deleteTargetGroup,
listStyleDirectionTargetGroups, listTrainingStyleTargetGroups,
createStyleDirectionTargetGroup, createTrainingStyleTargetGroup,
updateStyleDirectionTargetGroup, updateTrainingStyleTargetGroup,
deleteStyleDirectionTargetGroup, deleteTrainingStyleTargetGroup,
getStyleDirectionsHierarchy, getTrainingStylesHierarchy,
// System // System
getVersion, getVersion,

View File

@ -11,5 +11,5 @@ export const PAGE_VERSIONS = {
ClubsPage: "1.0.0", ClubsPage: "1.0.0",
SkillsPage: "1.0.0", SkillsPage: "1.0.0",
TrainingPlanningPage: "1.0.0", TrainingPlanningPage: "1.0.0",
AdminCatalogsPage: "2.2.0", // Updated: Frontend API Calls & Field Names für renamed tables AdminCatalogsPage: "2.1.0", // Updated: Stilrichtungen + Trainingsstil-Dimension
} }