import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; import { auth, company } from '../api'; export const bootstrapSession = createAsyncThunk('auth/bootstrap', async (session) => { if (session) return session; return auth.me(); }); export const loginWithGoogle = createAsyncThunk('auth/login', async (credential) => { const { session } = await auth.loginWithGoogle(credential); if (!session?.user) { throw new Error('Login ok but session cookie not accepted (/api/auth/me still unauthorized)'); } return session; }); export const selectCompany = createAsyncThunk('auth/selectCompany', async (companyId) => { const data = await company.select(companyId); return data.user ?? { selectedCompanyId: companyId }; }); export const createCompany = createAsyncThunk('auth/createCompany', async (payload) => { const data = await company.create(payload); if (data.user) return data.user; if (data.company?.id) { const selected = await company.select(data.company.id); return selected.user ?? { selectedCompanyId: data.company.id }; } throw new Error('Unternehmen angelegt, aber keine Benutzerdaten erhalten.'); }); export const logout = createAsyncThunk('auth/logout', async () => { await auth.logout(); window.history.replaceState(null, '', '/'); }); function mergeUser(state, incoming) { if (!incoming) return state.user; const selectedCompanyId = incoming.selectedCompanyId ?? state.user?.selectedCompanyId ?? null; return { ...state.user, ...incoming, selectedCompanyId }; } const initialState = { user: null, googleClientId: null, status: 'loading', }; const authSlice = createSlice({ name: 'auth', initialState, reducers: {}, extraReducers: (builder) => { builder .addCase(bootstrapSession.fulfilled, (state, action) => { const session = action.payload; state.googleClientId = session.googleClientId ?? state.googleClientId; if (!session.user) { if (state.status !== 'authed') { state.user = null; state.status = 'anon'; } return; } state.user = mergeUser(state, session.user); state.status = 'authed'; }) .addCase(bootstrapSession.rejected, (state) => { if (state.status === 'loading') { state.user = null; state.status = 'anon'; } }) .addCase(loginWithGoogle.fulfilled, (state, action) => { const session = action.payload; state.user = session.user; state.googleClientId = session.googleClientId ?? state.googleClientId; state.status = 'authed'; }) .addCase(loginWithGoogle.rejected, (state) => { state.user = null; state.status = 'anon'; }) .addCase(selectCompany.fulfilled, (state, action) => { state.user = mergeUser(state, action.payload); state.status = 'authed'; }) .addCase(createCompany.fulfilled, (state, action) => { state.user = mergeUser(state, action.payload); state.status = 'authed'; }) .addCase(createCompany.rejected, (state, action) => { console.error('[redux] createCompany.rejected', action.error?.message, action.payload); }) .addCase(logout.fulfilled, (state) => { state.user = null; state.status = 'anon'; }); }, }); export const selectAuthStatus = (state) => state.auth.status; export const selectAuthUser = (state) => state.auth.user; export const selectGoogleClientId = (state) => state.auth.googleClientId; export const selectNeedsCompanySetup = (state) => state.auth.status === 'authed' && state.auth.user?.selectedCompanyId == null; export default authSlice.reducer;