Enhance Authentication and Feature Usage Handling
Some checks failed
Deploy Development / deploy (push) Successful in 43s
Test Suite / pytest-backend (push) Successful in 42s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 14s
Test Suite / k6 /health Baseline (push) Successful in 34s
Test Suite / playwright-tests (push) Failing after 1m15s
Some checks failed
Deploy Development / deploy (push) Successful in 43s
Test Suite / pytest-backend (push) Successful in 42s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 14s
Test Suite / k6 /health Baseline (push) Successful in 34s
Test Suite / playwright-tests (push) Failing after 1m15s
- Refactored the logout function in AuthContext to handle asynchronous logout operations, improving session management. - Updated the FeatureUsageBadge component to display error messages when feature data retrieval fails, enhancing user feedback. - Replaced lazy loading of OnboardingPage with lazyWithRetry for improved loading reliability. - Adjusted the EntitlementsContext to determine club ID using utility functions for better governance form handling.
This commit is contained in:
parent
91dae7b614
commit
8718cf5c70
|
|
@ -1,4 +1,6 @@
|
|||
import React, { Suspense, lazy } from 'react'
|
||||
import LoginPage from './pages/LoginPage'
|
||||
import { lazyWithRetry } from './utils/lazyWithRetry'
|
||||
import {
|
||||
RouterProvider,
|
||||
createBrowserRouter,
|
||||
|
|
@ -21,8 +23,7 @@ import ActiveClubSwitcher from './components/ActiveClubSwitcher'
|
|||
import InactiveMembershipBanner from './components/InactiveMembershipBanner'
|
||||
import './app.css'
|
||||
|
||||
const LoginPage = lazy(() => import('./pages/LoginPage'))
|
||||
const OnboardingPage = lazy(() => import('./pages/OnboardingPage'))
|
||||
const OnboardingPage = lazyWithRetry(() => import('./pages/OnboardingPage'))
|
||||
const VerifyPage = lazy(() => import('./pages/VerifyPage'))
|
||||
const Dashboard = lazy(() => import('./pages/Dashboard'))
|
||||
const AccountSettingsPage = lazy(() => import('./pages/AccountSettingsPage'))
|
||||
|
|
@ -125,11 +126,10 @@ function Nav({ showAdminNav, onboardingOnly }) {
|
|||
function ProtectedLayout() {
|
||||
const { isAuthenticated, loading, user, logout } = useAuth()
|
||||
|
||||
const handleLogout = () => {
|
||||
if (confirm('Wirklich abmelden?')) {
|
||||
logout()
|
||||
window.location.href = '/'
|
||||
}
|
||||
const handleLogout = async () => {
|
||||
if (!confirm('Wirklich abmelden?')) return
|
||||
await logout()
|
||||
window.location.href = '/login'
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { useEntitlements } from '../context/EntitlementsContext'
|
|||
* Unbegrenzt (limit null) → nichts rendern.
|
||||
*/
|
||||
export default function FeatureUsageBadge({ featureId = 'ai_calls', label = 'KI-Kontingent' }) {
|
||||
const { entitlements, loading, getFeature } = useEntitlements()
|
||||
const { entitlements, loading, error, getFeature } = useEntitlements()
|
||||
const feat = getFeature(featureId)
|
||||
|
||||
if (loading && !feat) {
|
||||
|
|
@ -16,9 +16,23 @@ export default function FeatureUsageBadge({ featureId = 'ai_calls', label = 'KI-
|
|||
)
|
||||
}
|
||||
|
||||
if (!feat) return null
|
||||
if (!feat) {
|
||||
if (error) {
|
||||
return (
|
||||
<span
|
||||
className="feature-usage-badge muted"
|
||||
style={{ fontSize: '0.8rem', color: 'var(--text3)' }}
|
||||
title={error}
|
||||
>
|
||||
{label}: —
|
||||
</span>
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const { used = 0, limit, remaining, allowed } = feat
|
||||
// limit === 0 (z. B. Free-Plan ai_calls) anzeigen; nur echtes Unbegrenzt (null) ausblenden
|
||||
if (limit == null) return null
|
||||
|
||||
const tone = !allowed || remaining === 0 ? 'var(--danger)' : 'var(--text2)'
|
||||
|
|
|
|||
|
|
@ -122,7 +122,12 @@ export function AuthProvider({ children }) {
|
|||
setUser(payload)
|
||||
}, [])
|
||||
|
||||
const logout = useCallback(() => {
|
||||
const logout = useCallback(async () => {
|
||||
try {
|
||||
await api.logout()
|
||||
} catch {
|
||||
/* Session lokal trotzdem beenden */
|
||||
}
|
||||
setUser(null)
|
||||
localStorage.removeItem('authToken')
|
||||
localStorage.removeItem(ACTIVE_CLUB_STORAGE_KEY)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
||||
import { getMeEntitlements } from '../utils/api'
|
||||
import {
|
||||
getDefaultClubIdForGovernanceForms,
|
||||
getResolvedActiveClubIdForUi,
|
||||
} from '../utils/activeClub'
|
||||
import { useAuth } from './AuthContext'
|
||||
|
||||
const EntitlementsContext = createContext(null)
|
||||
|
|
@ -10,7 +14,8 @@ export function EntitlementsProvider({ children }) {
|
|||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState(null)
|
||||
|
||||
const clubId = user?.effective_club_id ?? null
|
||||
const clubId =
|
||||
getResolvedActiveClubIdForUi(user) ?? getDefaultClubIdForGovernanceForms(user)
|
||||
|
||||
const refreshEntitlements = useCallback(async () => {
|
||||
if (!isAuthenticated) {
|
||||
|
|
|
|||
20
frontend/src/utils/lazyWithRetry.js
Normal file
20
frontend/src/utils/lazyWithRetry.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { lazy } from 'react'
|
||||
|
||||
/**
|
||||
* Lazy-Import mit Reload bei fehlendem Chunk (nach Deploy alte Hashes im Browser-Cache).
|
||||
*/
|
||||
export function lazyWithRetry(importFn) {
|
||||
return lazy(() =>
|
||||
importFn().catch((err) => {
|
||||
const key = 'sj_chunk_reload'
|
||||
const reloaded = sessionStorage.getItem(key)
|
||||
if (!reloaded) {
|
||||
sessionStorage.setItem(key, '1')
|
||||
window.location.reload()
|
||||
return new Promise(() => {})
|
||||
}
|
||||
sessionStorage.removeItem(key)
|
||||
throw err
|
||||
}),
|
||||
)
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user