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 React, { Suspense, lazy } from 'react'
|
||||||
|
import LoginPage from './pages/LoginPage'
|
||||||
|
import { lazyWithRetry } from './utils/lazyWithRetry'
|
||||||
import {
|
import {
|
||||||
RouterProvider,
|
RouterProvider,
|
||||||
createBrowserRouter,
|
createBrowserRouter,
|
||||||
|
|
@ -21,8 +23,7 @@ import ActiveClubSwitcher from './components/ActiveClubSwitcher'
|
||||||
import InactiveMembershipBanner from './components/InactiveMembershipBanner'
|
import InactiveMembershipBanner from './components/InactiveMembershipBanner'
|
||||||
import './app.css'
|
import './app.css'
|
||||||
|
|
||||||
const LoginPage = lazy(() => import('./pages/LoginPage'))
|
const OnboardingPage = lazyWithRetry(() => import('./pages/OnboardingPage'))
|
||||||
const OnboardingPage = lazy(() => import('./pages/OnboardingPage'))
|
|
||||||
const VerifyPage = lazy(() => import('./pages/VerifyPage'))
|
const VerifyPage = lazy(() => import('./pages/VerifyPage'))
|
||||||
const Dashboard = lazy(() => import('./pages/Dashboard'))
|
const Dashboard = lazy(() => import('./pages/Dashboard'))
|
||||||
const AccountSettingsPage = lazy(() => import('./pages/AccountSettingsPage'))
|
const AccountSettingsPage = lazy(() => import('./pages/AccountSettingsPage'))
|
||||||
|
|
@ -125,11 +126,10 @@ function Nav({ showAdminNav, onboardingOnly }) {
|
||||||
function ProtectedLayout() {
|
function ProtectedLayout() {
|
||||||
const { isAuthenticated, loading, user, logout } = useAuth()
|
const { isAuthenticated, loading, user, logout } = useAuth()
|
||||||
|
|
||||||
const handleLogout = () => {
|
const handleLogout = async () => {
|
||||||
if (confirm('Wirklich abmelden?')) {
|
if (!confirm('Wirklich abmelden?')) return
|
||||||
logout()
|
await logout()
|
||||||
window.location.href = '/'
|
window.location.href = '/login'
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { useEntitlements } from '../context/EntitlementsContext'
|
||||||
* Unbegrenzt (limit null) → nichts rendern.
|
* Unbegrenzt (limit null) → nichts rendern.
|
||||||
*/
|
*/
|
||||||
export default function FeatureUsageBadge({ featureId = 'ai_calls', label = 'KI-Kontingent' }) {
|
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)
|
const feat = getFeature(featureId)
|
||||||
|
|
||||||
if (loading && !feat) {
|
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
|
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
|
if (limit == null) return null
|
||||||
|
|
||||||
const tone = !allowed || remaining === 0 ? 'var(--danger)' : 'var(--text2)'
|
const tone = !allowed || remaining === 0 ? 'var(--danger)' : 'var(--text2)'
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,12 @@ export function AuthProvider({ children }) {
|
||||||
setUser(payload)
|
setUser(payload)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const logout = useCallback(() => {
|
const logout = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
await api.logout()
|
||||||
|
} catch {
|
||||||
|
/* Session lokal trotzdem beenden */
|
||||||
|
}
|
||||||
setUser(null)
|
setUser(null)
|
||||||
localStorage.removeItem('authToken')
|
localStorage.removeItem('authToken')
|
||||||
localStorage.removeItem(ACTIVE_CLUB_STORAGE_KEY)
|
localStorage.removeItem(ACTIVE_CLUB_STORAGE_KEY)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'
|
||||||
import { getMeEntitlements } from '../utils/api'
|
import { getMeEntitlements } from '../utils/api'
|
||||||
|
import {
|
||||||
|
getDefaultClubIdForGovernanceForms,
|
||||||
|
getResolvedActiveClubIdForUi,
|
||||||
|
} from '../utils/activeClub'
|
||||||
import { useAuth } from './AuthContext'
|
import { useAuth } from './AuthContext'
|
||||||
|
|
||||||
const EntitlementsContext = createContext(null)
|
const EntitlementsContext = createContext(null)
|
||||||
|
|
@ -10,7 +14,8 @@ export function EntitlementsProvider({ children }) {
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [error, setError] = useState(null)
|
const [error, setError] = useState(null)
|
||||||
|
|
||||||
const clubId = user?.effective_club_id ?? null
|
const clubId =
|
||||||
|
getResolvedActiveClubIdForUi(user) ?? getDefaultClubIdForGovernanceForms(user)
|
||||||
|
|
||||||
const refreshEntitlements = useCallback(async () => {
|
const refreshEntitlements = useCallback(async () => {
|
||||||
if (!isAuthenticated) {
|
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