fix: stabilize auth-to-tenant flow and correct tenant header mapping
Resolve post-login routing and tenant context issues by proxying frontend API calls, redirecting authenticated users away from /login, and hardening club loading with retries/loading guards. Align tenant identity end-to-end by returning tenantId in /api/clubs/me and sending X-Tenant-Id from cookie-backed tenantId instead of local clubId, restoring authorized tasks/shifts data access after club selection.
This commit is contained in:
@@ -8,6 +8,7 @@ type Club = {
|
||||
id: string;
|
||||
name: string;
|
||||
sportType: string;
|
||||
tenantId: string;
|
||||
};
|
||||
|
||||
type TenantContextType = {
|
||||
@@ -15,6 +16,8 @@ type TenantContextType = {
|
||||
setActiveClub: (clubId: string) => void;
|
||||
userRole: string | null;
|
||||
clubs: Club[];
|
||||
clubsLoading: boolean;
|
||||
clubsError: Error | null;
|
||||
};
|
||||
|
||||
const TenantContext = createContext<TenantContextType | undefined>(undefined);
|
||||
@@ -24,14 +27,20 @@ export function TenantProvider({ children }: { children: ReactNode }) {
|
||||
const [activeClubId, setActiveClubId] = useState<string | null>(null);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { data: clubs = [] } = useQuery<Club[]>({
|
||||
queryKey: ['my-clubs'],
|
||||
const { data: clubs = [], isLoading: clubsLoading, error: clubsError } = useQuery<Club[]>({
|
||||
queryKey: ['my-clubs', session?.accessToken],
|
||||
queryFn: async () => {
|
||||
const res = await fetch('/api/clubs/me');
|
||||
if (!res.ok) return [];
|
||||
const res = await fetch('/api/clubs/me', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${session?.accessToken}`,
|
||||
},
|
||||
});
|
||||
if (!res.ok) throw new Error(`Failed to fetch clubs: ${res.statusText}`);
|
||||
return res.json();
|
||||
},
|
||||
enabled: status === 'authenticated',
|
||||
enabled: status === 'authenticated' && !!session?.accessToken,
|
||||
retry: 3,
|
||||
retryDelay: (attemptIndex) => Math.min(1000 * Math.pow(2, attemptIndex), 10000),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -47,21 +56,27 @@ export function TenantProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
useEffect(() => {
|
||||
if (activeClubId) {
|
||||
document.cookie = `X-Tenant-Id=${activeClubId}; path=/; max-age=86400`;
|
||||
const selectedClub = clubs.find(c => c.id === activeClubId);
|
||||
if (selectedClub) {
|
||||
document.cookie = `X-Tenant-Id=${selectedClub.tenantId}; path=/; max-age=86400`;
|
||||
}
|
||||
}
|
||||
}, [activeClubId]);
|
||||
}, [activeClubId, clubs]);
|
||||
|
||||
const handleSetActiveClub = (clubId: string) => {
|
||||
setActiveClubId(clubId);
|
||||
localStorage.setItem('activeClubId', clubId);
|
||||
document.cookie = `X-Tenant-Id=${clubId}; path=/; max-age=86400`;
|
||||
const selectedClub = clubs.find(c => c.id === clubId);
|
||||
if (selectedClub) {
|
||||
document.cookie = `X-Tenant-Id=${selectedClub.tenantId}; path=/; max-age=86400`;
|
||||
}
|
||||
queryClient.invalidateQueries();
|
||||
};
|
||||
|
||||
const userRole = activeClubId && session?.user?.clubs ? session.user.clubs[activeClubId] || null : null;
|
||||
|
||||
return (
|
||||
<TenantContext.Provider value={{ activeClubId, setActiveClub: handleSetActiveClub, userRole, clubs }}>
|
||||
<TenantContext.Provider value={{ activeClubId, setActiveClub: handleSetActiveClub, userRole, clubs, clubsLoading, clubsError: clubsError || null }}>
|
||||
{children}
|
||||
</TenantContext.Provider>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user