shinkan-jinkendo/frontend/src/utils/api.js
Lars 46d000d6b3
Some checks failed
Test Suite / lint-backend (push) Waiting to run
Test Suite / build-frontend (push) Waiting to run
Test Suite / playwright-tests (push) Blocked by required conditions
Deploy Development / deploy (push) Has been cancelled
feat: SMW-Importer Frontend (Phase 2 complete)
Phase 2A: API Functions
- 5 MediaWiki import functions in api.js
- previewMediaWikiImport, executeMediaWikiImport
- getMediaWikiImportStatus, listMediaWikiImportLogs
- deleteMediaWikiImportReference

Phase 2B: UI Component
- MediaWikiImportPage.jsx (3-tab interface)
- Preview Tab: Category selection, preview with accordions
- Execute Tab: Import form with status polling
- History Tab: Import log list with refresh

Phase 2C: Routing
- Added /admin/mediawiki-import route in App.jsx
- Import and ProtectedRoute wrapper

Issue: SMW-Importer Frontend (Option C from handover)
Related: backend/routers/csv_import.py (MediaWiki endpoints)
2026-04-24 16:06:49 +02:00

621 lines
16 KiB
JavaScript

/**
* Shinkan Jinkendo API Client
*
* Zentrale API-Kommunikation mit automatischer Token-Injektion
*/
const API_URL = import.meta.env.VITE_API_URL || ''
/**
* Generic API request with automatic token injection
*/
async function request(endpoint, options = {}) {
const token = localStorage.getItem('authToken')
const headers = {
'Content-Type': 'application/json',
...options.headers
}
if (token) {
headers['X-Auth-Token'] = token
}
const response = await fetch(`${API_URL}${endpoint}`, {
...options,
headers
})
if (!response.ok) {
const error = await response.json().catch(() => ({ detail: 'Unknown error' }))
throw new Error(error.detail || `HTTP ${response.status}`)
}
return response.json()
}
// ============================================================================
// Auth
// ============================================================================
export async function login(email, password) {
return request('/api/auth/login', {
method: 'POST',
body: JSON.stringify({ email, password })
})
}
export async function register(email, password, name) {
return request('/api/auth/register', {
method: 'POST',
body: JSON.stringify({ email, password, name })
})
}
export async function logout() {
return request('/api/auth/logout', { method: 'POST' })
}
export async function getCurrentProfile() {
return request('/api/profiles/me')
}
// ============================================================================
// Clubs & Groups
// ============================================================================
export async function listClubs() {
return request('/api/clubs')
}
export async function getClub(id) {
return request(`/api/clubs/${id}`)
}
export async function createClub(data) {
return request('/api/clubs', {
method: 'POST',
body: JSON.stringify(data)
})
}
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' })
}
export async function listDivisions(clubId) {
const query = clubId ? `?club_id=${clubId}` : ''
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}`)
}
export async function createTrainingGroup(data) {
return request('/api/groups', {
method: 'POST',
body: JSON.stringify(data)
})
}
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' })
}
// ============================================================================
// Skills & Methods
// ============================================================================
export async function listSkills(filters = {}) {
const query = new URLSearchParams(filters).toString()
return request(`/api/skills${query ? '?' + query : ''}`)
}
export async function getSkill(id) {
return request(`/api/skills/${id}`)
}
export async function createSkill(data) {
return request('/api/skills', {
method: 'POST',
body: JSON.stringify(data)
})
}
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}`)
}
export async function createMethod(data) {
return request('/api/methods', {
method: 'POST',
body: JSON.stringify(data)
})
}
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' })
}
// ============================================================================
// Exercises
// ============================================================================
export async function listExercises(filters = {}) {
const query = new URLSearchParams(filters).toString()
return request(`/api/exercises${query ? '?' + query : ''}`)
}
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) {
return request(`/api/exercises/${id}`, {
method: 'PUT',
body: JSON.stringify(data)
})
}
export async function deleteExercise(id) {
return request(`/api/exercises/${id}`, { method: 'DELETE' })
}
// ============================================================================
// 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' })
}
// Admin Hierarchy (Tree View)
export async function getAdminHierarchy() {
return request('/api/admin/hierarchy')
}
// Style Directions (formerly Training Styles)
export async function listStyleDirections(filters = {}) {
const query = new URLSearchParams(filters).toString()
return request(`/api/training-styles${query ? '?' + query : ''}`)
}
// Backward-compatibility alias
export const listTrainingStyles = listStyleDirections
export async function createStyleDirection(data) {
return request('/api/training-styles', {
method: 'POST',
body: JSON.stringify(data)
})
}
export async function updateStyleDirection(id, data) {
return request(`/api/training-styles/${id}`, {
method: 'PUT',
body: JSON.stringify(data)
})
}
export async function deleteStyleDirection(id) {
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' })
}
// 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' })
}
// 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' })
}
// 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' })
}
// Style Direction → Target Groups (M:N Assignments)
export async function listStyleDirectionTargetGroups(filters = {}) {
const query = new URLSearchParams(filters).toString()
return request(`/api/training-style-target-groups${query ? '?' + query : ''}`)
}
export async function createStyleDirectionTargetGroup(data) {
return request('/api/training-style-target-groups', {
method: 'POST',
body: JSON.stringify(data)
})
}
export async function updateStyleDirectionTargetGroup(id, data) {
return request(`/api/training-style-target-groups/${id}`, {
method: 'PUT',
body: JSON.stringify(data)
})
}
export async function deleteStyleDirectionTargetGroup(id) {
return request(`/api/training-style-target-groups/${id}`, { method: 'DELETE' })
}
export async function getStyleDirectionsHierarchy(filters = {}) {
const query = new URLSearchParams(filters).toString()
return request(`/api/training-styles/hierarchy${query ? '?' + query : ''}`)
}
// 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' })
}
// ============================================================================
// Training Planning
// ============================================================================
export async function listTrainingUnits(groupId, startDate, endDate) {
const query = new URLSearchParams({ group_id: groupId, start_date: startDate, end_date: endDate }).toString()
return request(`/api/training-units?${query}`)
}
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)
})
}
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)
})
}
// ============================================================================
// 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,
// Clubs & Groups
listClubs,
getClub,
createClub,
updateClub,
deleteClub,
listDivisions,
createDivision,
updateDivision,
deleteDivision,
listTrainingGroups,
getTrainingGroup,
createTrainingGroup,
updateTrainingGroup,
deleteTrainingGroup,
// Skills & Methods
listSkills,
getSkill,
createSkill,
updateSkill,
deleteSkill,
listMethods,
getMethod,
createMethod,
updateMethod,
deleteMethod,
// Exercises
listExercises,
getExercise,
createExercise,
updateExercise,
deleteExercise,
// Training Planning
listTrainingUnits,
getTrainingUnit,
createTrainingUnit,
updateTrainingUnit,
deleteTrainingUnit,
quickCreateTrainingUnit,
// Catalogs
listFocusAreas,
createFocusArea,
updateFocusArea,
deleteFocusArea,
getAdminHierarchy,
listStyleDirections,
createStyleDirection,
updateStyleDirection,
deleteStyleDirection,
listTrainingCharacters,
createTrainingCharacter,
updateTrainingCharacter,
deleteTrainingCharacter,
listTrainingTypes,
createTrainingType,
updateTrainingType,
deleteTrainingType,
listSkillCategories,
createSkillCategory,
updateSkillCategory,
deleteSkillCategory,
listTrainerFocusAreas,
assignTrainerFocusArea,
deleteTrainerFocusArea,
listTargetGroups,
createTargetGroup,
updateTargetGroup,
deleteTargetGroup,
listStyleDirectionTargetGroups,
createStyleDirectionTargetGroup,
updateStyleDirectionTargetGroup,
deleteStyleDirectionTargetGroup,
getStyleDirectionsHierarchy,
listTrainerContexts,
createTrainerContext,
updateTrainerContext,
deleteTrainerContext,
// System
getVersion,
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' })
}
export default api