shinkan-jinkendo/frontend/src/pages/AdminHierarchyPage.jsx
Lars 89055ddbc4
Some checks failed
Deploy Development / deploy (push) Successful in 36s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 5s
Test Suite / playwright-tests (push) Failing after 1m55s
feat: Admin page navigation with MediaWiki Import link
- New AdminPageNav component with horizontal navigation
- Links to Hierarchie / Kataloge / Wiki-Import
- Integrated in all 3 admin pages
- Uses lucide-react icons (TreePine, FolderTree, Download)
- Active state tracking via useLocation
- Mobile-friendly with flexbox layout

Navigation flow:
/admin/hierarchy → /admin/catalogs → /admin/mediawiki-import
2026-04-24 17:03:13 +02:00

197 lines
5.2 KiB
JavaScript

import React, { useState, useEffect } from 'react'
import { api } from '../utils/api'
import AdminPageNav from '../components/AdminPageNav'
import HierarchyTab from '../components/admin/HierarchyTab'
import CatalogsTab from '../components/admin/CatalogsTab'
import AssignmentsTab from '../components/admin/AssignmentsTab'
function AdminHierarchyPage() {
const [activeTab, setActiveTab] = useState('hierarchy')
const [loading, setLoading] = useState(true)
const [error, setError] = useState('')
// Hierarchy Tab State
const [hierarchy, setHierarchy] = useState([])
const [expandedNodes, setExpandedNodes] = useState(new Set())
const [selectedItem, setSelectedItem] = useState(null)
// Catalogs Tab State
const [targetGroups, setTargetGroups] = useState([])
const [skillCategories, setSkillCategories] = useState([])
const [trainingCharacters, setTrainingCharacters] = useState([])
// Assignments Tab State
const [styleDirections, setStyleDirections] = useState([])
const [assignments, setAssignments] = useState([])
useEffect(() => {
loadData()
}, [activeTab])
async function loadData() {
setLoading(true)
setError('')
try {
if (activeTab === 'hierarchy') {
const data = await api.getAdminHierarchy()
setHierarchy(data)
} else if (activeTab === 'catalogs') {
const [tgs, scs, tcs] = await Promise.all([
api.listTargetGroups(),
api.listSkillCategories(),
api.listTrainingCharacters()
])
setTargetGroups(tgs)
setSkillCategories(scs)
setTrainingCharacters(tcs)
} else if (activeTab === 'assignments') {
const [sds, tgs, assns] = await Promise.all([
api.listStyleDirections(),
api.listTargetGroups(),
api.listStyleDirectionTargetGroups()
])
setStyleDirections(sds)
setTargetGroups(tgs)
setAssignments(assns)
}
} catch (e) {
setError(e.message)
} finally {
setLoading(false)
}
}
function handleToggleNode(nodeId) {
setExpandedNodes(prev => {
const newSet = new Set(prev)
if (newSet.has(nodeId)) {
newSet.delete(nodeId)
} else {
newSet.add(nodeId)
}
return newSet
})
}
function handleSelectItem(item, type) {
if (item) {
setSelectedItem({ ...item, _type: type })
} else {
setSelectedItem(null)
}
}
function handleUpdate() {
setSelectedItem(null)
loadData()
}
const tabs = [
{ id: 'hierarchy', label: '🌳 Hierarchie', icon: '🌳' },
{ id: 'catalogs', label: '📋 Kataloge', icon: '📋' },
{ id: 'assignments', label: '🔗 Zuordnungen', icon: '🔗' }
]
return (
<div style={{ padding: '20px' }}>
<AdminPageNav />
<h1 style={{ marginTop: 0 }}>Admin: Katalog-Hierarchie</h1>
{/* Tab Navigation */}
<div className="tab-navigation">
{tabs.map(tab => (
<button
key={tab.id}
className={activeTab === tab.id ? 'tab-button active' : 'tab-button'}
onClick={() => setActiveTab(tab.id)}
>
{tab.icon} {tab.label}
</button>
))}
</div>
{/* Tab Content */}
<div style={{ marginTop: '20px' }}>
{activeTab === 'hierarchy' && (
<HierarchyTab
hierarchy={hierarchy}
expandedNodes={expandedNodes}
selectedItem={selectedItem}
loading={loading}
error={error}
onToggleNode={handleToggleNode}
onSelectItem={handleSelectItem}
onUpdate={handleUpdate}
/>
)}
{activeTab === 'catalogs' && (
<CatalogsTab
targetGroups={targetGroups}
skillCategories={skillCategories}
trainingCharacters={trainingCharacters}
loading={loading}
error={error}
onUpdate={handleUpdate}
/>
)}
{activeTab === 'assignments' && (
<AssignmentsTab
styleDirections={styleDirections}
targetGroups={targetGroups}
assignments={assignments}
loading={loading}
error={error}
onUpdate={handleUpdate}
/>
)}
</div>
<style>{`
.tab-navigation {
display: flex;
gap: 8px;
border-bottom: 2px solid var(--border);
margin-bottom: 20px;
flex-wrap: wrap;
}
.tab-button {
padding: 12px 20px;
background: transparent;
border: none;
border-bottom: 3px solid transparent;
cursor: pointer;
font-size: 16px;
font-weight: 500;
color: var(--text2);
transition: all 0.2s;
}
.tab-button:hover {
color: var(--text1);
background: var(--surface2);
}
.tab-button.active {
color: var(--accent);
border-bottom-color: var(--accent);
}
@media (max-width: 768px) {
.tab-button {
flex: 1 1 auto;
min-width: 120px;
font-size: 14px;
padding: 10px 12px;
}
}
`}</style>
</div>
)
}
export default AdminHierarchyPage