Enhance Exercise Progression Graph Panel with Governance Club Management
All checks were successful
Deploy Development / deploy (push) Successful in 42s
Test Suite / pytest-backend (push) Successful in 44s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 14s
Test Suite / k6 /health Baseline (push) Successful in 38s
Test Suite / playwright-tests (push) Successful in 1m12s
All checks were successful
Deploy Development / deploy (push) Successful in 42s
Test Suite / pytest-backend (push) Successful in 44s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 14s
Test Suite / k6 /health Baseline (push) Successful in 38s
Test Suite / playwright-tests (push) Successful in 1m12s
- Introduced functionality to manage governance clubs for superadmins, allowing for better club selection and organization within the Exercise Progression Graph Panel. - Implemented state management for clubs, including sorting and filtering options, to improve user experience and accessibility. - Enhanced the useEffect hook to fetch governance clubs dynamically, ensuring up-to-date club information is available for selection. - Updated the club selection dropdown to categorize clubs into "My Clubs" and "Other Clubs," improving clarity and usability for users.
This commit is contained in:
parent
87d9fa9b65
commit
1c67a50ce4
|
|
@ -41,9 +41,9 @@ function ExerciseProgressionGraphPanel(
|
|||
const { user } = useAuth()
|
||||
const location = useLocation()
|
||||
const isSuperadmin = user?.role === 'superadmin'
|
||||
const isPlatformAdmin = isSuperadmin || user?.role === 'admin'
|
||||
const memberClubs = useMemo(() => activeClubMemberships(user?.clubs), [user?.clubs])
|
||||
const tenantClubDepKey = useMemo(() => getTenantClubDependencyKey(user), [user])
|
||||
const [clubsForGovernanceForms, setClubsForGovernanceForms] = useState([])
|
||||
|
||||
const filteredGraphVisOptions = useMemo(
|
||||
() => VIS_OPTIONS.filter((o) => o.value !== 'official' || isSuperadmin),
|
||||
|
|
@ -64,7 +64,36 @@ function ExerciseProgressionGraphPanel(
|
|||
const [metaDescription, setMetaDescription] = useState('')
|
||||
const [metaVisibility, setMetaVisibility] = useState('private')
|
||||
const [metaClubSelect, setMetaClubSelect] = useState('')
|
||||
const [metaClubManual, setMetaClubManual] = useState('')
|
||||
|
||||
const memberClubIdSet = useMemo(
|
||||
() => new Set(memberClubs.map((c) => Number(c.id))),
|
||||
[memberClubs],
|
||||
)
|
||||
|
||||
const sortedMemberClubs = useMemo(
|
||||
() =>
|
||||
[...memberClubs].sort((a, b) =>
|
||||
String(a.name || '').localeCompare(String(b.name || ''), 'de'),
|
||||
),
|
||||
[memberClubs],
|
||||
)
|
||||
|
||||
const sortedOtherGovernanceClubs = useMemo(() => {
|
||||
if (!isSuperadmin || clubsForGovernanceForms.length === 0) return []
|
||||
return clubsForGovernanceForms
|
||||
.filter((c) => !memberClubIdSet.has(Number(c.id)))
|
||||
.sort((a, b) => String(a.name || '').localeCompare(String(b.name || ''), 'de'))
|
||||
}, [isSuperadmin, clubsForGovernanceForms, memberClubIdSet])
|
||||
|
||||
const showGovernanceClubOptgroups =
|
||||
isSuperadmin && sortedMemberClubs.length > 0 && sortedOtherGovernanceClubs.length > 0
|
||||
|
||||
const governanceClubSelectOptions = useMemo(() => {
|
||||
if (isSuperadmin && clubsForGovernanceForms.length > 0) {
|
||||
return [...sortedMemberClubs, ...sortedOtherGovernanceClubs]
|
||||
}
|
||||
return sortedMemberClubs
|
||||
}, [isSuperadmin, clubsForGovernanceForms.length, sortedMemberClubs, sortedOtherGovernanceClubs])
|
||||
|
||||
const [filterAnchorOnly, setFilterAnchorOnly] = useState(!!anchorExerciseId)
|
||||
const [editingEdgeNotes, setEditingEdgeNotes] = useState(null)
|
||||
|
|
@ -129,6 +158,25 @@ function ExerciseProgressionGraphPanel(
|
|||
}
|
||||
}, [refreshGraphs, tenantClubDepKey])
|
||||
|
||||
useEffect(() => {
|
||||
if (!isSuperadmin) {
|
||||
setClubsForGovernanceForms([])
|
||||
return undefined
|
||||
}
|
||||
let cancelled = false
|
||||
;(async () => {
|
||||
try {
|
||||
const list = await api.listClubs()
|
||||
if (!cancelled) setClubsForGovernanceForms(Array.isArray(list) ? list : [])
|
||||
} catch {
|
||||
if (!cancelled) setClubsForGovernanceForms([])
|
||||
}
|
||||
})()
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [isSuperadmin, tenantClubDepKey])
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedGraphId) {
|
||||
setSkillProfileData(null)
|
||||
|
|
@ -162,7 +210,6 @@ function ExerciseProgressionGraphPanel(
|
|||
setMetaDescription('')
|
||||
setMetaVisibility('private')
|
||||
setMetaClubSelect('')
|
||||
setMetaClubManual('')
|
||||
return
|
||||
}
|
||||
const g = graphs.find((x) => x.id === selectedGraphId)
|
||||
|
|
@ -176,7 +223,6 @@ function ExerciseProgressionGraphPanel(
|
|||
const fallback = getDefaultClubIdForGovernanceForms(user)
|
||||
setMetaClubSelect(fallback != null ? String(fallback) : '')
|
||||
}
|
||||
setMetaClubManual('')
|
||||
}
|
||||
let cancelled = false
|
||||
;(async () => {
|
||||
|
|
@ -195,14 +241,11 @@ function ExerciseProgressionGraphPanel(
|
|||
const g = graphs.find((x) => x.id === selectedGraphId)
|
||||
if (g?.club_id != null) return Number(g.club_id)
|
||||
|
||||
const manual = String(metaClubManual || '').trim()
|
||||
if (manual && /^\d+$/.test(manual)) return Number(manual)
|
||||
|
||||
const sel = String(metaClubSelect || '').trim()
|
||||
if (sel && /^\d+$/.test(sel)) return Number(sel)
|
||||
|
||||
return getDefaultClubIdForGovernanceForms(user)
|
||||
}, [graphs, selectedGraphId, metaClubManual, metaClubSelect, user])
|
||||
}, [graphs, selectedGraphId, metaClubSelect, user])
|
||||
|
||||
const filteredEdges = useMemo(() => {
|
||||
if (!filterAnchorOnly || anchorExerciseId == null) return edges
|
||||
|
|
@ -593,27 +636,31 @@ function ExerciseProgressionGraphPanel(
|
|||
onChange={(e) => setMetaClubSelect(e.target.value)}
|
||||
>
|
||||
<option value="">Aktiver Verein (Vereins-Umschalter / Header)</option>
|
||||
{memberClubs.map((c) => (
|
||||
{showGovernanceClubOptgroups ? (
|
||||
<>
|
||||
<optgroup label="Meine Vereine">
|
||||
{sortedMemberClubs.map((c) => (
|
||||
<option key={c.id} value={String(c.id)}>
|
||||
{c.name || `Verein #${c.id}`}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{isPlatformAdmin ? (
|
||||
<>
|
||||
<label className="form-label" style={{ marginTop: '10px' }}>
|
||||
Oder Vereins-ID (Plattform-Admin)
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
min={1}
|
||||
className="form-input"
|
||||
placeholder="Leer = wie Dropdown / aktiver Verein"
|
||||
value={metaClubManual}
|
||||
onChange={(e) => setMetaClubManual(e.target.value)}
|
||||
/>
|
||||
</optgroup>
|
||||
<optgroup label="Weitere Vereine">
|
||||
{sortedOtherGovernanceClubs.map((c) => (
|
||||
<option key={c.id} value={String(c.id)}>
|
||||
{c.name || `Verein #${c.id}`}
|
||||
</option>
|
||||
))}
|
||||
</optgroup>
|
||||
</>
|
||||
) : null}
|
||||
) : (
|
||||
governanceClubSelectOptions.map((c) => (
|
||||
<option key={c.id} value={String(c.id)}>
|
||||
{c.name || `Verein #${c.id}`}
|
||||
</option>
|
||||
))
|
||||
)}
|
||||
</select>
|
||||
</div>
|
||||
) : null}
|
||||
<div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user