feat: Admin-UI Trainingsstil-Dimension + Umbenennung
Admin-UI Erweiterung: 1. Tab "Trainingsstile" → "Stilrichtungen" umbenannt - Überschrift: "Neue Stilrichtung" (statt Trainingsstil) - Tab-Label: "Stilrichtungen" 2. Neuer Tab "Trainingsstil" (Breitensport/Leistungssport) - CRUD-UI: Create/Update/Delete - Felder: Name, Kürzel (abbreviation), Beschreibung - State: trainingTypes, editingTT, newTT - Funktionen: createTrainingType, updateTrainingType, deleteTrainingType - Load-Logik: activeTab === 'training-types' Tab-Reihenfolge: - Fokusbereiche → Stilrichtungen → Trainingsstil → Hierarchie → Zielgruppen → Zuordnungen Pattern: Konsistent mit anderen Katalog-Tabs Version: AdminCatalogsPage 2.1.0
This commit is contained in:
parent
72c927e69e
commit
a9a4c78a0e
|
|
@ -21,6 +21,11 @@ export default function AdminCatalogsPage() {
|
||||||
const [editingTC, setEditingTC] = useState(null)
|
const [editingTC, setEditingTC] = useState(null)
|
||||||
const [newTC, setNewTC] = useState({ name: '', description: '' })
|
const [newTC, setNewTC] = useState({ name: '', description: '' })
|
||||||
|
|
||||||
|
// Training Types (Breitensport, Leistungssport, etc.)
|
||||||
|
const [trainingTypes, setTrainingTypes] = useState([])
|
||||||
|
const [editingTT, setEditingTT] = useState(null)
|
||||||
|
const [newTT, setNewTT] = useState({ name: '', abbreviation: '', description: '' })
|
||||||
|
|
||||||
// Skill Categories
|
// Skill Categories
|
||||||
const [skillCategories, setSkillCategories] = useState([])
|
const [skillCategories, setSkillCategories] = useState([])
|
||||||
const [editingSC, setEditingSC] = useState(null)
|
const [editingSC, setEditingSC] = useState(null)
|
||||||
|
|
@ -61,6 +66,9 @@ export default function AdminCatalogsPage() {
|
||||||
} else if (activeTab === 'training-characters') {
|
} else if (activeTab === 'training-characters') {
|
||||||
const data = await api.listTrainingCharacters()
|
const data = await api.listTrainingCharacters()
|
||||||
setTrainingCharacters(data)
|
setTrainingCharacters(data)
|
||||||
|
} else if (activeTab === 'training-types') {
|
||||||
|
const data = await api.listTrainingTypes()
|
||||||
|
setTrainingTypes(data)
|
||||||
} else if (activeTab === 'skill-categories') {
|
} else if (activeTab === 'skill-categories') {
|
||||||
const data = await api.listSkillCategories()
|
const data = await api.listSkillCategories()
|
||||||
setSkillCategories(data)
|
setSkillCategories(data)
|
||||||
|
|
@ -189,6 +197,37 @@ export default function AdminCatalogsPage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Training Types
|
||||||
|
async function createTrainingType() {
|
||||||
|
try {
|
||||||
|
await api.createTrainingType(newTT)
|
||||||
|
setNewTT({ name: '', abbreviation: '', description: '' })
|
||||||
|
loadData()
|
||||||
|
} catch (e) {
|
||||||
|
setError(e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateTrainingType(id, data) {
|
||||||
|
try {
|
||||||
|
await api.updateTrainingType(id, data)
|
||||||
|
setEditingTT(null)
|
||||||
|
loadData()
|
||||||
|
} catch (e) {
|
||||||
|
setError(e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteTrainingType(id) {
|
||||||
|
if (!confirm('Trainingsstil wirklich löschen?')) return
|
||||||
|
try {
|
||||||
|
await api.deleteTrainingType(id)
|
||||||
|
loadData()
|
||||||
|
} catch (e) {
|
||||||
|
setError(e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Skill Categories
|
// Skill Categories
|
||||||
async function createSkillCategory() {
|
async function createSkillCategory() {
|
||||||
try {
|
try {
|
||||||
|
|
@ -280,7 +319,8 @@ export default function AdminCatalogsPage() {
|
||||||
<div style={{ display: 'flex', gap: '8px', borderBottom: '2px solid var(--border)', marginBottom: '24px', overflowX: 'auto' }}>
|
<div style={{ display: 'flex', gap: '8px', borderBottom: '2px solid var(--border)', marginBottom: '24px', overflowX: 'auto' }}>
|
||||||
{[
|
{[
|
||||||
{ id: 'focus-areas', label: 'Fokusbereiche' },
|
{ id: 'focus-areas', label: 'Fokusbereiche' },
|
||||||
{ id: 'training-styles', label: 'Trainingsstile' },
|
{ id: 'training-styles', label: 'Stilrichtungen' },
|
||||||
|
{ id: 'training-types', label: 'Trainingsstil' },
|
||||||
{ id: 'hierarchy', label: 'Hierarchie' },
|
{ id: 'hierarchy', label: 'Hierarchie' },
|
||||||
{ id: 'target-groups', label: 'Zielgruppen' },
|
{ id: 'target-groups', label: 'Zielgruppen' },
|
||||||
{ id: 'target-groups-matrix', label: 'Zuordnungen' },
|
{ id: 'target-groups-matrix', label: 'Zuordnungen' },
|
||||||
|
|
@ -437,7 +477,7 @@ export default function AdminCatalogsPage() {
|
||||||
{activeTab === 'training-styles' && (
|
{activeTab === 'training-styles' && (
|
||||||
<div>
|
<div>
|
||||||
<div className="card" style={{ marginBottom: '24px' }}>
|
<div className="card" style={{ marginBottom: '24px' }}>
|
||||||
<h3>Neuer Trainingsstil</h3>
|
<h3>Neue Stilrichtung</h3>
|
||||||
<div className="form-row">
|
<div className="form-row">
|
||||||
<label className="form-label">Name</label>
|
<label className="form-label">Name</label>
|
||||||
<input
|
<input
|
||||||
|
|
@ -602,6 +642,95 @@ export default function AdminCatalogsPage() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Training Types */}
|
||||||
|
{activeTab === 'training-types' && (
|
||||||
|
<div>
|
||||||
|
<div className="card" style={{ marginBottom: '24px' }}>
|
||||||
|
<h3>Neuer Trainingsstil</h3>
|
||||||
|
<div className="form-row">
|
||||||
|
<label className="form-label">Name</label>
|
||||||
|
<input
|
||||||
|
className="form-input"
|
||||||
|
value={newTT.name}
|
||||||
|
onChange={e => setNewTT({ ...newTT, name: e.target.value })}
|
||||||
|
placeholder="z.B. Breitensport"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form-row">
|
||||||
|
<label className="form-label">Kürzel (optional)</label>
|
||||||
|
<input
|
||||||
|
className="form-input"
|
||||||
|
value={newTT.abbreviation || ''}
|
||||||
|
onChange={e => setNewTT({ ...newTT, abbreviation: e.target.value })}
|
||||||
|
placeholder="z.B. BS"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form-row">
|
||||||
|
<label className="form-label">Beschreibung</label>
|
||||||
|
<textarea
|
||||||
|
className="form-input"
|
||||||
|
value={newTT.description || ''}
|
||||||
|
onChange={e => setNewTT({ ...newTT, description: e.target.value })}
|
||||||
|
rows={3}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button className="btn btn-primary" onClick={createTrainingType}>Anlegen</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ display: 'grid', gap: '16px' }}>
|
||||||
|
{trainingTypes.map(tt => (
|
||||||
|
<div key={tt.id} className="card">
|
||||||
|
{editingTT?.id === tt.id ? (
|
||||||
|
<div>
|
||||||
|
<div className="form-row">
|
||||||
|
<label className="form-label">Name</label>
|
||||||
|
<input
|
||||||
|
className="form-input"
|
||||||
|
value={editingTT.name}
|
||||||
|
onChange={e => setEditingTT({ ...editingTT, name: e.target.value })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form-row">
|
||||||
|
<label className="form-label">Kürzel</label>
|
||||||
|
<input
|
||||||
|
className="form-input"
|
||||||
|
value={editingTT.abbreviation || ''}
|
||||||
|
onChange={e => setEditingTT({ ...editingTT, abbreviation: e.target.value })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form-row">
|
||||||
|
<label className="form-label">Beschreibung</label>
|
||||||
|
<textarea
|
||||||
|
className="form-input"
|
||||||
|
value={editingTT.description || ''}
|
||||||
|
onChange={e => setEditingTT({ ...editingTT, description: e.target.value })}
|
||||||
|
rows={3}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div style={{ display: 'flex', gap: '8px' }}>
|
||||||
|
<button className="btn btn-primary" onClick={() => updateTrainingType(tt.id, editingTT)}>Speichern</button>
|
||||||
|
<button className="btn" onClick={() => setEditingTT(null)}>Abbrechen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<h3>
|
||||||
|
{tt.name}
|
||||||
|
{tt.abbreviation && <span style={{ marginLeft: '8px', fontSize: '14px', color: 'var(--text2)' }}>({tt.abbreviation})</span>}
|
||||||
|
</h3>
|
||||||
|
{tt.description && <p style={{ margin: '8px 0', color: 'var(--text2)' }}>{tt.description}</p>}
|
||||||
|
<div style={{ display: 'flex', gap: '8px', marginTop: '12px' }}>
|
||||||
|
<button className="btn" onClick={() => setEditingTT(tt)}>Bearbeiten</button>
|
||||||
|
<button className="btn" onClick={() => deleteTrainingType(tt.id)}>Löschen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Skill Categories */}
|
{/* Skill Categories */}
|
||||||
{activeTab === 'skill-categories' && (
|
{activeTab === 'skill-categories' && (
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -11,5 +11,5 @@ export const PAGE_VERSIONS = {
|
||||||
ClubsPage: "1.0.0",
|
ClubsPage: "1.0.0",
|
||||||
SkillsPage: "1.0.0",
|
SkillsPage: "1.0.0",
|
||||||
TrainingPlanningPage: "1.0.0",
|
TrainingPlanningPage: "1.0.0",
|
||||||
AdminCatalogsPage: "2.0.0", // Updated: M:N Refactoring - Hierarchy Tree + Matrix
|
AdminCatalogsPage: "2.1.0", // Updated: Stilrichtungen + Trainingsstil-Dimension
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user