Initial commit: WrestleDesk full project

- Django backend with DRF (clubs, wrestlers, trainers, exercises, templates, trainings, homework, locations, leistungstest)
- Next.js 16 frontend with React, Shadcn UI, Tailwind
- JWT authentication
- Full CRUD for all entities
- Calendar view for trainings
- Homework management system
- Leistungstest tracking
This commit is contained in:
Andrej Spielmann
2026-03-26 13:24:57 +01:00
commit 3fefc550fe
256 changed files with 38295 additions and 0 deletions
@@ -0,0 +1,66 @@
"use client"
import { useState, useCallback } from "react"
import { useAuth } from "@/lib/auth"
import { apiFetch } from "@/lib/api"
export interface FilterState {
[key: string]: string
}
export function useFilterPreferences(pageKey: string, defaultFilters: FilterState) {
const { token } = useAuth()
const [filters, setFilters] = useState<FilterState>(defaultFilters)
const fetchPreferences = useCallback(async () => {
if (!token) return
try {
const prefs = await apiFetch<Record<string, FilterState>>(`/auth/preferences/`, { token })
const key = `${pageKey}_filters`
const savedFilters = prefs[key] || {}
const mergedFilters = { ...defaultFilters, ...savedFilters }
setFilters(mergedFilters)
return mergedFilters
} catch (err) {
console.error(`Failed to fetch ${pageKey} preferences:`, err)
return defaultFilters
}
}, [token, pageKey, defaultFilters])
const savePreferences = useCallback(async (newFilters: FilterState) => {
if (!token) return
try {
const payload: Record<string, FilterState> = {}
payload[`${pageKey}_filters`] = newFilters
await apiFetch(`/auth/preferences/`, {
method: "PATCH",
token,
body: JSON.stringify(payload),
})
} catch (err) {
console.error(`Failed to save ${pageKey} preferences:`, err)
}
}, [token, pageKey])
const updateFilter = useCallback((key: string, value: string, onUpdate?: () => void) => {
const newFilters = { ...filters, [key]: value }
setFilters(newFilters)
savePreferences(newFilters)
onUpdate?.()
}, [filters, savePreferences])
const resetFilters = useCallback((onReset?: () => void) => {
setFilters(defaultFilters)
savePreferences(defaultFilters)
onReset?.()
}, [defaultFilters, savePreferences])
return {
filters,
setFilters,
fetchPreferences,
savePreferences,
updateFilter,
resetFilters,
}
}
+30
View File
@@ -0,0 +1,30 @@
"use client"
import { useSearchParams } from "next/navigation"
import { useCallback, useMemo } from "react"
export function useFilters() {
const searchParams = useSearchParams()
const getParam = useCallback((key: string): string => {
return searchParams.get(key) || ""
}, [searchParams])
const getParamArray = useCallback((key: string): string[] => {
const val = searchParams.get(key)
return val ? val.split(",") : []
}, [searchParams])
const hasActiveFilters = useMemo(() => {
return Array.from(searchParams.keys()).some(
key => key !== "page" && searchParams.get(key)
)
}, [searchParams])
return {
getParam,
getParamArray,
hasActiveFilters,
allParams: searchParams,
}
}