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 { user } = useAuth()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const isSuperadmin = user?.role === 'superadmin'
|
const isSuperadmin = user?.role === 'superadmin'
|
||||||
const isPlatformAdmin = isSuperadmin || user?.role === 'admin'
|
|
||||||
const memberClubs = useMemo(() => activeClubMemberships(user?.clubs), [user?.clubs])
|
const memberClubs = useMemo(() => activeClubMemberships(user?.clubs), [user?.clubs])
|
||||||
const tenantClubDepKey = useMemo(() => getTenantClubDependencyKey(user), [user])
|
const tenantClubDepKey = useMemo(() => getTenantClubDependencyKey(user), [user])
|
||||||
|
const [clubsForGovernanceForms, setClubsForGovernanceForms] = useState([])
|
||||||
|
|
||||||
const filteredGraphVisOptions = useMemo(
|
const filteredGraphVisOptions = useMemo(
|
||||||
() => VIS_OPTIONS.filter((o) => o.value !== 'official' || isSuperadmin),
|
() => VIS_OPTIONS.filter((o) => o.value !== 'official' || isSuperadmin),
|
||||||
|
|
@ -64,7 +64,36 @@ function ExerciseProgressionGraphPanel(
|
||||||
const [metaDescription, setMetaDescription] = useState('')
|
const [metaDescription, setMetaDescription] = useState('')
|
||||||
const [metaVisibility, setMetaVisibility] = useState('private')
|
const [metaVisibility, setMetaVisibility] = useState('private')
|
||||||
const [metaClubSelect, setMetaClubSelect] = useState('')
|
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 [filterAnchorOnly, setFilterAnchorOnly] = useState(!!anchorExerciseId)
|
||||||
const [editingEdgeNotes, setEditingEdgeNotes] = useState(null)
|
const [editingEdgeNotes, setEditingEdgeNotes] = useState(null)
|
||||||
|
|
@ -129,6 +158,25 @@ function ExerciseProgressionGraphPanel(
|
||||||
}
|
}
|
||||||
}, [refreshGraphs, tenantClubDepKey])
|
}, [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(() => {
|
useEffect(() => {
|
||||||
if (!selectedGraphId) {
|
if (!selectedGraphId) {
|
||||||
setSkillProfileData(null)
|
setSkillProfileData(null)
|
||||||
|
|
@ -162,7 +210,6 @@ function ExerciseProgressionGraphPanel(
|
||||||
setMetaDescription('')
|
setMetaDescription('')
|
||||||
setMetaVisibility('private')
|
setMetaVisibility('private')
|
||||||
setMetaClubSelect('')
|
setMetaClubSelect('')
|
||||||
setMetaClubManual('')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const g = graphs.find((x) => x.id === selectedGraphId)
|
const g = graphs.find((x) => x.id === selectedGraphId)
|
||||||
|
|
@ -176,7 +223,6 @@ function ExerciseProgressionGraphPanel(
|
||||||
const fallback = getDefaultClubIdForGovernanceForms(user)
|
const fallback = getDefaultClubIdForGovernanceForms(user)
|
||||||
setMetaClubSelect(fallback != null ? String(fallback) : '')
|
setMetaClubSelect(fallback != null ? String(fallback) : '')
|
||||||
}
|
}
|
||||||
setMetaClubManual('')
|
|
||||||
}
|
}
|
||||||
let cancelled = false
|
let cancelled = false
|
||||||
;(async () => {
|
;(async () => {
|
||||||
|
|
@ -195,14 +241,11 @@ function ExerciseProgressionGraphPanel(
|
||||||
const g = graphs.find((x) => x.id === selectedGraphId)
|
const g = graphs.find((x) => x.id === selectedGraphId)
|
||||||
if (g?.club_id != null) return Number(g.club_id)
|
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()
|
const sel = String(metaClubSelect || '').trim()
|
||||||
if (sel && /^\d+$/.test(sel)) return Number(sel)
|
if (sel && /^\d+$/.test(sel)) return Number(sel)
|
||||||
|
|
||||||
return getDefaultClubIdForGovernanceForms(user)
|
return getDefaultClubIdForGovernanceForms(user)
|
||||||
}, [graphs, selectedGraphId, metaClubManual, metaClubSelect, user])
|
}, [graphs, selectedGraphId, metaClubSelect, user])
|
||||||
|
|
||||||
const filteredEdges = useMemo(() => {
|
const filteredEdges = useMemo(() => {
|
||||||
if (!filterAnchorOnly || anchorExerciseId == null) return edges
|
if (!filterAnchorOnly || anchorExerciseId == null) return edges
|
||||||
|
|
@ -593,27 +636,31 @@ function ExerciseProgressionGraphPanel(
|
||||||
onChange={(e) => setMetaClubSelect(e.target.value)}
|
onChange={(e) => setMetaClubSelect(e.target.value)}
|
||||||
>
|
>
|
||||||
<option value="">Aktiver Verein (Vereins-Umschalter / Header)</option>
|
<option value="">Aktiver Verein (Vereins-Umschalter / Header)</option>
|
||||||
{memberClubs.map((c) => (
|
{showGovernanceClubOptgroups ? (
|
||||||
<option key={c.id} value={String(c.id)}>
|
<>
|
||||||
{c.name || `Verein #${c.id}`}
|
<optgroup label="Meine Vereine">
|
||||||
</option>
|
{sortedMemberClubs.map((c) => (
|
||||||
))}
|
<option key={c.id} value={String(c.id)}>
|
||||||
|
{c.name || `Verein #${c.id}`}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="Weitere Vereine">
|
||||||
|
{sortedOtherGovernanceClubs.map((c) => (
|
||||||
|
<option key={c.id} value={String(c.id)}>
|
||||||
|
{c.name || `Verein #${c.id}`}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</optgroup>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
governanceClubSelectOptions.map((c) => (
|
||||||
|
<option key={c.id} value={String(c.id)}>
|
||||||
|
{c.name || `Verein #${c.id}`}
|
||||||
|
</option>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</select>
|
</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)}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
|
<div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user