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 [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
|
||||
const [skillCategories, setSkillCategories] = useState([])
|
||||
const [editingSC, setEditingSC] = useState(null)
|
||||
|
|
@ -61,6 +66,9 @@ export default function AdminCatalogsPage() {
|
|||
} else if (activeTab === 'training-characters') {
|
||||
const data = await api.listTrainingCharacters()
|
||||
setTrainingCharacters(data)
|
||||
} else if (activeTab === 'training-types') {
|
||||
const data = await api.listTrainingTypes()
|
||||
setTrainingTypes(data)
|
||||
} else if (activeTab === 'skill-categories') {
|
||||
const data = await api.listSkillCategories()
|
||||
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
|
||||
async function createSkillCategory() {
|
||||
try {
|
||||
|
|
@ -280,7 +319,8 @@ export default function AdminCatalogsPage() {
|
|||
<div style={{ display: 'flex', gap: '8px', borderBottom: '2px solid var(--border)', marginBottom: '24px', overflowX: 'auto' }}>
|
||||
{[
|
||||
{ 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: 'target-groups', label: 'Zielgruppen' },
|
||||
{ id: 'target-groups-matrix', label: 'Zuordnungen' },
|
||||
|
|
@ -437,7 +477,7 @@ export default function AdminCatalogsPage() {
|
|||
{activeTab === 'training-styles' && (
|
||||
<div>
|
||||
<div className="card" style={{ marginBottom: '24px' }}>
|
||||
<h3>Neuer Trainingsstil</h3>
|
||||
<h3>Neue Stilrichtung</h3>
|
||||
<div className="form-row">
|
||||
<label className="form-label">Name</label>
|
||||
<input
|
||||
|
|
@ -602,6 +642,95 @@ export default function AdminCatalogsPage() {
|
|||
</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 */}
|
||||
{activeTab === 'skill-categories' && (
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -11,5 +11,5 @@ export const PAGE_VERSIONS = {
|
|||
ClubsPage: "1.0.0",
|
||||
SkillsPage: "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