feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
/ * *
* Shinkan Jinkendo API Client
*
* Zentrale API - Kommunikation mit automatischer Token - Injektion
* /
2026-04-27 14:48:46 +02:00
import { stripHtmlToText } from './htmlUtils'
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
const API _URL = import . meta . env . VITE _API _URL || ''
2026-05-05 16:12:17 +02:00
/** LocalStorage + Request-Header für Mandanten-Kontext */
export const ACTIVE _CLUB _STORAGE _KEY = 'shinkan_active_club_id'
function mergeActiveClubHeader ( headers = { } ) {
const cid = localStorage . getItem ( ACTIVE _CLUB _STORAGE _KEY )
if ( cid && /^\d+$/ . test ( String ( cid ) . trim ( ) ) ) {
return { ... headers , 'X-Active-Club-Id' : String ( cid ) . trim ( ) }
}
return { ... headers }
}
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
/ * *
* Generic API request with automatic token injection
* /
async function request ( endpoint , options = { } ) {
const token = localStorage . getItem ( 'authToken' )
2026-04-29 11:37:54 +02:00
const method = ( options . method || 'GET' ) . toUpperCase ( )
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
2026-05-05 16:12:17 +02:00
const headers = mergeActiveClubHeader ( {
2026-04-29 11:37:54 +02:00
... options . headers ,
2026-05-05 16:12:17 +02:00
} )
2026-04-29 11:37:54 +02:00
// GET ohne Body: kein Content-Type: application/json (manche Proxies/Headers stören sich)
if ( method !== 'GET' && method !== 'HEAD' ) {
if ( ! headers [ 'Content-Type' ] && ! headers [ 'content-type' ] ) {
headers [ 'Content-Type' ] = 'application/json'
}
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
}
if ( token ) {
headers [ 'X-Auth-Token' ] = token
}
2026-04-29 06:15:26 +02:00
const url = ` ${ API _URL } ${ endpoint } `
try {
const response = await fetch ( url , {
... options ,
headers ,
} )
if ( ! response . ok ) {
2026-04-29 09:18:22 +02:00
const text = await response . text ( )
let parsed = null
try {
parsed = JSON . parse ( text )
} catch {
parsed = null
}
if ( parsed ? . detail != null ) {
const d = parsed . detail
throw new Error ( typeof d === 'string' ? d : JSON . stringify ( d ) )
}
if ( response . status === 502 ) {
throw new Error (
'HTTP 502 (Bad Gateway): Der Reverse-Proxy hat die API nicht korrekt erreicht. Ist `shinkan-api` aktiv (`docker compose ps`, `docker logs shinkan-api`)? Bei Host-Routing nur einen Weg verwenden — alles auf Port 3003 (Nginx nach `backend:8000`) oder sauber `/api` → Backend-Port.'
)
}
const snippet = ( text || '' ) . replace ( /\s+/g , ' ' ) . trim ( ) . slice ( 0 , 180 )
throw new Error ( snippet ? ` HTTP ${ response . status } : ${ snippet } ` : ` HTTP ${ response . status } ` )
2026-04-29 06:15:26 +02:00
}
return response . json ( )
} catch ( e ) {
if ( e instanceof TypeError && ( e . message === 'Failed to fetch' || e . message . includes ( 'fetch' ) ) ) {
const hint =
API _URL && API _URL . length > 0
? ` Verbindung zum API unter ${ API _URL } fehlgeschlagen. Läuft das Backend (z. B. Port 8098) und ist CORS erlaubt? `
: 'Kein VITE_API_URL gesetzt: Anfragen gehen an die Frontend-URL und schlagen oft fehl. Setze in .env z. B. VITE_API_URL=http://localhost:8098 und starte Vite neu.'
2026-04-29 06:33:23 +02:00
// Ursache oft: CORS (v. a. bei Fehler-Antworten ohne CORS-Header), Adblocker, falsche URL —
// Login kann trotzdem klappen wenn nur eine andere Route betroffen ist.
throw new Error ( ` ${ hint } [Technisch: ${ e . message } ; URL war ${ endpoint } ] ` )
2026-04-29 06:15:26 +02:00
}
throw e
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
}
}
// ============================================================================
// Auth
// ============================================================================
export async function login ( email , password ) {
return request ( '/api/auth/login' , {
method : 'POST' ,
body : JSON . stringify ( { email , password } )
} )
}
2026-05-05 16:40:49 +02:00
export async function register ( email , password , name , extra = { } ) {
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
return request ( '/api/auth/register' , {
method : 'POST' ,
2026-05-05 16:40:49 +02:00
body : JSON . stringify ( { email , password , name , ... extra } ) ,
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
} )
}
export async function logout ( ) {
return request ( '/api/auth/logout' , { method : 'POST' } )
}
export async function getCurrentProfile ( ) {
return request ( '/api/profiles/me' )
}
2026-05-05 16:12:17 +02:00
/** Liste aller Profile – nur für Plattform-Admins (Vereinsanlage). */
export async function listProfiles ( ) {
return request ( '/api/profiles' )
}
2026-05-05 21:05:52 +02:00
/** Alle Nutzer inkl. Vereinsmitgliedschaften — nur Portal-Admin (UI: Admin → Nutzer). */
export async function listAdminUsers ( ) {
return request ( '/api/admin/users' )
}
2026-05-07 12:36:46 +02:00
/** Medien-Speicher (MEDIA_ROOT + relativer Unterordner) — GET: admin/superadmin, PUT: nur superadmin. */
export async function getPlatformMediaStorage ( ) {
return request ( '/api/admin/platform-media-storage' )
}
export async function putPlatformMediaStorage ( payload ) {
return request ( '/api/admin/platform-media-storage' , {
method : 'PUT' ,
body : JSON . stringify ( payload ) ,
} )
}
2026-04-29 11:19:05 +02:00
export async function updateProfile ( profileId , data ) {
return request ( ` /api/profiles/ ${ profileId } ` , {
method : 'PUT' ,
body : JSON . stringify ( data ) ,
} )
}
export async function changePassword ( newPassword ) {
return request ( '/api/auth/pin' , {
method : 'PUT' ,
body : JSON . stringify ( { pin : newPassword } ) ,
} )
}
2026-04-29 11:37:54 +02:00
/** GET /api/auth/verify/{token} — keine Auth nötig; Token gehört zur URL des Bestätigungslinks */
export async function verifyEmail ( token ) {
const t = encodeURIComponent ( token )
return request ( ` /api/auth/verify/ ${ t } ` , {
method : 'GET' ,
} )
}
export async function resendVerification ( email ) {
return request ( '/api/auth/resend-verification' , {
method : 'POST' ,
body : JSON . stringify ( { email } ) ,
} )
}
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
// ============================================================================
// Clubs & Groups
// ============================================================================
export async function listClubs ( ) {
return request ( '/api/clubs' )
}
feat: Clubs & Organization Management complete
Backend:
- Created routers/clubs.py with full CRUD
- Clubs: list, get, create, update, delete (admin only)
- Divisions: list, create, update, delete (admin only)
- Training Groups: list, get, create, update, delete (admin/trainer)
- Registered clubs router in main.py
- Permission checks: admin for clubs/divisions, trainer for groups
Frontend:
- Complete ClubsPage with 3 tabs (Vereine, Sparten, Gruppen)
- Role-based UI (admin sees all actions, trainer can manage groups)
- Full CRUD forms with modals
- Mobile-responsive card layouts
- Updated api.js with all club/division/group functions
Migration already exists: 002_organization.sql (clubs, divisions, training_groups)
Next: Skills & Methods display (read-only)
2026-04-22 16:48:02 +02:00
export async function getClub ( id ) {
return request ( ` /api/clubs/ ${ id } ` )
}
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
export async function createClub ( data ) {
return request ( '/api/clubs' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
feat: Clubs & Organization Management complete
Backend:
- Created routers/clubs.py with full CRUD
- Clubs: list, get, create, update, delete (admin only)
- Divisions: list, create, update, delete (admin only)
- Training Groups: list, get, create, update, delete (admin/trainer)
- Registered clubs router in main.py
- Permission checks: admin for clubs/divisions, trainer for groups
Frontend:
- Complete ClubsPage with 3 tabs (Vereine, Sparten, Gruppen)
- Role-based UI (admin sees all actions, trainer can manage groups)
- Full CRUD forms with modals
- Mobile-responsive card layouts
- Updated api.js with all club/division/group functions
Migration already exists: 002_organization.sql (clubs, divisions, training_groups)
Next: Skills & Methods display (read-only)
2026-04-22 16:48:02 +02:00
export async function updateClub ( id , data ) {
return request ( ` /api/clubs/ ${ id } ` , {
method : 'PUT' ,
body : JSON . stringify ( data )
} )
}
export async function deleteClub ( id ) {
return request ( ` /api/clubs/ ${ id } ` , { method : 'DELETE' } )
}
2026-05-05 16:29:11 +02:00
/** Vereinsmitglieder (API für Admin ohne eigene UI) */
export async function listClubMembers ( clubId , { includeInactive = false } = { } ) {
const q = includeInactive ? '?include_inactive=true' : ''
return request ( ` /api/clubs/ ${ clubId } /members ${ q } ` )
}
export async function getClubMember ( clubId , profileId ) {
return request ( ` /api/clubs/ ${ clubId } /members/ ${ profileId } ` )
}
export async function addClubMember ( clubId , payload ) {
return request ( ` /api/clubs/ ${ clubId } /members ` , {
method : 'POST' ,
body : JSON . stringify ( payload ) ,
} )
}
export async function updateClubMember ( clubId , profileId , payload ) {
return request ( ` /api/clubs/ ${ clubId } /members/ ${ profileId } ` , {
method : 'PUT' ,
body : JSON . stringify ( payload ) ,
} )
}
export async function removeClubMember ( clubId , profileId ) {
return request ( ` /api/clubs/ ${ clubId } /members/ ${ profileId } ` , { method : 'DELETE' } )
}
2026-05-05 16:40:49 +02:00
/** Aktive Vereine (öffentlich, für Registrierungswahl). */
export async function listPublicClubsDirectory ( ) {
return request ( '/api/clubs/public-directory' )
}
/** Vereinsinternes Mitgliederverzeichnis (Trainer-/Co-Auswahl). */
export async function clubMembersDirectory ( clubId ) {
return request ( ` /api/clubs/ ${ clubId } /members/directory ` )
}
/** Eigene Beitrittsanträge. */
export async function getMyClubJoinRequests ( ) {
return request ( '/api/me/club-join-requests' )
}
export async function createClubJoinRequest ( payload ) {
return request ( '/api/me/club-join-requests' , {
method : 'POST' ,
body : JSON . stringify ( payload ) ,
} )
}
export async function withdrawClubJoinRequest ( requestId ) {
return request ( ` /api/me/club-join-requests/ ${ requestId } ` , { method : 'DELETE' } )
}
/** Offene Anträge (Vereins-/Plattform-Admin). */
export async function listClubJoinRequests ( clubId ) {
return request ( ` /api/clubs/ ${ clubId } /join-requests ` )
}
export async function acceptClubJoinRequest ( clubId , requestId , roles = [ 'trainer' ] ) {
return request ( ` /api/clubs/ ${ clubId } /join-requests/ ${ requestId } /accept ` , {
method : 'POST' ,
body : JSON . stringify ( { roles } ) ,
} )
}
export async function rejectClubJoinRequest ( clubId , requestId ) {
return request ( ` /api/clubs/ ${ clubId } /join-requests/ ${ requestId } /reject ` , {
method : 'POST' ,
body : JSON . stringify ( { } ) ,
} )
}
feat: Clubs & Organization Management complete
Backend:
- Created routers/clubs.py with full CRUD
- Clubs: list, get, create, update, delete (admin only)
- Divisions: list, create, update, delete (admin only)
- Training Groups: list, get, create, update, delete (admin/trainer)
- Registered clubs router in main.py
- Permission checks: admin for clubs/divisions, trainer for groups
Frontend:
- Complete ClubsPage with 3 tabs (Vereine, Sparten, Gruppen)
- Role-based UI (admin sees all actions, trainer can manage groups)
- Full CRUD forms with modals
- Mobile-responsive card layouts
- Updated api.js with all club/division/group functions
Migration already exists: 002_organization.sql (clubs, divisions, training_groups)
Next: Skills & Methods display (read-only)
2026-04-22 16:48:02 +02:00
export async function listDivisions ( clubId ) {
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
const query = clubId ? ` ?club_id= ${ clubId } ` : ''
feat: Clubs & Organization Management complete
Backend:
- Created routers/clubs.py with full CRUD
- Clubs: list, get, create, update, delete (admin only)
- Divisions: list, create, update, delete (admin only)
- Training Groups: list, get, create, update, delete (admin/trainer)
- Registered clubs router in main.py
- Permission checks: admin for clubs/divisions, trainer for groups
Frontend:
- Complete ClubsPage with 3 tabs (Vereine, Sparten, Gruppen)
- Role-based UI (admin sees all actions, trainer can manage groups)
- Full CRUD forms with modals
- Mobile-responsive card layouts
- Updated api.js with all club/division/group functions
Migration already exists: 002_organization.sql (clubs, divisions, training_groups)
Next: Skills & Methods display (read-only)
2026-04-22 16:48:02 +02:00
return request ( ` /api/divisions ${ query } ` )
}
export async function createDivision ( data ) {
return request ( '/api/divisions' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
export async function updateDivision ( id , data ) {
return request ( ` /api/divisions/ ${ id } ` , {
method : 'PUT' ,
body : JSON . stringify ( data )
} )
}
export async function deleteDivision ( id ) {
return request ( ` /api/divisions/ ${ id } ` , { method : 'DELETE' } )
}
export async function listTrainingGroups ( filters = { } ) {
const query = new URLSearchParams ( filters ) . toString ( )
return request ( ` /api/groups ${ query ? '?' + query : '' } ` )
}
export async function getTrainingGroup ( id ) {
return request ( ` /api/groups/ ${ id } ` )
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
}
export async function createTrainingGroup ( data ) {
return request ( '/api/groups' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
feat: Clubs & Organization Management complete
Backend:
- Created routers/clubs.py with full CRUD
- Clubs: list, get, create, update, delete (admin only)
- Divisions: list, create, update, delete (admin only)
- Training Groups: list, get, create, update, delete (admin/trainer)
- Registered clubs router in main.py
- Permission checks: admin for clubs/divisions, trainer for groups
Frontend:
- Complete ClubsPage with 3 tabs (Vereine, Sparten, Gruppen)
- Role-based UI (admin sees all actions, trainer can manage groups)
- Full CRUD forms with modals
- Mobile-responsive card layouts
- Updated api.js with all club/division/group functions
Migration already exists: 002_organization.sql (clubs, divisions, training_groups)
Next: Skills & Methods display (read-only)
2026-04-22 16:48:02 +02:00
export async function updateTrainingGroup ( id , data ) {
return request ( ` /api/groups/ ${ id } ` , {
method : 'PUT' ,
body : JSON . stringify ( data )
} )
}
export async function deleteTrainingGroup ( id ) {
return request ( ` /api/groups/ ${ id } ` , { method : 'DELETE' } )
}
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
// ============================================================================
// Skills & Methods
// ============================================================================
feat: Skills & Methods catalog complete
Backend:
- Created routers/skills.py with full CRUD
- Skills: list (with category filter), get, create, update, delete
- Methods: list (with category filter), get, create, update, delete
- Default: only show active items
- Read access: all authenticated users
- Write access: admin only
- Registered skills router in main.py
Frontend:
- Complete SkillsPage with 2 tabs (Fähigkeiten, Trainingsmethoden)
- Browse by category with cards layout
- Admin CRUD forms (importance rating for skills, duration/group size for methods)
- Mobile-responsive grid layout
- Updated api.js with all skill/method functions
- Added /skills route to App.jsx
Migration already exists: 003_catalogs.sql (skills, training_methods + seed data)
Next: Training Planning (core feature)
2026-04-22 16:50:31 +02:00
export async function listSkills ( filters = { } ) {
const query = new URLSearchParams ( filters ) . toString ( )
return request ( ` /api/skills ${ query ? '?' + query : '' } ` )
}
2026-04-27 11:53:25 +02:00
/** Admin: Fähigkeiten hierarchisch sortiert (Hauptkategorie → Kategorie → Sortierung) */
export async function listSkillsCatalog ( filters = { } ) {
const query = new URLSearchParams ( filters ) . toString ( )
return request ( ` /api/skills/catalog ${ query ? '?' + query : '' } ` )
}
feat: Skills & Methods catalog complete
Backend:
- Created routers/skills.py with full CRUD
- Skills: list (with category filter), get, create, update, delete
- Methods: list (with category filter), get, create, update, delete
- Default: only show active items
- Read access: all authenticated users
- Write access: admin only
- Registered skills router in main.py
Frontend:
- Complete SkillsPage with 2 tabs (Fähigkeiten, Trainingsmethoden)
- Browse by category with cards layout
- Admin CRUD forms (importance rating for skills, duration/group size for methods)
- Mobile-responsive grid layout
- Updated api.js with all skill/method functions
- Added /skills route to App.jsx
Migration already exists: 003_catalogs.sql (skills, training_methods + seed data)
Next: Training Planning (core feature)
2026-04-22 16:50:31 +02:00
export async function getSkill ( id ) {
return request ( ` /api/skills/ ${ id } ` )
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
}
export async function createSkill ( data ) {
return request ( '/api/skills' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
feat: Skills & Methods catalog complete
Backend:
- Created routers/skills.py with full CRUD
- Skills: list (with category filter), get, create, update, delete
- Methods: list (with category filter), get, create, update, delete
- Default: only show active items
- Read access: all authenticated users
- Write access: admin only
- Registered skills router in main.py
Frontend:
- Complete SkillsPage with 2 tabs (Fähigkeiten, Trainingsmethoden)
- Browse by category with cards layout
- Admin CRUD forms (importance rating for skills, duration/group size for methods)
- Mobile-responsive grid layout
- Updated api.js with all skill/method functions
- Added /skills route to App.jsx
Migration already exists: 003_catalogs.sql (skills, training_methods + seed data)
Next: Training Planning (core feature)
2026-04-22 16:50:31 +02:00
export async function updateSkill ( id , data ) {
return request ( ` /api/skills/ ${ id } ` , {
method : 'PUT' ,
body : JSON . stringify ( data )
} )
}
export async function deleteSkill ( id ) {
return request ( ` /api/skills/ ${ id } ` , { method : 'DELETE' } )
}
export async function listMethods ( filters = { } ) {
const query = new URLSearchParams ( filters ) . toString ( )
return request ( ` /api/methods ${ query ? '?' + query : '' } ` )
}
export async function getMethod ( id ) {
return request ( ` /api/methods/ ${ id } ` )
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
}
export async function createMethod ( data ) {
return request ( '/api/methods' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
feat: Skills & Methods catalog complete
Backend:
- Created routers/skills.py with full CRUD
- Skills: list (with category filter), get, create, update, delete
- Methods: list (with category filter), get, create, update, delete
- Default: only show active items
- Read access: all authenticated users
- Write access: admin only
- Registered skills router in main.py
Frontend:
- Complete SkillsPage with 2 tabs (Fähigkeiten, Trainingsmethoden)
- Browse by category with cards layout
- Admin CRUD forms (importance rating for skills, duration/group size for methods)
- Mobile-responsive grid layout
- Updated api.js with all skill/method functions
- Added /skills route to App.jsx
Migration already exists: 003_catalogs.sql (skills, training_methods + seed data)
Next: Training Planning (core feature)
2026-04-22 16:50:31 +02:00
export async function updateMethod ( id , data ) {
return request ( ` /api/methods/ ${ id } ` , {
method : 'PUT' ,
body : JSON . stringify ( data )
} )
}
export async function deleteMethod ( id ) {
return request ( ` /api/methods/ ${ id } ` , { method : 'DELETE' } )
}
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
// ============================================================================
// Exercises
// ============================================================================
export async function listExercises ( filters = { } ) {
2026-04-27 14:27:25 +02:00
const q = new URLSearchParams ( )
Object . entries ( filters ) . forEach ( ( [ k , v ] ) => {
2026-04-28 08:02:08 +02:00
if ( v === undefined || v === null ) return
2026-04-28 09:30:33 +02:00
if ( typeof v === 'boolean' ) {
2026-05-06 13:52:24 +02:00
q . set ( k , v ? 'true' : 'false' )
2026-04-28 09:30:33 +02:00
return
}
2026-04-28 08:02:08 +02:00
if ( Array . isArray ( v ) ) {
if ( v . length === 0 ) return
v . forEach ( ( item ) => {
if ( item !== '' && item !== undefined && item !== null && String ( item ) . trim ( ) !== '' ) {
q . append ( k , String ( item ) )
}
} )
return
2026-04-27 14:27:25 +02:00
}
2026-04-28 08:02:08 +02:00
if ( String ( v ) . trim ( ) !== '' ) q . set ( k , String ( v ) )
2026-04-27 14:27:25 +02:00
} )
const query = q . toString ( )
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
return request ( ` /api/exercises ${ query ? '?' + query : '' } ` )
}
2026-04-27 14:48:46 +02:00
/** Formular → API-Body (M:N gemäß EXERCISES_API_SPEC + training_types) */
2026-05-07 13:34:22 +02:00
export function buildExerciseApiPayload ( formData , extras = { } ) {
2026-04-27 14:27:25 +02:00
const num = ( v ) => ( v === '' || v == null ? null : Number ( v ) )
2026-04-27 14:48:46 +02:00
const goalHtml = formData . goal || ''
const execHtml = formData . execution || ''
const goalText = stripHtmlToText ( goalHtml )
const execText = stripHtmlToText ( execHtml )
if ( ! goalText && ! execText ) {
throw new Error ( 'Ziel oder Durchführung ausfüllen (mindestens eines, auch mit Formatierung).' )
2026-04-27 14:27:25 +02:00
}
2026-04-27 14:48:46 +02:00
const mapFocus = ( formData . focus _areas _multi || [ ] )
. filter ( ( x ) => x && x . focus _area _id )
. map ( ( x ) => ( { focus _area _id : Number ( x . focus _area _id ) , is _primary : ! ! x . is _primary } ) )
const mapStyles = ( formData . training _styles _multi || [ ] )
. filter ( ( x ) => x && x . training _style _id )
. map ( ( x ) => ( { training _style _id : Number ( x . training _style _id ) , is _primary : ! ! x . is _primary } ) )
const mapTTypes = ( formData . training _types _multi || [ ] )
. filter ( ( x ) => x && x . training _type _id )
. map ( ( x ) => ( { training _type _id : Number ( x . training _type _id ) , is _primary : ! ! x . is _primary } ) )
const mapTg = ( formData . target _groups _multi || [ ] )
. filter ( ( x ) => x && x . target _group _id )
. map ( ( x ) => ( { target _group _id : Number ( x . target _group _id ) , is _primary : ! ! x . is _primary } ) )
2026-04-27 14:27:25 +02:00
return {
title : ( formData . title || '' ) . trim ( ) ,
summary : formData . summary || null ,
2026-04-27 14:48:46 +02:00
goal : goalHtml . trim ( ) ? goalHtml : null ,
execution : execHtml . trim ( ) ? execHtml : null ,
2026-04-27 14:27:25 +02:00
preparation : formData . preparation || null ,
trainer _notes : formData . trainer _notes || null ,
duration _min : num ( formData . duration _min ) ,
duration _max : num ( formData . duration _max ) ,
group _size _min : num ( formData . group _size _min ) ,
group _size _max : num ( formData . group _size _max ) ,
equipment : Array . isArray ( formData . equipment ) ? formData . equipment : [ ] ,
2026-04-27 14:48:46 +02:00
focus _areas _multi : mapFocus ,
training _styles _multi : mapStyles ,
training _types _multi : mapTTypes ,
target _groups _multi : mapTg ,
2026-04-27 15:01:47 +02:00
age _groups : [ ] ,
2026-04-27 14:27:25 +02:00
skills : ( formData . skills || [ ] ) . map ( ( s ) => ( {
skill _id : s . skill _id ,
is _primary : ! ! s . is _primary ,
intensity : s . intensity || null ,
required _level : s . required _level || null ,
target _level : s . target _level || null ,
} ) ) ,
visibility : formData . visibility || 'private' ,
status : formData . status || 'draft' ,
club _id : formData . club _id ? ? null ,
2026-05-07 13:34:22 +02:00
... extras ,
2026-04-27 14:27:25 +02:00
}
}
export async function uploadExerciseMedia ( exerciseId , formData ) {
const token = localStorage . getItem ( 'authToken' )
const headers = { }
if ( token ) headers [ 'X-Auth-Token' ] = token
const response = await fetch ( ` ${ API _URL } /api/exercises/ ${ exerciseId } /media ` , {
method : 'POST' ,
headers ,
body : formData ,
} )
if ( ! response . ok ) {
const err = await response . json ( ) . catch ( ( ) => ( { detail : 'Unknown error' } ) )
const d = err . detail
2026-05-07 13:25:42 +02:00
if (
response . status === 409 &&
d &&
typeof d === 'object' &&
! Array . isArray ( d ) &&
typeof d . code === 'string'
) {
const e = new Error (
typeof d . message === 'string' ? d . message : 'Upload konnte nicht verarbeitet werden' ,
)
e . code = d . code
e . status = 409
e . payload = d
throw e
}
2026-04-27 14:27:25 +02:00
const msg =
typeof d === 'string'
? d
2026-05-07 13:25:42 +02:00
: d != null && typeof d === 'object' && typeof d . message === 'string'
? d . message
: d != null
? JSON . stringify ( d )
: ` HTTP ${ response . status } `
2026-04-27 14:27:25 +02:00
throw new Error ( msg )
}
return response . json ( )
}
export async function updateExerciseMedia ( exerciseId , mediaId , data ) {
return request ( ` /api/exercises/ ${ exerciseId } /media/ ${ mediaId } ` , {
method : 'PUT' ,
body : JSON . stringify ( data ) ,
} )
}
export async function deleteExerciseMedia ( exerciseId , mediaId ) {
return request ( ` /api/exercises/ ${ exerciseId } /media/ ${ mediaId } ` , { method : 'DELETE' } )
}
export async function reorderExerciseMedia ( exerciseId , mediaIds ) {
return request ( ` /api/exercises/ ${ exerciseId } /media/reorder ` , {
method : 'PUT' ,
body : JSON . stringify ( { media _ids : mediaIds } ) ,
} )
}
2026-05-07 13:25:42 +02:00
/** Papierkorb / Recovery — `action`: trash_soft | trash_hidden | recover | purge | reactivate */
2026-05-07 14:33:02 +02:00
export async function postMediaAssetLifecycle ( assetId , action , extra = { } ) {
2026-05-07 12:55:50 +02:00
return request ( ` /api/media-assets/ ${ assetId } /lifecycle ` , {
method : 'POST' ,
2026-05-07 14:33:02 +02:00
body : JSON . stringify ( { action , ... extra } ) ,
2026-05-07 12:55:50 +02:00
} )
}
2026-05-07 13:00:57 +02:00
/** Archiv: aktive media_assets sichtbar für den Nutzer (Bibliotheksrechte). */
export async function listMediaAssets ( params = { } ) {
const sp = new URLSearchParams ( )
if ( params . q ) sp . set ( 'q' , params . q )
if ( params . limit != null ) sp . set ( 'limit' , String ( params . limit ) )
if ( params . offset != null ) sp . set ( 'offset' , String ( params . offset ) )
2026-05-07 14:10:26 +02:00
if ( params . lifecycle ) sp . set ( 'lifecycle' , String ( params . lifecycle ) )
2026-05-07 16:15:07 +02:00
if ( params . media _kind ) sp . set ( 'media_kind' , String ( params . media _kind ) )
if ( params . club _id != null && params . club _id !== '' ) sp . set ( 'club_id' , String ( params . club _id ) )
if ( params . uploaded _by != null && params . uploaded _by !== '' ) sp . set ( 'uploaded_by' , String ( params . uploaded _by ) )
if ( params . include _filter _meta ) sp . set ( 'include_filter_meta' , 'true' )
2026-05-07 13:00:57 +02:00
const qs = sp . toString ( )
return request ( ` /api/media-assets ${ qs ? ` ? ${ qs } ` : '' } ` )
}
2026-05-07 14:10:26 +02:00
export async function patchMediaAsset ( assetId , data ) {
return request ( ` /api/media-assets/ ${ assetId } ` , {
method : 'PATCH' ,
body : JSON . stringify ( data ) ,
} )
}
2026-05-07 14:33:02 +02:00
export async function bulkMediaLifecycle ( data ) {
return request ( '/api/media-assets/bulk-lifecycle' , {
method : 'POST' ,
body : JSON . stringify ( data ) ,
} )
}
export async function bulkPatchMediaAssets ( data ) {
return request ( '/api/media-assets/bulk-patch' , {
method : 'POST' ,
body : JSON . stringify ( data ) ,
} )
}
2026-05-07 13:00:57 +02:00
/** Übung: bestehendes Archiv-Medium verknüpfen (`POST /api/exercises/{id}/media/from-asset`). */
export async function attachExerciseMediaFromAsset ( exerciseId , body ) {
return request ( ` /api/exercises/ ${ exerciseId } /media/from-asset ` , {
method : 'POST' ,
body : JSON . stringify ( body ) ,
} )
}
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
export async function getExercise ( id ) {
return request ( ` /api/exercises/ ${ id } ` )
}
export async function createExercise ( data ) {
return request ( '/api/exercises' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
export async function updateExercise ( id , data ) {
2026-05-07 13:34:22 +02:00
const token = localStorage . getItem ( 'authToken' )
const headers = mergeActiveClubHeader ( { 'Content-Type' : 'application/json' } )
if ( token ) headers [ 'X-Auth-Token' ] = token
const url = ` ${ API _URL } /api/exercises/ ${ id } `
const response = await fetch ( url , {
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
method : 'PUT' ,
2026-05-07 13:34:22 +02:00
headers ,
body : JSON . stringify ( data ) ,
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
} )
2026-05-07 13:34:22 +02:00
if ( ! response . ok ) {
const text = await response . text ( )
let parsed = null
try {
parsed = JSON . parse ( text )
} catch {
parsed = null
}
const d = parsed ? . detail
if (
response . status === 422 &&
d &&
typeof d === 'object' &&
! Array . isArray ( d ) &&
typeof d . code === 'string'
) {
const e = new Error ( typeof d . message === 'string' ? d . message : 'Validierung fehlgeschlagen' )
e . status = 422
e . code = d . code
e . payload = d
throw e
}
if ( parsed ? . detail != null ) {
const msg = typeof parsed . detail === 'string' ? parsed . detail : JSON . stringify ( parsed . detail )
throw new Error ( msg )
}
const snippet = ( text || '' ) . replace ( /\s+/g , ' ' ) . trim ( ) . slice ( 0 , 180 )
throw new Error ( snippet ? ` HTTP ${ response . status } : ${ snippet } ` : ` HTTP ${ response . status } ` )
}
return response . json ( )
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
}
2026-05-06 11:02:46 +02:00
/** Massenänderung Übungen: Sichtbarkeit, Status, Katalog-Zuordnungen (`PATCH /api/exercises/bulk-metadata`). */
2026-05-05 22:18:04 +02:00
export async function bulkPatchExercisesMetadata ( data ) {
return request ( '/api/exercises/bulk-metadata' , {
method : 'PATCH' ,
body : JSON . stringify ( data ) ,
} )
}
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
export async function deleteExercise ( id ) {
return request ( ` /api/exercises/ ${ id } ` , { method : 'DELETE' } )
}
2026-04-28 10:59:09 +02:00
export async function createExerciseVariant ( exerciseId , data ) {
return request ( ` /api/exercises/ ${ exerciseId } /variants ` , {
method : 'POST' ,
body : JSON . stringify ( data ) ,
} )
}
export async function updateExerciseVariant ( exerciseId , variantId , data ) {
return request ( ` /api/exercises/ ${ exerciseId } /variants/ ${ variantId } ` , {
method : 'PUT' ,
body : JSON . stringify ( data ) ,
} )
}
export async function deleteExerciseVariant ( exerciseId , variantId ) {
return request ( ` /api/exercises/ ${ exerciseId } /variants/ ${ variantId } ` , { method : 'DELETE' } )
}
export async function reorderExerciseVariants ( exerciseId , variantIds ) {
return request ( ` /api/exercises/ ${ exerciseId } /variants/reorder ` , {
method : 'PUT' ,
body : JSON . stringify ( { variant _ids : variantIds } ) ,
} )
}
2026-04-30 11:47:50 +02:00
// Progressionsgraphen (Übung → Übung), Migration 032/033
export async function listExerciseProgressionGraphs ( ) {
return request ( '/api/exercise-progression-graphs' )
}
export async function getExerciseProgressionGraph ( id , { includeEdges = false } = { } ) {
const q = includeEdges ? '?include_edges=true' : ''
return request ( ` /api/exercise-progression-graphs/ ${ id } ${ q } ` )
}
export async function createExerciseProgressionGraph ( data ) {
return request ( '/api/exercise-progression-graphs' , {
method : 'POST' ,
body : JSON . stringify ( data ) ,
} )
}
export async function updateExerciseProgressionGraph ( id , data ) {
return request ( ` /api/exercise-progression-graphs/ ${ id } ` , {
method : 'PUT' ,
body : JSON . stringify ( data ) ,
} )
}
export async function deleteExerciseProgressionGraph ( id ) {
return request ( ` /api/exercise-progression-graphs/ ${ id } ` , { method : 'DELETE' } )
}
export async function listExerciseProgressionEdges ( graphId , query = { } ) {
const q = new URLSearchParams ( )
if ( query . from _exercise _id != null ) q . set ( 'from_exercise_id' , String ( query . from _exercise _id ) )
if ( query . to _exercise _id != null ) q . set ( 'to_exercise_id' , String ( query . to _exercise _id ) )
const qs = q . toString ( )
return request ( ` /api/exercise-progression-graphs/ ${ graphId } /edges ${ qs ? ` ? ${ qs } ` : '' } ` )
}
export async function createExerciseProgressionEdge ( graphId , data ) {
return request ( ` /api/exercise-progression-graphs/ ${ graphId } /edges ` , {
method : 'POST' ,
body : JSON . stringify ( data ) ,
} )
}
export async function updateExerciseProgressionEdge ( graphId , edgeId , data ) {
return request ( ` /api/exercise-progression-graphs/ ${ graphId } /edges/ ${ edgeId } ` , {
method : 'PUT' ,
body : JSON . stringify ( data ) ,
} )
}
export async function deleteExerciseProgressionEdge ( graphId , edgeId ) {
return request ( ` /api/exercise-progression-graphs/ ${ graphId } /edges/ ${ edgeId } ` , { method : 'DELETE' } )
}
2026-05-03 18:07:52 +02:00
export async function createExerciseProgressionSequence ( graphId , data ) {
return request ( ` /api/exercise-progression-graphs/ ${ graphId } /edges/sequence ` , {
method : 'POST' ,
body : JSON . stringify ( data ) ,
} )
}
export async function deleteExerciseProgressionEdgesBatch ( graphId , edgeIds ) {
return request ( ` /api/exercise-progression-graphs/ ${ graphId } /edges/delete-batch ` , {
method : 'POST' ,
body : JSON . stringify ( { edge _ids : edgeIds } ) ,
} )
}
2026-04-27 15:01:47 +02:00
/** KI-Ausbaustufe (EXERCISES_API_SPEC): benötigt Backend + z. B. OPENROUTER_API_KEY. */
export async function suggestExerciseAi ( payload ) {
return request ( '/api/exercises/ai/suggest' , {
method : 'POST' ,
body : JSON . stringify ( payload ) ,
} )
}
export async function regenerateExerciseAi ( exerciseId , payload ) {
return request ( ` /api/exercises/ ${ exerciseId } /ai/regenerate ` , {
method : 'POST' ,
body : JSON . stringify ( payload ) ,
} )
}
2026-04-23 07:52:03 +02:00
// ============================================================================
// Catalogs (Admin-verwaltbare Stammdaten)
// ============================================================================
// Focus Areas
export async function listFocusAreas ( filters = { } ) {
const query = new URLSearchParams ( filters ) . toString ( )
return request ( ` /api/focus-areas ${ query ? '?' + query : '' } ` )
}
export async function createFocusArea ( data ) {
return request ( '/api/focus-areas' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
export async function updateFocusArea ( id , data ) {
return request ( ` /api/focus-areas/ ${ id } ` , {
method : 'PUT' ,
body : JSON . stringify ( data )
} )
}
export async function deleteFocusArea ( id ) {
return request ( ` /api/focus-areas/ ${ id } ` , { method : 'DELETE' } )
}
2026-04-24 07:51:40 +02:00
// Admin Hierarchy (Tree View)
export async function getAdminHierarchy ( ) {
return request ( '/api/admin/hierarchy' )
}
2026-04-27 11:32:30 +02:00
// ============================================================================
// Reifegradmodelle / Fähigkeitsmatrix
// ============================================================================
export async function listMaturityModels ( filters = { } ) {
const query = new URLSearchParams (
Object . entries ( filters ) . filter ( ( [ , v ] ) => v !== undefined && v !== null && v !== '' )
) . toString ( )
return request ( ` /api/maturity-models ${ query ? '?' + query : '' } ` )
}
export async function resolveMaturityModel ( filters = { } ) {
const query = new URLSearchParams (
Object . entries ( filters ) . filter ( ( [ , v ] ) => v !== undefined && v !== null && v !== '' )
) . toString ( )
return request ( ` /api/maturity-models/resolve ${ query ? '?' + query : '' } ` )
}
export async function getMaturityModel ( id ) {
return request ( ` /api/maturity-models/ ${ id } ` )
}
export async function createMaturityModel ( data ) {
return request ( '/api/maturity-models' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
export async function updateMaturityModel ( id , data ) {
return request ( ` /api/maturity-models/ ${ id } ` , {
method : 'PUT' ,
body : JSON . stringify ( data )
} )
}
export async function deleteMaturityModel ( id ) {
return request ( ` /api/maturity-models/ ${ id } ` , { method : 'DELETE' } )
}
export async function addMaturityModelSkill ( modelId , data ) {
return request ( ` /api/maturity-models/ ${ modelId } /skills ` , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
export async function removeMaturityModelSkill ( modelId , skillId ) {
return request ( ` /api/maturity-models/ ${ modelId } /skills/ ${ skillId } ` , { method : 'DELETE' } )
}
export async function upsertMaturityModelSkillLevels ( modelId , data ) {
return request ( ` /api/maturity-models/ ${ modelId } /skill-levels ` , {
method : 'PUT' ,
body : JSON . stringify ( data )
} )
}
2026-04-27 12:35:48 +02:00
/** Hierarchische Zuordnung Modell → Fokus / Stilrichtung / Trainingsstil (training_types) */
export async function listMaturityModelContextBindings ( ) {
return request ( '/api/maturity-model-context-bindings' )
}
export async function upsertMaturityModelContextBinding ( data ) {
return request ( '/api/maturity-model-context-bindings' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
export async function deleteMaturityModelContextBinding ( id ) {
return request ( ` /api/maturity-model-context-bindings/ ${ id } ` , { method : 'DELETE' } )
}
2026-04-27 12:48:22 +02:00
export async function importMaturityModelBundle ( payload ) {
return request ( '/api/maturity-models/import' , {
method : 'POST' ,
body : JSON . stringify ( payload )
} )
}
export async function exportMaturityModelBundle ( modelId ) {
return request ( ` /api/maturity-models/ ${ modelId } /export ` )
}
export async function exportResolvedMaturityBundle ( filters = { } ) {
const query = new URLSearchParams (
Object . entries ( filters ) . filter ( ( [ , v ] ) => v !== undefined && v !== null && v !== '' )
) . toString ( )
return request ( ` /api/maturity-models/export-resolved ${ query ? '?' + query : '' } ` )
}
2026-04-27 13:13:36 +02:00
/** Komplett: Fähigkeitskatalog + Reifegradmodelle + Kontext-Bindings (slug-/namensbasiert auf Ziel-DB) */
export async function exportMatrixStackBundle ( ) {
return request ( '/api/admin/matrix-stack/export' )
}
export async function importMatrixStackBundle ( payload ) {
return request ( '/api/admin/matrix-stack/import' , {
method : 'POST' ,
body : JSON . stringify ( payload )
} )
}
2026-04-23 12:27:09 +02:00
// Style Directions (formerly Training Styles)
export async function listStyleDirections ( filters = { } ) {
2026-04-23 07:52:03 +02:00
const query = new URLSearchParams ( filters ) . toString ( )
return request ( ` /api/training-styles ${ query ? '?' + query : '' } ` )
}
2026-04-24 15:20:39 +02:00
// Backward-compatibility alias
export const listTrainingStyles = listStyleDirections
2026-04-23 12:27:09 +02:00
export async function createStyleDirection ( data ) {
2026-04-23 07:52:03 +02:00
return request ( '/api/training-styles' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
2026-04-23 12:27:09 +02:00
export async function updateStyleDirection ( id , data ) {
2026-04-23 07:52:03 +02:00
return request ( ` /api/training-styles/ ${ id } ` , {
method : 'PUT' ,
body : JSON . stringify ( data )
} )
}
2026-04-23 12:27:09 +02:00
export async function deleteStyleDirection ( id ) {
2026-04-23 07:52:03 +02:00
return request ( ` /api/training-styles/ ${ id } ` , { method : 'DELETE' } )
}
// Training Characters
export async function listTrainingCharacters ( filters = { } ) {
const query = new URLSearchParams ( filters ) . toString ( )
return request ( ` /api/training-characters ${ query ? '?' + query : '' } ` )
}
export async function createTrainingCharacter ( data ) {
return request ( '/api/training-characters' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
export async function updateTrainingCharacter ( id , data ) {
return request ( ` /api/training-characters/ ${ id } ` , {
method : 'PUT' ,
body : JSON . stringify ( data )
} )
}
export async function deleteTrainingCharacter ( id ) {
return request ( ` /api/training-characters/ ${ id } ` , { method : 'DELETE' } )
}
2026-04-23 12:12:48 +02:00
// Training Types (Breitensport, Leistungssport, etc.)
export async function listTrainingTypes ( filters = { } ) {
const query = new URLSearchParams ( filters ) . toString ( )
return request ( ` /api/training-types ${ query ? '?' + query : '' } ` )
}
export async function createTrainingType ( data ) {
return request ( '/api/training-types' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
export async function updateTrainingType ( id , data ) {
return request ( ` /api/training-types/ ${ id } ` , {
method : 'PUT' ,
body : JSON . stringify ( data )
} )
}
export async function deleteTrainingType ( id ) {
return request ( ` /api/training-types/ ${ id } ` , { method : 'DELETE' } )
}
2026-04-27 11:53:25 +02:00
// Skill main categories (Hauptgruppen im Fähigkeitskatalog)
export async function listSkillMainCategories ( ) {
return request ( '/api/skill-main-categories' )
}
export async function createSkillMainCategory ( data ) {
return request ( '/api/skill-main-categories' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
export async function updateSkillMainCategory ( id , data ) {
return request ( ` /api/skill-main-categories/ ${ id } ` , {
method : 'PUT' ,
body : JSON . stringify ( data )
} )
}
export async function deleteSkillMainCategory ( id ) {
return request ( ` /api/skill-main-categories/ ${ id } ` , { method : 'DELETE' } )
}
2026-04-23 07:52:03 +02:00
// Skill Categories
export async function listSkillCategories ( filters = { } ) {
const query = new URLSearchParams ( filters ) . toString ( )
return request ( ` /api/skill-categories ${ query ? '?' + query : '' } ` )
}
export async function createSkillCategory ( data ) {
return request ( '/api/skill-categories' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
export async function updateSkillCategory ( id , data ) {
return request ( ` /api/skill-categories/ ${ id } ` , {
method : 'PUT' ,
body : JSON . stringify ( data )
} )
}
export async function deleteSkillCategory ( id ) {
return request ( ` /api/skill-categories/ ${ id } ` , { method : 'DELETE' } )
}
// Trainer Focus Areas
export async function listTrainerFocusAreas ( filters = { } ) {
const query = new URLSearchParams ( filters ) . toString ( )
return request ( ` /api/trainer-focus-areas ${ query ? '?' + query : '' } ` )
}
export async function assignTrainerFocusArea ( data ) {
return request ( '/api/trainer-focus-areas' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
export async function deleteTrainerFocusArea ( id ) {
return request ( ` /api/trainer-focus-areas/ ${ id } ` , { method : 'DELETE' } )
}
2026-04-23 08:55:54 +02:00
// Target Groups (Zielgruppen)
export async function listTargetGroups ( filters = { } ) {
const query = new URLSearchParams ( filters ) . toString ( )
return request ( ` /api/target-groups ${ query ? '?' + query : '' } ` )
}
export async function createTargetGroup ( data ) {
return request ( '/api/target-groups' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
export async function updateTargetGroup ( id , data ) {
return request ( ` /api/target-groups/ ${ id } ` , {
method : 'PUT' ,
body : JSON . stringify ( data )
} )
}
export async function deleteTargetGroup ( id ) {
return request ( ` /api/target-groups/ ${ id } ` , { method : 'DELETE' } )
}
2026-04-23 12:27:09 +02:00
// Style Direction → Target Groups (M:N Assignments)
export async function listStyleDirectionTargetGroups ( filters = { } ) {
2026-04-23 10:45:14 +02:00
const query = new URLSearchParams ( filters ) . toString ( )
return request ( ` /api/training-style-target-groups ${ query ? '?' + query : '' } ` )
}
2026-04-23 12:27:09 +02:00
export async function createStyleDirectionTargetGroup ( data ) {
2026-04-23 10:45:14 +02:00
return request ( '/api/training-style-target-groups' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
2026-04-23 12:27:09 +02:00
export async function updateStyleDirectionTargetGroup ( id , data ) {
2026-04-23 10:45:14 +02:00
return request ( ` /api/training-style-target-groups/ ${ id } ` , {
method : 'PUT' ,
body : JSON . stringify ( data )
} )
}
2026-04-23 12:27:09 +02:00
export async function deleteStyleDirectionTargetGroup ( id ) {
2026-04-23 10:45:14 +02:00
return request ( ` /api/training-style-target-groups/ ${ id } ` , { method : 'DELETE' } )
}
2026-04-23 12:27:09 +02:00
export async function getStyleDirectionsHierarchy ( filters = { } ) {
2026-04-23 10:45:14 +02:00
const query = new URLSearchParams ( filters ) . toString ( )
return request ( ` /api/training-styles/hierarchy ${ query ? '?' + query : '' } ` )
}
2026-04-23 14:22:17 +02:00
// Trainer Contexts (Fokussierte Ansichten)
export async function listTrainerContexts ( ) {
return request ( '/api/trainer-contexts' )
}
export async function createTrainerContext ( data ) {
return request ( '/api/trainer-contexts' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
export async function updateTrainerContext ( id , data ) {
return request ( ` /api/trainer-contexts/ ${ id } ` , {
method : 'PUT' ,
body : JSON . stringify ( data )
} )
}
export async function deleteTrainerContext ( id ) {
return request ( ` /api/trainer-contexts/ ${ id } ` , { method : 'DELETE' } )
}
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
// ============================================================================
// Training Planning
// ============================================================================
2026-05-05 15:40:29 +02:00
/** Query-Parameter wie GET /api/training-units. */
2026-04-28 16:18:25 +02:00
export async function listTrainingUnits ( filters = { } ) {
const q = new URLSearchParams ( )
if ( filters . group _id != null && filters . group _id !== '' ) {
q . set ( 'group_id' , String ( filters . group _id ) )
}
2026-05-05 15:40:29 +02:00
if ( filters . club _id != null && filters . club _id !== '' ) {
q . set ( 'club_id' , String ( filters . club _id ) )
}
2026-04-28 16:18:25 +02:00
if ( filters . start _date ) q . set ( 'start_date' , filters . start _date )
if ( filters . end _date ) q . set ( 'end_date' , filters . end _date )
if ( filters . status ) q . set ( 'status' , filters . status )
2026-05-07 09:01:01 +02:00
if ( filters . debrief _pending === true ) q . set ( 'debrief_pending' , 'true' )
2026-05-05 15:40:29 +02:00
if ( filters . assigned _to _me === true ) q . set ( 'assigned_to_me' , 'true' )
if ( filters . sort ) q . set ( 'sort' , String ( filters . sort ) )
if ( filters . limit != null && filters . limit !== '' ) q . set ( 'limit' , String ( filters . limit ) )
2026-04-28 16:18:25 +02:00
const qs = q . toString ( )
return request ( ` /api/training-units ${ qs ? ` ? ${ qs } ` : '' } ` )
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
}
2026-05-07 10:19:55 +02:00
/** Dashboard: Übungen in geplanten Einheiten, die für den Verein noch auf Sichtbarkeit „Verein“ gehören. */
export async function getTrainingExerciseClubVisibilityQueue ( filters = { } ) {
const q = new URLSearchParams ( )
if ( filters . start _date ) q . set ( 'start_date' , String ( filters . start _date ) )
if ( filters . end _date ) q . set ( 'end_date' , String ( filters . end _date ) )
if ( filters . assigned _to _me === false ) q . set ( 'assigned_to_me' , 'false' )
if ( filters . limit _units != null && filters . limit _units !== '' ) {
q . set ( 'limit_units' , String ( filters . limit _units ) )
}
const qs = q . toString ( )
return request ( ` /api/training-units/exercises-club-visibility-queue ${ qs ? ` ? ${ qs } ` : '' } ` )
}
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
export async function getTrainingUnit ( id ) {
return request ( ` /api/training-units/ ${ id } ` )
}
export async function createTrainingUnit ( data ) {
return request ( '/api/training-units' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
export async function updateTrainingUnit ( id , data ) {
return request ( ` /api/training-units/ ${ id } ` , {
method : 'PUT' ,
body : JSON . stringify ( data )
} )
}
feat: Training Planning (core feature) complete
Backend:
- Created migration 006_training_planning.sql
- training_units table (planned vs actual, status, notes)
- training_unit_exercises M:N (order, duration, modifications)
- Created routers/training_planning.py with full CRUD
- List with filters (group, date range, status)
- Get detail with exercises
- Create/Update/Delete with access control
- Quick create endpoint (auto-fills from group defaults)
- Permission: trainer for own groups, admin for all
- Registered training_planning router in main.py
Frontend:
- Complete TrainingPlanningPage with full planning workflow
- Group selector + date range picker
- List view with status badges (planned/completed/cancelled)
- Create/Edit modal with exercise management
- Drag to reorder exercises (▲▼)
- Quick create button (one-click with group defaults)
- Durchführung section (actual date/time, attendance, status)
- Public notes + trainer-only notes
- Updated api.js with deleteTrainingUnit and quickCreateTrainingUnit
- Added /planning route to App.jsx
- Navigation already configured (Calendar icon)
This is the CORE feature - trainers can now plan and document training sessions.
Next: Admin routes, role system refinement, testing
2026-04-22 16:54:34 +02:00
export async function deleteTrainingUnit ( id ) {
return request ( ` /api/training-units/ ${ id } ` , { method : 'DELETE' } )
}
export async function quickCreateTrainingUnit ( data ) {
return request ( '/api/training-units/quick-create' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
2026-05-05 13:31:26 +02:00
/** Rahmen-Slot → geplante Einheit (tiefe Kopie, origin_framework_slot_id). */
export async function createTrainingUnitFromFrameworkSlot ( data ) {
return request ( '/api/training-units/from-framework-slot' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
2026-04-28 16:31:45 +02:00
export async function listTrainingPlanTemplates ( ) {
return request ( '/api/training-plan-templates' )
}
export async function getTrainingPlanTemplate ( id ) {
return request ( ` /api/training-plan-templates/ ${ id } ` )
}
export async function createTrainingPlanTemplate ( data ) {
return request ( '/api/training-plan-templates' , {
method : 'POST' ,
body : JSON . stringify ( data )
} )
}
export async function updateTrainingPlanTemplate ( id , data ) {
return request ( ` /api/training-plan-templates/ ${ id } ` , {
method : 'PUT' ,
body : JSON . stringify ( data )
} )
}
export async function deleteTrainingPlanTemplate ( id ) {
return request ( ` /api/training-plan-templates/ ${ id } ` , { method : 'DELETE' } )
}
2026-05-05 09:02:13 +02:00
export async function listTrainingFrameworkPrograms ( ) {
return request ( '/api/training-framework-programs' )
}
export async function getTrainingFrameworkProgram ( id ) {
return request ( ` /api/training-framework-programs/ ${ id } ` )
}
export async function createTrainingFrameworkProgram ( data ) {
return request ( '/api/training-framework-programs' , {
method : 'POST' ,
body : JSON . stringify ( data ) ,
} )
}
export async function updateTrainingFrameworkProgram ( id , data ) {
return request ( ` /api/training-framework-programs/ ${ id } ` , {
method : 'PUT' ,
body : JSON . stringify ( data ) ,
} )
}
export async function deleteTrainingFrameworkProgram ( id ) {
return request ( ` /api/training-framework-programs/ ${ id } ` , { method : 'DELETE' } )
}
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
// ============================================================================
// Version & Health
// ============================================================================
export async function getVersion ( ) {
return request ( '/api/version' )
}
export async function healthCheck ( ) {
return request ( '/health' )
}
export const api = {
// Auth
login ,
register ,
logout ,
getCurrentProfile ,
2026-05-05 16:12:17 +02:00
listProfiles ,
2026-05-05 21:05:52 +02:00
listAdminUsers ,
2026-04-29 11:19:05 +02:00
updateProfile ,
changePassword ,
2026-04-29 11:37:54 +02:00
verifyEmail ,
resendVerification ,
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
// Clubs & Groups
listClubs ,
feat: Clubs & Organization Management complete
Backend:
- Created routers/clubs.py with full CRUD
- Clubs: list, get, create, update, delete (admin only)
- Divisions: list, create, update, delete (admin only)
- Training Groups: list, get, create, update, delete (admin/trainer)
- Registered clubs router in main.py
- Permission checks: admin for clubs/divisions, trainer for groups
Frontend:
- Complete ClubsPage with 3 tabs (Vereine, Sparten, Gruppen)
- Role-based UI (admin sees all actions, trainer can manage groups)
- Full CRUD forms with modals
- Mobile-responsive card layouts
- Updated api.js with all club/division/group functions
Migration already exists: 002_organization.sql (clubs, divisions, training_groups)
Next: Skills & Methods display (read-only)
2026-04-22 16:48:02 +02:00
getClub ,
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
createClub ,
feat: Clubs & Organization Management complete
Backend:
- Created routers/clubs.py with full CRUD
- Clubs: list, get, create, update, delete (admin only)
- Divisions: list, create, update, delete (admin only)
- Training Groups: list, get, create, update, delete (admin/trainer)
- Registered clubs router in main.py
- Permission checks: admin for clubs/divisions, trainer for groups
Frontend:
- Complete ClubsPage with 3 tabs (Vereine, Sparten, Gruppen)
- Role-based UI (admin sees all actions, trainer can manage groups)
- Full CRUD forms with modals
- Mobile-responsive card layouts
- Updated api.js with all club/division/group functions
Migration already exists: 002_organization.sql (clubs, divisions, training_groups)
Next: Skills & Methods display (read-only)
2026-04-22 16:48:02 +02:00
updateClub ,
deleteClub ,
2026-05-05 16:29:11 +02:00
listClubMembers ,
getClubMember ,
addClubMember ,
updateClubMember ,
removeClubMember ,
2026-05-05 16:40:49 +02:00
listPublicClubsDirectory ,
clubMembersDirectory ,
getMyClubJoinRequests ,
createClubJoinRequest ,
withdrawClubJoinRequest ,
listClubJoinRequests ,
acceptClubJoinRequest ,
rejectClubJoinRequest ,
feat: Clubs & Organization Management complete
Backend:
- Created routers/clubs.py with full CRUD
- Clubs: list, get, create, update, delete (admin only)
- Divisions: list, create, update, delete (admin only)
- Training Groups: list, get, create, update, delete (admin/trainer)
- Registered clubs router in main.py
- Permission checks: admin for clubs/divisions, trainer for groups
Frontend:
- Complete ClubsPage with 3 tabs (Vereine, Sparten, Gruppen)
- Role-based UI (admin sees all actions, trainer can manage groups)
- Full CRUD forms with modals
- Mobile-responsive card layouts
- Updated api.js with all club/division/group functions
Migration already exists: 002_organization.sql (clubs, divisions, training_groups)
Next: Skills & Methods display (read-only)
2026-04-22 16:48:02 +02:00
listDivisions ,
createDivision ,
updateDivision ,
deleteDivision ,
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
listTrainingGroups ,
feat: Clubs & Organization Management complete
Backend:
- Created routers/clubs.py with full CRUD
- Clubs: list, get, create, update, delete (admin only)
- Divisions: list, create, update, delete (admin only)
- Training Groups: list, get, create, update, delete (admin/trainer)
- Registered clubs router in main.py
- Permission checks: admin for clubs/divisions, trainer for groups
Frontend:
- Complete ClubsPage with 3 tabs (Vereine, Sparten, Gruppen)
- Role-based UI (admin sees all actions, trainer can manage groups)
- Full CRUD forms with modals
- Mobile-responsive card layouts
- Updated api.js with all club/division/group functions
Migration already exists: 002_organization.sql (clubs, divisions, training_groups)
Next: Skills & Methods display (read-only)
2026-04-22 16:48:02 +02:00
getTrainingGroup ,
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
createTrainingGroup ,
feat: Clubs & Organization Management complete
Backend:
- Created routers/clubs.py with full CRUD
- Clubs: list, get, create, update, delete (admin only)
- Divisions: list, create, update, delete (admin only)
- Training Groups: list, get, create, update, delete (admin/trainer)
- Registered clubs router in main.py
- Permission checks: admin for clubs/divisions, trainer for groups
Frontend:
- Complete ClubsPage with 3 tabs (Vereine, Sparten, Gruppen)
- Role-based UI (admin sees all actions, trainer can manage groups)
- Full CRUD forms with modals
- Mobile-responsive card layouts
- Updated api.js with all club/division/group functions
Migration already exists: 002_organization.sql (clubs, divisions, training_groups)
Next: Skills & Methods display (read-only)
2026-04-22 16:48:02 +02:00
updateTrainingGroup ,
deleteTrainingGroup ,
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
// Skills & Methods
listSkills ,
2026-04-27 11:53:25 +02:00
listSkillsCatalog ,
feat: Skills & Methods catalog complete
Backend:
- Created routers/skills.py with full CRUD
- Skills: list (with category filter), get, create, update, delete
- Methods: list (with category filter), get, create, update, delete
- Default: only show active items
- Read access: all authenticated users
- Write access: admin only
- Registered skills router in main.py
Frontend:
- Complete SkillsPage with 2 tabs (Fähigkeiten, Trainingsmethoden)
- Browse by category with cards layout
- Admin CRUD forms (importance rating for skills, duration/group size for methods)
- Mobile-responsive grid layout
- Updated api.js with all skill/method functions
- Added /skills route to App.jsx
Migration already exists: 003_catalogs.sql (skills, training_methods + seed data)
Next: Training Planning (core feature)
2026-04-22 16:50:31 +02:00
getSkill ,
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
createSkill ,
feat: Skills & Methods catalog complete
Backend:
- Created routers/skills.py with full CRUD
- Skills: list (with category filter), get, create, update, delete
- Methods: list (with category filter), get, create, update, delete
- Default: only show active items
- Read access: all authenticated users
- Write access: admin only
- Registered skills router in main.py
Frontend:
- Complete SkillsPage with 2 tabs (Fähigkeiten, Trainingsmethoden)
- Browse by category with cards layout
- Admin CRUD forms (importance rating for skills, duration/group size for methods)
- Mobile-responsive grid layout
- Updated api.js with all skill/method functions
- Added /skills route to App.jsx
Migration already exists: 003_catalogs.sql (skills, training_methods + seed data)
Next: Training Planning (core feature)
2026-04-22 16:50:31 +02:00
updateSkill ,
deleteSkill ,
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
listMethods ,
feat: Skills & Methods catalog complete
Backend:
- Created routers/skills.py with full CRUD
- Skills: list (with category filter), get, create, update, delete
- Methods: list (with category filter), get, create, update, delete
- Default: only show active items
- Read access: all authenticated users
- Write access: admin only
- Registered skills router in main.py
Frontend:
- Complete SkillsPage with 2 tabs (Fähigkeiten, Trainingsmethoden)
- Browse by category with cards layout
- Admin CRUD forms (importance rating for skills, duration/group size for methods)
- Mobile-responsive grid layout
- Updated api.js with all skill/method functions
- Added /skills route to App.jsx
Migration already exists: 003_catalogs.sql (skills, training_methods + seed data)
Next: Training Planning (core feature)
2026-04-22 16:50:31 +02:00
getMethod ,
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
createMethod ,
feat: Skills & Methods catalog complete
Backend:
- Created routers/skills.py with full CRUD
- Skills: list (with category filter), get, create, update, delete
- Methods: list (with category filter), get, create, update, delete
- Default: only show active items
- Read access: all authenticated users
- Write access: admin only
- Registered skills router in main.py
Frontend:
- Complete SkillsPage with 2 tabs (Fähigkeiten, Trainingsmethoden)
- Browse by category with cards layout
- Admin CRUD forms (importance rating for skills, duration/group size for methods)
- Mobile-responsive grid layout
- Updated api.js with all skill/method functions
- Added /skills route to App.jsx
Migration already exists: 003_catalogs.sql (skills, training_methods + seed data)
Next: Training Planning (core feature)
2026-04-22 16:50:31 +02:00
updateMethod ,
deleteMethod ,
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
// Exercises
listExercises ,
getExercise ,
createExercise ,
updateExercise ,
2026-05-05 22:18:04 +02:00
bulkPatchExercisesMetadata ,
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
deleteExercise ,
2026-04-28 10:59:09 +02:00
createExerciseVariant ,
updateExerciseVariant ,
deleteExerciseVariant ,
reorderExerciseVariants ,
2026-04-27 14:27:25 +02:00
buildExerciseApiPayload ,
2026-04-27 15:01:47 +02:00
suggestExerciseAi ,
regenerateExerciseAi ,
2026-04-27 14:27:25 +02:00
uploadExerciseMedia ,
updateExerciseMedia ,
deleteExerciseMedia ,
reorderExerciseMedia ,
2026-05-07 12:55:50 +02:00
postMediaAssetLifecycle ,
2026-05-07 13:00:57 +02:00
listMediaAssets ,
2026-05-07 14:10:26 +02:00
patchMediaAsset ,
2026-05-07 14:33:02 +02:00
bulkMediaLifecycle ,
bulkPatchMediaAssets ,
2026-05-07 13:00:57 +02:00
attachExerciseMediaFromAsset ,
2026-04-30 11:47:50 +02:00
listExerciseProgressionGraphs ,
getExerciseProgressionGraph ,
createExerciseProgressionGraph ,
updateExerciseProgressionGraph ,
deleteExerciseProgressionGraph ,
listExerciseProgressionEdges ,
createExerciseProgressionEdge ,
updateExerciseProgressionEdge ,
deleteExerciseProgressionEdge ,
2026-05-03 18:07:52 +02:00
createExerciseProgressionSequence ,
deleteExerciseProgressionEdgesBatch ,
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
// Training Planning
listTrainingUnits ,
2026-05-07 10:19:55 +02:00
getTrainingExerciseClubVisibilityQueue ,
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
getTrainingUnit ,
createTrainingUnit ,
updateTrainingUnit ,
feat: Training Planning (core feature) complete
Backend:
- Created migration 006_training_planning.sql
- training_units table (planned vs actual, status, notes)
- training_unit_exercises M:N (order, duration, modifications)
- Created routers/training_planning.py with full CRUD
- List with filters (group, date range, status)
- Get detail with exercises
- Create/Update/Delete with access control
- Quick create endpoint (auto-fills from group defaults)
- Permission: trainer for own groups, admin for all
- Registered training_planning router in main.py
Frontend:
- Complete TrainingPlanningPage with full planning workflow
- Group selector + date range picker
- List view with status badges (planned/completed/cancelled)
- Create/Edit modal with exercise management
- Drag to reorder exercises (▲▼)
- Quick create button (one-click with group defaults)
- Durchführung section (actual date/time, attendance, status)
- Public notes + trainer-only notes
- Updated api.js with deleteTrainingUnit and quickCreateTrainingUnit
- Added /planning route to App.jsx
- Navigation already configured (Calendar icon)
This is the CORE feature - trainers can now plan and document training sessions.
Next: Admin routes, role system refinement, testing
2026-04-22 16:54:34 +02:00
deleteTrainingUnit ,
quickCreateTrainingUnit ,
2026-05-05 15:06:34 +02:00
createTrainingUnitFromFrameworkSlot ,
2026-04-28 16:31:45 +02:00
listTrainingPlanTemplates ,
getTrainingPlanTemplate ,
createTrainingPlanTemplate ,
updateTrainingPlanTemplate ,
deleteTrainingPlanTemplate ,
2026-05-05 09:02:13 +02:00
listTrainingFrameworkPrograms ,
getTrainingFrameworkProgram ,
createTrainingFrameworkProgram ,
updateTrainingFrameworkProgram ,
deleteTrainingFrameworkProgram ,
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
2026-04-23 07:52:03 +02:00
// Catalogs
listFocusAreas ,
createFocusArea ,
updateFocusArea ,
deleteFocusArea ,
2026-04-24 08:25:03 +02:00
getAdminHierarchy ,
2026-04-23 12:27:09 +02:00
listStyleDirections ,
2026-04-27 15:03:48 +02:00
listTrainingStyles ,
2026-04-23 12:27:09 +02:00
createStyleDirection ,
updateStyleDirection ,
deleteStyleDirection ,
2026-04-23 07:52:03 +02:00
listTrainingCharacters ,
createTrainingCharacter ,
updateTrainingCharacter ,
deleteTrainingCharacter ,
2026-04-23 12:12:48 +02:00
listTrainingTypes ,
createTrainingType ,
updateTrainingType ,
deleteTrainingType ,
2026-04-27 11:53:25 +02:00
listSkillMainCategories ,
createSkillMainCategory ,
updateSkillMainCategory ,
deleteSkillMainCategory ,
2026-04-23 07:52:03 +02:00
listSkillCategories ,
createSkillCategory ,
updateSkillCategory ,
deleteSkillCategory ,
listTrainerFocusAreas ,
assignTrainerFocusArea ,
deleteTrainerFocusArea ,
2026-04-23 08:55:54 +02:00
listTargetGroups ,
createTargetGroup ,
updateTargetGroup ,
deleteTargetGroup ,
2026-04-23 12:27:09 +02:00
listStyleDirectionTargetGroups ,
createStyleDirectionTargetGroup ,
updateStyleDirectionTargetGroup ,
deleteStyleDirectionTargetGroup ,
2026-04-27 11:32:30 +02:00
listMaturityModels ,
2026-04-27 12:35:48 +02:00
listMaturityModelContextBindings ,
upsertMaturityModelContextBinding ,
deleteMaturityModelContextBinding ,
2026-04-27 12:48:22 +02:00
importMaturityModelBundle ,
exportMaturityModelBundle ,
exportResolvedMaturityBundle ,
2026-04-27 13:13:36 +02:00
exportMatrixStackBundle ,
importMatrixStackBundle ,
2026-04-27 11:32:30 +02:00
resolveMaturityModel ,
getMaturityModel ,
createMaturityModel ,
updateMaturityModel ,
deleteMaturityModel ,
addMaturityModelSkill ,
removeMaturityModelSkill ,
upsertMaturityModelSkillLevels ,
2026-04-23 12:27:09 +02:00
getStyleDirectionsHierarchy ,
2026-04-23 14:22:17 +02:00
listTrainerContexts ,
createTrainerContext ,
updateTrainerContext ,
deleteTrainerContext ,
2026-04-23 07:52:03 +02:00
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
// System
getVersion ,
2026-04-24 16:06:49 +02:00
healthCheck ,
// MediaWiki Import
previewMediaWikiImport : ( category , importType = 'exercise' , limit = 10 ) =>
request ( ` /api/import/mediawiki/preview?category= ${ encodeURIComponent ( category ) } &import_type= ${ importType } &limit= ${ limit } ` ) ,
executeMediaWikiImport : ( data ) =>
request ( '/api/import/mediawiki/execute' , {
method : 'POST' ,
body : JSON . stringify ( data )
} ) ,
getMediaWikiImportStatus : ( logId ) =>
request ( ` /api/import/mediawiki/status/ ${ logId } ` ) ,
listMediaWikiImportLogs : ( ) =>
request ( '/api/import/mediawiki/logs' ) ,
deleteMediaWikiImportReference : ( refId ) =>
request ( ` /api/import/mediawiki/references/ ${ refId } ` , { method : 'DELETE' } )
feat: Complete MVP setup - Docker, Frontend, Migrations, CI/CD
Docker & Deployment:
- docker-compose.yml (Prod: Port 3003/8003)
- docker-compose.dev-env.yml (Dev: Port 3098/8098)
- Backend Dockerfile (Python 3.12-slim)
- Frontend Dockerfile (Node 20 + Nginx)
- Gitea Actions (deploy-dev.yml, deploy-prod.yml)
Frontend:
- React 18 + Vite setup
- package.json, vite.config.js, index.html
- App.jsx (minimal with version display)
- api.js (complete API client)
- app.css + AuthContext from Mitai
- main.jsx entry point
Backend Migrations:
- 001_auth_membership.sql (Auth + Features + Tier Limits)
- 002_organization.sql (Clubs, Divisions, Training Groups)
- 003_catalogs.sql (Skills + Methods with sample data)
Documentation:
- .claude/rules/ (ARCHITECTURE, CODING_RULES, etc.)
- SHINKAN_PROJECT_SETUP.md (technical setup guide)
Server:
- Directories created on Pi: /home/lars/docker/shinkan[-dev]
- Gitea Runner configured and running
Ready for first deployment to dev.shinkan.jinkendo.de
version: 0.1.0
date: 2026-04-21
2026-04-21 14:36:52 +02:00
}
export default api