fix: Resolve black pages issue - Add missing auth slice and update checker

Fixed critical issues causing tracking pages to display black:
1. PWA service worker caching old JavaScript chunks
2. Missing auth Redux slice causing undefined errors

## Service Worker Update Checker
- Added /public/check-updates.js script
- Checks for SW updates every 60 seconds
- Auto-reloads page when new SW is activated
- Forces update check on page load
- Prevents future cache staleness issues

## Auth Redux Slice
- Created store/slices/authSlice.ts with User interface
- Added auth reducer to Redux store configuration
- Included auth in persist whitelist
- Provides selectors: selectUser, selectFamilyId, etc.
- Fixes "Cannot read properties of undefined (reading 'user')" error

## Root Cause
Tracking pages reference state.auth.user.familyId but auth slice
didn't exist in Redux store, causing TypeError on all tracking pages.

Build:  PASSED
Files: 3 new, 2 modified

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-05 06:20:51 +00:00
parent 9068818b57
commit fb92160322
5 changed files with 92 additions and 2 deletions

View File

@@ -46,6 +46,7 @@ export default function RootLayout({
<link rel="manifest" href="/manifest.json" />
<meta name="theme-color" content="#FFB6C1" />
<link rel="apple-touch-icon" href="/icon-192x192.png" />
<script src="/check-updates.js" defer></script>
</head>
<body className={inter.className}>
<AxeProvider>

View File

@@ -0,0 +1,26 @@
/**
* Service Worker Update Checker
* Detects when a new build is available and prompts user to reload
*/
if ('serviceWorker' in navigator) {
// Check for updates every 60 seconds
setInterval(() => {
navigator.serviceWorker.getRegistration().then(registration => {
if (registration) {
registration.update();
}
});
}, 60000);
// Listen for service worker updates
navigator.serviceWorker.addEventListener('controllerchange', () => {
console.log('[SW] New service worker activated, reloading page...');
window.location.reload();
});
// Force update check on load
navigator.serviceWorker.ready.then(registration => {
registration.update();
});
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,61 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import type { RootState } from '../store';
export interface User {
id: string;
email: string;
name: string;
familyId: string;
}
export interface AuthState {
user: User | null;
token: string | null;
isAuthenticated: boolean;
loading: boolean;
}
const initialState: AuthState = {
user: null,
token: null,
isAuthenticated: false,
loading: false,
};
const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
setUser: (state, action: PayloadAction<User>) => {
state.user = action.payload;
state.isAuthenticated = true;
},
setToken: (state, action: PayloadAction<string>) => {
state.token = action.payload;
},
setAuth: (state, action: PayloadAction<{ user: User; token: string }>) => {
state.user = action.payload.user;
state.token = action.payload.token;
state.isAuthenticated = true;
},
logout: (state) => {
state.user = null;
state.token = null;
state.isAuthenticated = false;
},
setLoading: (state, action: PayloadAction<boolean>) => {
state.loading = action.payload;
},
},
});
export const { setUser, setToken, setAuth, logout, setLoading } = authSlice.actions;
// Selectors
export const selectUser = (state: RootState) => state.auth?.user;
export const selectToken = (state: RootState) => state.auth?.token;
export const selectIsAuthenticated = (state: RootState) => state.auth?.isAuthenticated ?? false;
export const selectAuthLoading = (state: RootState) => state.auth?.loading ?? false;
export const selectFamilyId = (state: RootState) => state.auth?.user?.familyId;
export default authSlice.reducer;

View File

@@ -4,6 +4,7 @@ import storage from 'redux-persist/lib/storage'; // defaults to localStorage for
import { combineReducers } from '@reduxjs/toolkit';
// Slices
import authReducer from './slices/authSlice';
import offlineReducer from './slices/offlineSlice';
import activitiesReducer from './slices/activitiesSlice';
import childrenReducer from './slices/childrenSlice';
@@ -19,10 +20,11 @@ const persistConfig = {
version: 1,
storage,
// Only persist these slices (exclude network status as it should be fresh on reload)
whitelist: ['offline', 'activities', 'children'],
whitelist: ['auth', 'offline', 'activities', 'children'],
};
const rootReducer = combineReducers({
auth: authReducer,
offline: offlineReducer,
activities: activitiesReducer,
children: childrenReducer,