diff --git a/maternal-web/app/(auth)/login/page.tsx b/maternal-web/app/(auth)/login/page.tsx
index 50efbfa..459f979 100644
--- a/maternal-web/app/(auth)/login/page.tsx
+++ b/maternal-web/app/(auth)/login/page.tsx
@@ -333,11 +333,9 @@ export default function LoginPage() {
{t('login.noAccount')}{' '}
-
-
- {t('login.signUp')}
-
-
+
+ {t('login.signUp')}
+
diff --git a/maternal-web/app/(auth)/register/page.tsx b/maternal-web/app/(auth)/register/page.tsx
index e45b468..8ccdbf5 100644
--- a/maternal-web/app/(auth)/register/page.tsx
+++ b/maternal-web/app/(auth)/register/page.tsx
@@ -484,11 +484,9 @@ export default function RegisterPage() {
Already have an account?{' '}
-
-
- Sign in
-
-
+
+ Sign in
+
diff --git a/maternal-web/app/page.tsx b/maternal-web/app/page.tsx
index 0693809..58092c9 100644
--- a/maternal-web/app/page.tsx
+++ b/maternal-web/app/page.tsx
@@ -47,6 +47,7 @@ export default function HomePage() {
const [children, setChildren] = useState([]);
const [recentActivities, setRecentActivities] = useState([]);
const [todaySummary, setTodaySummary] = useState(null);
+ const [allChildMetrics, setAllChildMetrics] = useState({});
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
@@ -58,10 +59,21 @@ export default function HomePage() {
}, [familyId, authLoading]);
const loadChildren = async () => {
- if (!familyId) return;
+ if (!familyId) {
+ console.warn('[HomePage] No familyId available, cannot load children');
+ return;
+ }
try {
+ console.log('[HomePage] Loading children for familyId:', familyId);
+
+ // Dispatch to Redux to load children into store
+ await dispatch(fetchChildren(familyId)).unwrap();
+
+ // Also get children for local state
const data = await childrenApi.getChildren(familyId);
+ console.log('[HomePage] Loaded children:', data.map(c => ({ id: c.id, name: c.name, familyId: c.familyId })));
+
setChildren(data);
if (data.length > 0 && !selectedChildId) {
setSelectedChildId(data[0].id);
@@ -72,48 +84,68 @@ export default function HomePage() {
}
};
- // Load dashboard data when child selected
+ // Load dashboard data when children are loaded
useEffect(() => {
- if (selectedChildId) {
+ if (children.length > 0) {
loadDashboardData();
}
- }, [selectedChildId]);
+ }, [children]);
const loadDashboardData = async () => {
- if (!selectedChildId) return;
-
setLoading(true);
try {
- // Load recent activities
- const activities = await trackingApi.getActivities(selectedChildId);
- setRecentActivities(activities.slice(0, 10));
+ // Load activities for ALL children to populate metrics
+ const allMetrics: any = {};
- // Calculate today's summary from activities
- const today = new Date();
- today.setHours(0, 0, 0, 0);
- const todayActivities = activities.filter((a: any) => {
- const activityDate = new Date(a.timestamp || a.startedAt);
- return activityDate >= today;
- });
+ for (const child of children) {
+ try {
+ const activities = await trackingApi.getActivities(child.id);
- const summary = {
- feedingCount: todayActivities.filter((a: any) => a.type === 'feeding').length,
- sleepCount: todayActivities.filter((a: any) => a.type === 'sleep').length,
- diaperCount: todayActivities.filter((a: any) => a.type === 'diaper').length,
- medicationCount: todayActivities.filter((a: any) => ['medication', 'medicine'].includes(a.type)).length,
- totalFeedingAmount: todayActivities
- .filter((a: any) => a.type === 'feeding')
- .reduce((sum: number, a: any) => sum + (a.data?.amount || 0), 0),
- totalSleepDuration: todayActivities
- .filter((a: any) => a.type === 'sleep')
- .reduce((sum: number, a: any) => {
- const start = new Date(a.startedAt);
- const end = a.endedAt ? new Date(a.endedAt) : new Date();
- return sum + (end.getTime() - start.getTime()) / (1000 * 60);
- }, 0),
- };
+ // Calculate today's summary from activities
+ const today = new Date();
+ today.setHours(0, 0, 0, 0);
+ const todayActivities = activities.filter((a: any) => {
+ const activityDate = new Date(a.timestamp || a.startedAt);
+ return activityDate >= today;
+ });
- setTodaySummary(summary);
+ allMetrics[child.id] = {
+ feedingCount: todayActivities.filter((a: any) => a.type === 'feeding').length,
+ sleepCount: todayActivities.filter((a: any) => a.type === 'sleep').length,
+ diaperCount: todayActivities.filter((a: any) => a.type === 'diaper').length,
+ medicationCount: todayActivities.filter((a: any) => ['medication', 'medicine'].includes(a.type)).length,
+ totalFeedingAmount: todayActivities
+ .filter((a: any) => a.type === 'feeding')
+ .reduce((sum: number, a: any) => sum + (a.data?.amount || a.metadata?.amount || 0), 0),
+ totalSleepDuration: todayActivities
+ .filter((a: any) => a.type === 'sleep')
+ .reduce((sum: number, a: any) => {
+ const start = new Date(a.startedAt);
+ const end = a.endedAt ? new Date(a.endedAt) : new Date();
+ return sum + (end.getTime() - start.getTime()) / (1000 * 60);
+ }, 0),
+ };
+
+ // If this is the selected child, also set recent activities
+ if (child.id === selectedChildId) {
+ setRecentActivities(activities.slice(0, 10));
+ setTodaySummary(allMetrics[child.id]);
+ }
+ } catch (err) {
+ console.error(`Failed to load activities for child ${child.id}:`, err);
+ allMetrics[child.id] = {
+ feedingCount: 0,
+ sleepCount: 0,
+ diaperCount: 0,
+ medicationCount: 0,
+ totalFeedingAmount: 0,
+ totalSleepDuration: 0,
+ };
+ }
+ }
+
+ // Store all metrics in state
+ setAllChildMetrics(allMetrics);
setError(null);
} catch (err) {
console.error('Failed to load dashboard data:', err);
@@ -188,15 +220,15 @@ export default function HomePage() {
const isLoading = authLoading || loading;
- // Build child metrics object for DynamicChildDashboard
+ // Build child metrics object for DynamicChildDashboard from allChildMetrics
const childMetrics = children.reduce((acc: any, child: any) => {
- // Use todaySummary for selected child, or zero for others
- if (child.id === selectedChildId && todaySummary) {
+ const metrics = allChildMetrics[child.id];
+ if (metrics) {
acc[child.id] = {
- feedingCount: todaySummary.feedingCount || 0,
- sleepDuration: todaySummary.totalSleepDuration || 0,
- diaperCount: todaySummary.diaperCount || 0,
- medicationCount: todaySummary.medicationCount || 0,
+ feedingCount: metrics.feedingCount || 0,
+ sleepDuration: metrics.totalSleepDuration || 0,
+ diaperCount: metrics.diaperCount || 0,
+ medicationCount: metrics.medicationCount || 0,
};
} else {
acc[child.id] = {
diff --git a/maternal-web/components/dashboard/DynamicChildDashboard.tsx b/maternal-web/components/dashboard/DynamicChildDashboard.tsx
index b591458..cf0a858 100644
--- a/maternal-web/components/dashboard/DynamicChildDashboard.tsx
+++ b/maternal-web/components/dashboard/DynamicChildDashboard.tsx
@@ -49,19 +49,25 @@ export default function DynamicChildDashboard({
const children = useSelector((state: RootState) => childrenSelectors.selectAll(state));
const viewMode = useSelector(selectViewMode);
const selectedChild = useSelector(selectSelectedChild);
- const [selectedTab, setSelectedTab] = useState('');
+
+ // Initialize selectedTab with first available child ID
+ const [selectedTab, setSelectedTab] = useState(() => {
+ if (selectedChild?.id) return selectedChild.id;
+ if (children.length > 0) return children[0].id;
+ return '';
+ });
// Initialize selected tab
useEffect(() => {
- if (selectedChild?.id) {
+ if (selectedChild?.id && selectedTab !== selectedChild.id) {
setSelectedTab(selectedChild.id);
- } else if (children.length > 0) {
+ } else if (!selectedTab && children.length > 0) {
const firstChildId = children[0].id;
setSelectedTab(firstChildId);
dispatch(selectChild(firstChildId));
onChildSelect(firstChildId);
}
- }, [selectedChild, children, dispatch, onChildSelect]);
+ }, [selectedChild, children, dispatch, onChildSelect, selectedTab]);
const handleTabChange = (_event: React.SyntheticEvent, newValue: string) => {
setSelectedTab(newValue);
@@ -180,6 +186,11 @@ export default function DynamicChildDashboard({
// Tab view for 1-3 children
if (viewMode === 'tabs') {
+ // Don't render tabs if no children or no valid selectedTab
+ if (children.length === 0 || !selectedTab) {
+ return null;
+ }
+
return (
{
label=""
value="voice"
showLabel={false}
- icon={
- {
+ e.stopPropagation();
+ const voiceButton = document.querySelector('[aria-label="voice input"]') as HTMLButtonElement;
+ if (voiceButton) {
+ voiceButton.click();
+ }
+ }}
+ icon={(
+ {
- e.stopPropagation();
- const voiceButton = document.querySelector('[aria-label="voice input"]') as HTMLButtonElement;
- if (voiceButton) {
- voiceButton.click();
- }
- }}
sx={{
bgcolor: '#FF69B4',
color: 'white',
@@ -89,11 +89,15 @@ export const TabBar = () => {
'&:hover': {
bgcolor: '#FF1493',
},
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ borderRadius: '50%',
}}
>
-
- }
+
+ )}
sx={{ minWidth: 80 }}
/>
);
diff --git a/maternal-web/lib/api/client.ts b/maternal-web/lib/api/client.ts
index 022d75a..e1c5307 100644
--- a/maternal-web/lib/api/client.ts
+++ b/maternal-web/lib/api/client.ts
@@ -42,19 +42,35 @@ apiClient.interceptors.response.use(
try {
const refreshToken = tokenStorage.getRefreshToken();
+ const deviceId = tokenStorage.getDeviceId();
+
+ console.log('[API Client] Attempting token refresh, refreshToken exists:', !!refreshToken, 'deviceId exists:', !!deviceId);
+
if (!refreshToken) {
+ console.error('[API Client] No refresh token found in storage');
throw new Error('No refresh token');
}
- const response = await axios.post(
+ if (!deviceId) {
+ console.error('[API Client] No device ID found in storage');
+ throw new Error('No device ID');
+ }
+
+ // Use a plain axios instance without interceptors to avoid loops
+ const refreshResponse = await axios.create().post(
`${API_BASE_URL}/api/v1/auth/refresh`,
- { refreshToken },
+ {
+ refreshToken,
+ deviceId
+ },
{
headers: { 'Content-Type': 'application/json' },
withCredentials: true
}
);
+ const response = refreshResponse;
+
// Handle different response structures
let newAccessToken;
let newRefreshToken;
@@ -81,13 +97,28 @@ apiClient.interceptors.response.use(
// Retry original request with new token
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
return apiClient(originalRequest);
- } catch (refreshError) {
- console.error('Token refresh failed:', refreshError);
- // Refresh failed, clear tokens and redirect to login
- tokenStorage.clearTokens();
+ } catch (refreshError: any) {
+ console.error('[API Client] Token refresh failed:', refreshError);
+
+ // Only clear tokens if this is a real auth failure (not a network error)
+ // and not during the initial page load where React Strict Mode might cause issues
+ const isAuthFailure = refreshError?.response?.status === 401 ||
+ refreshError?.response?.status === 403;
+
+ // Check if this is likely a React Strict Mode double-invocation
+ // by seeing if we're in development mode and the error happened very quickly
+ const isDevelopment = process.env.NODE_ENV === 'development';
+
+ if (isAuthFailure && !isDevelopment) {
+ console.log('[API Client] Auth failure in production, clearing tokens');
+ tokenStorage.clearTokens();
+ } else if (isDevelopment) {
+ console.log('[API Client] Development mode - not clearing tokens to avoid React Strict Mode issues');
+ }
// Avoid redirect loop - only redirect if not already on login page
- if (!window.location.pathname.includes('/login')) {
+ // and only in production or after a real auth failure
+ if (!window.location.pathname.includes('/login') && isAuthFailure && !isDevelopment) {
window.location.href = '/login';
}
return Promise.reject(refreshError);
diff --git a/maternal-web/lib/auth/AuthContext.tsx b/maternal-web/lib/auth/AuthContext.tsx
index 5974e2f..88ed1f7 100644
--- a/maternal-web/lib/auth/AuthContext.tsx
+++ b/maternal-web/lib/auth/AuthContext.tsx
@@ -76,13 +76,58 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
try {
const accessToken = tokenStorage.getAccessToken();
- if (!accessToken) {
+ const refreshToken = tokenStorage.getRefreshToken();
+
+ console.log('[AuthContext] checkAuth - tokens present:', {
+ hasAccess: !!accessToken,
+ hasRefresh: !!refreshToken
+ });
+
+ if (!accessToken && !refreshToken) {
+ console.log('[AuthContext] No tokens found, user not authenticated');
+ setUser(null);
+ setToken(null);
setIsLoading(false);
return;
}
- // Set token in state
- setToken(accessToken);
+ // If we only have refresh token but no access token, don't make /me call
+ // The axios interceptor will handle getting a new access token when needed
+ if (!accessToken && refreshToken) {
+ console.log('[AuthContext] Only refresh token present, skipping /me call');
+ setUser(null);
+ setToken(null);
+ setIsLoading(false);
+ return;
+ }
+
+ // If we only have access token but no refresh token, it might be a timing issue
+ // during login (React Strict Mode). Give it a moment and check again.
+ if (accessToken && !refreshToken) {
+ console.log('[AuthContext] Only access token present, checking if this is temporary...');
+
+ // Wait a tiny bit and check again (for React Strict Mode race condition)
+ await new Promise(resolve => setTimeout(resolve, 10));
+ const refreshTokenRetry = tokenStorage.getRefreshToken();
+
+ if (!refreshTokenRetry) {
+ console.log('[AuthContext] Still no refresh token after retry, clearing invalid state');
+ tokenStorage.clearTokens();
+ setUser(null);
+ setToken(null);
+ setIsLoading(false);
+ return;
+ } else {
+ console.log('[AuthContext] Refresh token found on retry, proceeding with auth check');
+ // Update local variable for the rest of the function
+ }
+ }
+
+ // At this point we have both tokens - proceed with auth check
+ // Set token in state if we have one
+ if (accessToken) {
+ setToken(accessToken);
+ }
const response = await apiClient.get('/api/v1/auth/me');
@@ -104,9 +149,28 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
throw new Error('Invalid response structure');
}
} catch (error: any) {
- console.error('Auth check failed:', error);
- // Only clear tokens if it's an actual auth error (401, 403)
- if (error?.response?.status === 401 || error?.response?.status === 403) {
+ console.error('[AuthContext] Auth check failed:', error);
+ // Don't clear tokens on 401 during initial auth check
+ // The axios interceptor in client.ts will handle token refresh automatically
+ // Only clear tokens if the error is NOT a 401 (e.g., network error, 403, etc.)
+ // Or if there's no refresh token available (meaning refresh already failed)
+ const hasRefreshToken = tokenStorage.getRefreshToken();
+
+ if (!hasRefreshToken) {
+ // No refresh token means we can't recover - clear everything
+ console.log('[AuthContext] No refresh token available, clearing auth state');
+ tokenStorage.clearTokens();
+ setUser(null);
+ setToken(null);
+ } else if (error?.response?.status === 401) {
+ // 401 with refresh token - let axios interceptor handle refresh
+ console.log('[AuthContext] 401 error but refresh token exists, letting axios interceptor handle refresh');
+ // Don't clear tokens - the axios interceptor will attempt refresh
+ setUser(null);
+ setToken(null);
+ } else if (error?.response?.status === 403) {
+ // 403 means forbidden - clear tokens
+ console.log('[AuthContext] 403 Forbidden, clearing auth state');
tokenStorage.clearTokens();
setUser(null);
setToken(null);
@@ -118,8 +182,9 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
const login = async (credentials: LoginCredentials) => {
try {
+ const deviceId = generateDeviceFingerprint();
const deviceInfo = {
- deviceId: generateDeviceFingerprint(),
+ deviceId,
platform: 'web',
model: navigator.userAgent,
osVersion: navigator.platform,
@@ -141,7 +206,8 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
eulaVersion: userData.eulaVersion,
});
- tokenStorage.setTokens(tokens.accessToken, tokens.refreshToken);
+ // Store tokens and deviceId
+ tokenStorage.setTokens(tokens.accessToken, tokens.refreshToken, deviceId);
setToken(tokens.accessToken);
setUser(userData);
@@ -154,8 +220,9 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
const register = async (data: RegisterData) => {
try {
+ const deviceId = generateDeviceFingerprint();
const deviceInfo = {
- deviceId: generateDeviceFingerprint(),
+ deviceId,
platform: 'web',
model: navigator.userAgent,
osVersion: navigator.platform,
@@ -205,7 +272,7 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
}] : [],
};
- tokenStorage.setTokens(accessToken, refreshToken);
+ tokenStorage.setTokens(accessToken, refreshToken, deviceId);
setToken(accessToken);
setUser(userWithFamily);
diff --git a/maternal-web/lib/utils/tokenStorage.ts b/maternal-web/lib/utils/tokenStorage.ts
index 5a23e71..b8b58f6 100644
--- a/maternal-web/lib/utils/tokenStorage.ts
+++ b/maternal-web/lib/utils/tokenStorage.ts
@@ -61,12 +61,44 @@ export const tokenStorage = {
}
},
+ /**
+ * Get device ID from storage
+ */
+ getDeviceId: (): string | null => {
+ if (typeof window === 'undefined') {
+ return null;
+ }
+ try {
+ return localStorage.getItem('deviceId');
+ } catch (error) {
+ console.error('Error reading deviceId:', error);
+ return null;
+ }
+ },
+
+ /**
+ * Set device ID in storage
+ */
+ setDeviceId: (deviceId: string): void => {
+ if (typeof window === 'undefined') {
+ return;
+ }
+ try {
+ localStorage.setItem('deviceId', deviceId);
+ } catch (error) {
+ console.error('Error setting deviceId:', error);
+ }
+ },
+
/**
* Set both tokens at once
*/
- setTokens: (accessToken: string, refreshToken: string): void => {
+ setTokens: (accessToken: string, refreshToken: string, deviceId?: string): void => {
tokenStorage.setAccessToken(accessToken);
tokenStorage.setRefreshToken(refreshToken);
+ if (deviceId) {
+ tokenStorage.setDeviceId(deviceId);
+ }
},
/**
@@ -79,6 +111,7 @@ export const tokenStorage = {
try {
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
+ localStorage.removeItem('deviceId');
} catch (error) {
console.error('Error clearing tokens:', error);
}
diff --git a/maternal-web/public/sw.js b/maternal-web/public/sw.js
index 54aadff..ee01af9 100644
--- a/maternal-web/public/sw.js
+++ b/maternal-web/public/sw.js
@@ -1 +1 @@
-if(!self.define){let e,a={};const s=(s,c)=>(s=new URL(s+".js",c).href,a[s]||new Promise(a=>{if("document"in self){const e=document.createElement("script");e.src=s,e.onload=a,document.head.appendChild(e)}else e=s,importScripts(s),a()}).then(()=>{let e=a[s];if(!e)throw new Error(`Module ${s} didn’t register its module`);return e}));self.define=(c,i)=>{const n=e||("document"in self?document.currentScript.src:"")||location.href;if(a[n])return;let t={};const r=e=>s(e,n),f={module:{uri:n},exports:t,require:r};a[n]=Promise.all(c.map(e=>f[e]||r(e))).then(e=>(i(...e),t))}}define(["./workbox-4d767a27"],function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_next/app-build-manifest.json",revision:"215a4943065f695ea24ed66e6c76c5af"},{url:"/_next/static/S6HVlen5VK32mY-2C8b-v/_buildManifest.js",revision:"003849461a7dd4bad470c5d1d5e5254c"},{url:"/_next/static/S6HVlen5VK32mY-2C8b-v/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/_next/static/chunks/1091.c762d795c6885f94.js",revision:"c762d795c6885f94"},{url:"/_next/static/chunks/1197.b7511947a737e909.js",revision:"b7511947a737e909"},{url:"/_next/static/chunks/1255-b2f7fd83e387a9e1.js",revision:"b2f7fd83e387a9e1"},{url:"/_next/static/chunks/1280-296e0a2b6e9dd9b1.js",revision:"296e0a2b6e9dd9b1"},{url:"/_next/static/chunks/1514-a6ed8a01b9885870.js",revision:"a6ed8a01b9885870"},{url:"/_next/static/chunks/1543-530e0f57f7af68aa.js",revision:"530e0f57f7af68aa"},{url:"/_next/static/chunks/1569-55e6fb5a18f58b21.js",revision:"55e6fb5a18f58b21"},{url:"/_next/static/chunks/1863-7231108310f72246.js",revision:"7231108310f72246"},{url:"/_next/static/chunks/189-acf917e753c20aa8.js",revision:"acf917e753c20aa8"},{url:"/_next/static/chunks/2262-26293d6453fcc927.js",revision:"26293d6453fcc927"},{url:"/_next/static/chunks/2506-b6cf4a8108dc7c86.js",revision:"b6cf4a8108dc7c86"},{url:"/_next/static/chunks/2619-04bc32f026a0d946.js",revision:"04bc32f026a0d946"},{url:"/_next/static/chunks/3039-0e9bf08230c8ee7b.js",revision:"0e9bf08230c8ee7b"},{url:"/_next/static/chunks/3127-49a95e7cb556ace3.js",revision:"49a95e7cb556ace3"},{url:"/_next/static/chunks/3183-823e6c5b74284106.js",revision:"823e6c5b74284106"},{url:"/_next/static/chunks/340-ce44e06c2ff738ed.js",revision:"ce44e06c2ff738ed"},{url:"/_next/static/chunks/3664-56dedfcaec4aaceb.js",revision:"56dedfcaec4aaceb"},{url:"/_next/static/chunks/3762-c2c13ecf11b3eabb.js",revision:"c2c13ecf11b3eabb"},{url:"/_next/static/chunks/4241-f94f398f62924b88.js",revision:"f94f398f62924b88"},{url:"/_next/static/chunks/4337-7b7eba57ddf5f8f1.js",revision:"7b7eba57ddf5f8f1"},{url:"/_next/static/chunks/4bd1b696-100b9d70ed4e49c1.js",revision:"100b9d70ed4e49c1"},{url:"/_next/static/chunks/5125-c990fc036d2a6ce4.js",revision:"c990fc036d2a6ce4"},{url:"/_next/static/chunks/525-9faf3d762ffa7508.js",revision:"9faf3d762ffa7508"},{url:"/_next/static/chunks/5380-9004e1ac3565daca.js",revision:"9004e1ac3565daca"},{url:"/_next/static/chunks/5385-7ecda8e4ba984edc.js",revision:"7ecda8e4ba984edc"},{url:"/_next/static/chunks/5482-7535aa0aab02d518.js",revision:"7535aa0aab02d518"},{url:"/_next/static/chunks/5761-e5b2862a3b7d7efa.js",revision:"e5b2862a3b7d7efa"},{url:"/_next/static/chunks/6088-c165c565edce02be.js",revision:"c165c565edce02be"},{url:"/_next/static/chunks/670-a4ca0f366ee779f5.js",revision:"a4ca0f366ee779f5"},{url:"/_next/static/chunks/6733-52673e6aade9c963.js",revision:"52673e6aade9c963"},{url:"/_next/static/chunks/6873-ff265086321345c8.js",revision:"ff265086321345c8"},{url:"/_next/static/chunks/6886-40f1779ffff00d58.js",revision:"40f1779ffff00d58"},{url:"/_next/static/chunks/710-7e96cbf5d461482a.js",revision:"7e96cbf5d461482a"},{url:"/_next/static/chunks/7359-1abfb9f346309354.js",revision:"1abfb9f346309354"},{url:"/_next/static/chunks/7741-0af8b5a61d8e63d3.js",revision:"0af8b5a61d8e63d3"},{url:"/_next/static/chunks/7855-72c79224370eff7b.js",revision:"72c79224370eff7b"},{url:"/_next/static/chunks/787-032067ae978e62a8.js",revision:"032067ae978e62a8"},{url:"/_next/static/chunks/8221-d51102291d5ddaf9.js",revision:"d51102291d5ddaf9"},{url:"/_next/static/chunks/8241-eaf1b9c6054e9ad8.js",revision:"eaf1b9c6054e9ad8"},{url:"/_next/static/chunks/8412-8ce7440f3599e2d9.js",revision:"8ce7440f3599e2d9"},{url:"/_next/static/chunks/8466-ffa71cea7998f777.js",revision:"ffa71cea7998f777"},{url:"/_next/static/chunks/8544.74f59dd908783038.js",revision:"74f59dd908783038"},{url:"/_next/static/chunks/8746-92ff3ad56eb06d6e.js",revision:"92ff3ad56eb06d6e"},{url:"/_next/static/chunks/8863-7b43165e8b5cae38.js",revision:"7b43165e8b5cae38"},{url:"/_next/static/chunks/9205-f540995b767df00b.js",revision:"f540995b767df00b"},{url:"/_next/static/chunks/9333-b4ce9ef429de942e.js",revision:"b4ce9ef429de942e"},{url:"/_next/static/chunks/9392-2887c5e5703ed90a.js",revision:"2887c5e5703ed90a"},{url:"/_next/static/chunks/9397-40b8ac68e22a4d87.js",revision:"40b8ac68e22a4d87"},{url:"/_next/static/chunks/9515-53e74005e71810bd.js",revision:"53e74005e71810bd"},{url:"/_next/static/chunks/9517-17518b5fffe76114.js",revision:"17518b5fffe76114"},{url:"/_next/static/chunks/9738-c70b13d86cc3ea77.js",revision:"c70b13d86cc3ea77"},{url:"/_next/static/chunks/9958.962493a298c38a17.js",revision:"962493a298c38a17"},{url:"/_next/static/chunks/app/(auth)/forgot-password/page-294fb4d9f0014755.js",revision:"294fb4d9f0014755"},{url:"/_next/static/chunks/app/(auth)/login/page-d8f5656a139ee55d.js",revision:"d8f5656a139ee55d"},{url:"/_next/static/chunks/app/(auth)/onboarding/page-ea1c1fa93f2bff9a.js",revision:"ea1c1fa93f2bff9a"},{url:"/_next/static/chunks/app/(auth)/register/page-4b000eea571750ea.js",revision:"4b000eea571750ea"},{url:"/_next/static/chunks/app/(auth)/reset-password/page-836751db40b617b5.js",revision:"836751db40b617b5"},{url:"/_next/static/chunks/app/_not-found/page-95f11f5fe94340f1.js",revision:"95f11f5fe94340f1"},{url:"/_next/static/chunks/app/ai-assistant/page-e34646713b4eca94.js",revision:"e34646713b4eca94"},{url:"/_next/static/chunks/app/analytics/page-7a6b08f18da0121a.js",revision:"7a6b08f18da0121a"},{url:"/_next/static/chunks/app/api/ai/chat/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/api/auth/login/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/api/auth/password-reset/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/api/auth/register/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/api/health/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/api/tracking/feeding/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/api/voice/transcribe/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/children/page-b6fb68596ba09b7a.js",revision:"b6fb68596ba09b7a"},{url:"/_next/static/chunks/app/family/page-3bf65f3ee77dd58f.js",revision:"3bf65f3ee77dd58f"},{url:"/_next/static/chunks/app/history/page-db18505f04d26741.js",revision:"db18505f04d26741"},{url:"/_next/static/chunks/app/insights/page-605c43713d69ed1e.js",revision:"605c43713d69ed1e"},{url:"/_next/static/chunks/app/layout-e9ae6b6360689f26.js",revision:"e9ae6b6360689f26"},{url:"/_next/static/chunks/app/legal/cookies/page-c39a3fa6e27a8806.js",revision:"c39a3fa6e27a8806"},{url:"/_next/static/chunks/app/legal/eula/page-8015f749ab4dd660.js",revision:"8015f749ab4dd660"},{url:"/_next/static/chunks/app/legal/page-3de074f0b9741bc6.js",revision:"3de074f0b9741bc6"},{url:"/_next/static/chunks/app/legal/privacy/page-3cb58024b6fd8e21.js",revision:"3cb58024b6fd8e21"},{url:"/_next/static/chunks/app/legal/terms/page-b5a1c96cae251767.js",revision:"b5a1c96cae251767"},{url:"/_next/static/chunks/app/logout/page-359b0e371fd55c32.js",revision:"359b0e371fd55c32"},{url:"/_next/static/chunks/app/offline/page-28c005360c2b2736.js",revision:"28c005360c2b2736"},{url:"/_next/static/chunks/app/page-9946ecd9dac0d22f.js",revision:"9946ecd9dac0d22f"},{url:"/_next/static/chunks/app/settings/page-3f8776db67351a35.js",revision:"3f8776db67351a35"},{url:"/_next/static/chunks/app/track/activity/page-2307107edcbf16b6.js",revision:"2307107edcbf16b6"},{url:"/_next/static/chunks/app/track/diaper/page-a39423216f7058a8.js",revision:"a39423216f7058a8"},{url:"/_next/static/chunks/app/track/feeding/page-45257beac2eb2bc3.js",revision:"45257beac2eb2bc3"},{url:"/_next/static/chunks/app/track/growth/page-207ac7ed192d7b4a.js",revision:"207ac7ed192d7b4a"},{url:"/_next/static/chunks/app/track/medicine/page-9e2aaabd12c4ea8f.js",revision:"9e2aaabd12c4ea8f"},{url:"/_next/static/chunks/app/track/page-dd5ade1eb19ad389.js",revision:"dd5ade1eb19ad389"},{url:"/_next/static/chunks/app/track/sleep/page-bc6d6b16af0f9b5e.js",revision:"bc6d6b16af0f9b5e"},{url:"/_next/static/chunks/framework-bd61ec64032c2de7.js",revision:"bd61ec64032c2de7"},{url:"/_next/static/chunks/main-520e5ec2d671abe7.js",revision:"520e5ec2d671abe7"},{url:"/_next/static/chunks/main-app-02fc3649960ba6c7.js",revision:"02fc3649960ba6c7"},{url:"/_next/static/chunks/pages/_app-4b3fb5e477a0267f.js",revision:"4b3fb5e477a0267f"},{url:"/_next/static/chunks/pages/_error-c970d8b55ace1b48.js",revision:"c970d8b55ace1b48"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-cd699ef02cde1c76.js",revision:"cd699ef02cde1c76"},{url:"/_next/static/css/2eb0f1dfbb62d2c0.css",revision:"2eb0f1dfbb62d2c0"},{url:"/_next/static/media/19cfc7226ec3afaa-s.woff2",revision:"9dda5cfc9a46f256d0e131bb535e46f8"},{url:"/_next/static/media/21350d82a1f187e9-s.woff2",revision:"4e2553027f1d60eff32898367dd4d541"},{url:"/_next/static/media/8e9860b6e62d6359-s.woff2",revision:"01ba6c2a184b8cba08b0d57167664d75"},{url:"/_next/static/media/ba9851c3c22cd980-s.woff2",revision:"9e494903d6b0ffec1a1e14d34427d44d"},{url:"/_next/static/media/c5fe6dc8356a8c31-s.woff2",revision:"027a89e9ab733a145db70f09b8a18b42"},{url:"/_next/static/media/df0a9ae256c0569c-s.woff2",revision:"d54db44de5ccb18886ece2fda72bdfe0"},{url:"/_next/static/media/e4af272ccee01ff0-s.p.woff2",revision:"65850a373e258f1c897a2b3d75eb74de"},{url:"/apple-touch-icon.png",revision:"fa2d4d791b90148a18d49bc3bfd7a43a"},{url:"/check-updates.js",revision:"bc016a0ceb6c72a5fe9ba02ad05d78be"},{url:"/favicon-16x16.png",revision:"db2da3355c89a6149f6d9ee35ebe6bf3"},{url:"/favicon-32x32.png",revision:"0fd88d56aa584bd0546d05ffc63ef777"},{url:"/icon-192x192.png",revision:"b8ef7f117472c4399cceffea644eb8bd"},{url:"/icons/icon-128x128.png",revision:"96cff3b189d9c1daa1edf470290a90cd"},{url:"/icons/icon-144x144.png",revision:"b627c346c431d7e306005aec5f51baff"},{url:"/icons/icon-152x152.png",revision:"012071830c13d310e51f833baed531af"},{url:"/icons/icon-192x192.png",revision:"dfb20132ddb628237eccd4b0e2ee4aaa"},{url:"/icons/icon-384x384.png",revision:"d032b25376232878a2a29b5688992a8d"},{url:"/icons/icon-512x512.png",revision:"ffda0043571d60956f4e321cba706670"},{url:"/icons/icon-72x72.png",revision:"cc89e74126e7e1109f0186774b3c0d77"},{url:"/icons/icon-96x96.png",revision:"32813cdad5b636fc09eec01c7d705936"},{url:"/manifest.json",revision:"5cbf1ecd33b05c4772688ce7d00c2c23"},{url:"/next.svg",revision:"8e061864f388b47f33a1c3780831193e"},{url:"/vercel.svg",revision:"61c6b19abff40ea7acd577be818f3976"}],{ignoreURLParametersMatching:[]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({request:e,response:a,event:s,state:c})=>a&&"opaqueredirect"===a.type?new Response(a.body,{status:200,statusText:"OK",headers:a.headers}):a}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/api\/.*$/i,new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/.*/i,new e.NetworkFirst({cacheName:"others",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET")});
+if(!self.define){let e,a={};const s=(s,c)=>(s=new URL(s+".js",c).href,a[s]||new Promise(a=>{if("document"in self){const e=document.createElement("script");e.src=s,e.onload=a,document.head.appendChild(e)}else e=s,importScripts(s),a()}).then(()=>{let e=a[s];if(!e)throw new Error(`Module ${s} didn’t register its module`);return e}));self.define=(c,i)=>{const n=e||("document"in self?document.currentScript.src:"")||location.href;if(a[n])return;let t={};const r=e=>s(e,n),f={module:{uri:n},exports:t,require:r};a[n]=Promise.all(c.map(e=>f[e]||r(e))).then(e=>(i(...e),t))}}define(["./workbox-4d767a27"],function(e){"use strict";importScripts(),self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"/_next/app-build-manifest.json",revision:"2b3cb35ea252002ccf2dc5f5b26bc330"},{url:"/_next/static/O-Rs9FsvedxX8COyH-JBk/_buildManifest.js",revision:"003849461a7dd4bad470c5d1d5e5254c"},{url:"/_next/static/O-Rs9FsvedxX8COyH-JBk/_ssgManifest.js",revision:"b6652df95db52feb4daf4eca35380933"},{url:"/_next/static/chunks/1091.c762d795c6885f94.js",revision:"c762d795c6885f94"},{url:"/_next/static/chunks/1197.b7511947a737e909.js",revision:"b7511947a737e909"},{url:"/_next/static/chunks/1255-b2f7fd83e387a9e1.js",revision:"b2f7fd83e387a9e1"},{url:"/_next/static/chunks/1280-296e0a2b6e9dd9b1.js",revision:"296e0a2b6e9dd9b1"},{url:"/_next/static/chunks/1514-a6ed8a01b9885870.js",revision:"a6ed8a01b9885870"},{url:"/_next/static/chunks/1543-530e0f57f7af68aa.js",revision:"530e0f57f7af68aa"},{url:"/_next/static/chunks/1569-55e6fb5a18f58b21.js",revision:"55e6fb5a18f58b21"},{url:"/_next/static/chunks/1863-7231108310f72246.js",revision:"7231108310f72246"},{url:"/_next/static/chunks/189-acf917e753c20aa8.js",revision:"acf917e753c20aa8"},{url:"/_next/static/chunks/2262-26293d6453fcc927.js",revision:"26293d6453fcc927"},{url:"/_next/static/chunks/2506-b6cf4a8108dc7c86.js",revision:"b6cf4a8108dc7c86"},{url:"/_next/static/chunks/2619-04bc32f026a0d946.js",revision:"04bc32f026a0d946"},{url:"/_next/static/chunks/3039-0e9bf08230c8ee7b.js",revision:"0e9bf08230c8ee7b"},{url:"/_next/static/chunks/3127-49a95e7cb556ace3.js",revision:"49a95e7cb556ace3"},{url:"/_next/static/chunks/3183-823e6c5b74284106.js",revision:"823e6c5b74284106"},{url:"/_next/static/chunks/340-ce44e06c2ff738ed.js",revision:"ce44e06c2ff738ed"},{url:"/_next/static/chunks/3664-56dedfcaec4aaceb.js",revision:"56dedfcaec4aaceb"},{url:"/_next/static/chunks/3762-c2c13ecf11b3eabb.js",revision:"c2c13ecf11b3eabb"},{url:"/_next/static/chunks/4241-f94f398f62924b88.js",revision:"f94f398f62924b88"},{url:"/_next/static/chunks/4337-7b7eba57ddf5f8f1.js",revision:"7b7eba57ddf5f8f1"},{url:"/_next/static/chunks/4bd1b696-100b9d70ed4e49c1.js",revision:"100b9d70ed4e49c1"},{url:"/_next/static/chunks/5125-c990fc036d2a6ce4.js",revision:"c990fc036d2a6ce4"},{url:"/_next/static/chunks/525-9faf3d762ffa7508.js",revision:"9faf3d762ffa7508"},{url:"/_next/static/chunks/5380-9004e1ac3565daca.js",revision:"9004e1ac3565daca"},{url:"/_next/static/chunks/5385-7ecda8e4ba984edc.js",revision:"7ecda8e4ba984edc"},{url:"/_next/static/chunks/5482-7535aa0aab02d518.js",revision:"7535aa0aab02d518"},{url:"/_next/static/chunks/5761-e5b2862a3b7d7efa.js",revision:"e5b2862a3b7d7efa"},{url:"/_next/static/chunks/6088-c165c565edce02be.js",revision:"c165c565edce02be"},{url:"/_next/static/chunks/670-a4ca0f366ee779f5.js",revision:"a4ca0f366ee779f5"},{url:"/_next/static/chunks/6733-52673e6aade9c963.js",revision:"52673e6aade9c963"},{url:"/_next/static/chunks/6873-ff265086321345c8.js",revision:"ff265086321345c8"},{url:"/_next/static/chunks/6886-40f1779ffff00d58.js",revision:"40f1779ffff00d58"},{url:"/_next/static/chunks/710-7e96cbf5d461482a.js",revision:"7e96cbf5d461482a"},{url:"/_next/static/chunks/7359-1abfb9f346309354.js",revision:"1abfb9f346309354"},{url:"/_next/static/chunks/7741-0af8b5a61d8e63d3.js",revision:"0af8b5a61d8e63d3"},{url:"/_next/static/chunks/7855-72c79224370eff7b.js",revision:"72c79224370eff7b"},{url:"/_next/static/chunks/787-032067ae978e62a8.js",revision:"032067ae978e62a8"},{url:"/_next/static/chunks/8221-d51102291d5ddaf9.js",revision:"d51102291d5ddaf9"},{url:"/_next/static/chunks/8241-eaf1b9c6054e9ad8.js",revision:"eaf1b9c6054e9ad8"},{url:"/_next/static/chunks/8412-8ce7440f3599e2d9.js",revision:"8ce7440f3599e2d9"},{url:"/_next/static/chunks/8466-ffa71cea7998f777.js",revision:"ffa71cea7998f777"},{url:"/_next/static/chunks/8544.74f59dd908783038.js",revision:"74f59dd908783038"},{url:"/_next/static/chunks/8746-92ff3ad56eb06d6e.js",revision:"92ff3ad56eb06d6e"},{url:"/_next/static/chunks/8863-7b43165e8b5cae38.js",revision:"7b43165e8b5cae38"},{url:"/_next/static/chunks/9205-f540995b767df00b.js",revision:"f540995b767df00b"},{url:"/_next/static/chunks/9333-b4ce9ef429de942e.js",revision:"b4ce9ef429de942e"},{url:"/_next/static/chunks/9392-2887c5e5703ed90a.js",revision:"2887c5e5703ed90a"},{url:"/_next/static/chunks/9397-40b8ac68e22a4d87.js",revision:"40b8ac68e22a4d87"},{url:"/_next/static/chunks/9515-53e74005e71810bd.js",revision:"53e74005e71810bd"},{url:"/_next/static/chunks/9517-17518b5fffe76114.js",revision:"17518b5fffe76114"},{url:"/_next/static/chunks/9738-c70b13d86cc3ea77.js",revision:"c70b13d86cc3ea77"},{url:"/_next/static/chunks/9958.962493a298c38a17.js",revision:"962493a298c38a17"},{url:"/_next/static/chunks/app/(auth)/forgot-password/page-294fb4d9f0014755.js",revision:"294fb4d9f0014755"},{url:"/_next/static/chunks/app/(auth)/login/page-d8f5656a139ee55d.js",revision:"d8f5656a139ee55d"},{url:"/_next/static/chunks/app/(auth)/onboarding/page-ea1c1fa93f2bff9a.js",revision:"ea1c1fa93f2bff9a"},{url:"/_next/static/chunks/app/(auth)/register/page-4b000eea571750ea.js",revision:"4b000eea571750ea"},{url:"/_next/static/chunks/app/(auth)/reset-password/page-836751db40b617b5.js",revision:"836751db40b617b5"},{url:"/_next/static/chunks/app/_not-found/page-95f11f5fe94340f1.js",revision:"95f11f5fe94340f1"},{url:"/_next/static/chunks/app/ai-assistant/page-e34646713b4eca94.js",revision:"e34646713b4eca94"},{url:"/_next/static/chunks/app/analytics/page-7a6b08f18da0121a.js",revision:"7a6b08f18da0121a"},{url:"/_next/static/chunks/app/api/ai/chat/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/api/auth/login/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/api/auth/password-reset/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/api/auth/register/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/api/health/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/api/tracking/feeding/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/api/voice/transcribe/route-a631d97a33877f8a.js",revision:"a631d97a33877f8a"},{url:"/_next/static/chunks/app/children/page-b6fb68596ba09b7a.js",revision:"b6fb68596ba09b7a"},{url:"/_next/static/chunks/app/family/page-3bf65f3ee77dd58f.js",revision:"3bf65f3ee77dd58f"},{url:"/_next/static/chunks/app/history/page-db18505f04d26741.js",revision:"db18505f04d26741"},{url:"/_next/static/chunks/app/insights/page-605c43713d69ed1e.js",revision:"605c43713d69ed1e"},{url:"/_next/static/chunks/app/layout-e9ae6b6360689f26.js",revision:"e9ae6b6360689f26"},{url:"/_next/static/chunks/app/legal/cookies/page-c39a3fa6e27a8806.js",revision:"c39a3fa6e27a8806"},{url:"/_next/static/chunks/app/legal/eula/page-8015f749ab4dd660.js",revision:"8015f749ab4dd660"},{url:"/_next/static/chunks/app/legal/page-3de074f0b9741bc6.js",revision:"3de074f0b9741bc6"},{url:"/_next/static/chunks/app/legal/privacy/page-3cb58024b6fd8e21.js",revision:"3cb58024b6fd8e21"},{url:"/_next/static/chunks/app/legal/terms/page-b5a1c96cae251767.js",revision:"b5a1c96cae251767"},{url:"/_next/static/chunks/app/logout/page-359b0e371fd55c32.js",revision:"359b0e371fd55c32"},{url:"/_next/static/chunks/app/offline/page-28c005360c2b2736.js",revision:"28c005360c2b2736"},{url:"/_next/static/chunks/app/page-6db54b61da7c6316.js",revision:"6db54b61da7c6316"},{url:"/_next/static/chunks/app/settings/page-3f8776db67351a35.js",revision:"3f8776db67351a35"},{url:"/_next/static/chunks/app/track/activity/page-2307107edcbf16b6.js",revision:"2307107edcbf16b6"},{url:"/_next/static/chunks/app/track/diaper/page-a39423216f7058a8.js",revision:"a39423216f7058a8"},{url:"/_next/static/chunks/app/track/feeding/page-45257beac2eb2bc3.js",revision:"45257beac2eb2bc3"},{url:"/_next/static/chunks/app/track/growth/page-207ac7ed192d7b4a.js",revision:"207ac7ed192d7b4a"},{url:"/_next/static/chunks/app/track/medicine/page-9e2aaabd12c4ea8f.js",revision:"9e2aaabd12c4ea8f"},{url:"/_next/static/chunks/app/track/page-dd5ade1eb19ad389.js",revision:"dd5ade1eb19ad389"},{url:"/_next/static/chunks/app/track/sleep/page-bc6d6b16af0f9b5e.js",revision:"bc6d6b16af0f9b5e"},{url:"/_next/static/chunks/framework-bd61ec64032c2de7.js",revision:"bd61ec64032c2de7"},{url:"/_next/static/chunks/main-520e5ec2d671abe7.js",revision:"520e5ec2d671abe7"},{url:"/_next/static/chunks/main-app-02fc3649960ba6c7.js",revision:"02fc3649960ba6c7"},{url:"/_next/static/chunks/pages/_app-4b3fb5e477a0267f.js",revision:"4b3fb5e477a0267f"},{url:"/_next/static/chunks/pages/_error-c970d8b55ace1b48.js",revision:"c970d8b55ace1b48"},{url:"/_next/static/chunks/polyfills-42372ed130431b0a.js",revision:"846118c33b2c0e922d7b3a7676f81f6f"},{url:"/_next/static/chunks/webpack-cd699ef02cde1c76.js",revision:"cd699ef02cde1c76"},{url:"/_next/static/css/2eb0f1dfbb62d2c0.css",revision:"2eb0f1dfbb62d2c0"},{url:"/_next/static/media/19cfc7226ec3afaa-s.woff2",revision:"9dda5cfc9a46f256d0e131bb535e46f8"},{url:"/_next/static/media/21350d82a1f187e9-s.woff2",revision:"4e2553027f1d60eff32898367dd4d541"},{url:"/_next/static/media/8e9860b6e62d6359-s.woff2",revision:"01ba6c2a184b8cba08b0d57167664d75"},{url:"/_next/static/media/ba9851c3c22cd980-s.woff2",revision:"9e494903d6b0ffec1a1e14d34427d44d"},{url:"/_next/static/media/c5fe6dc8356a8c31-s.woff2",revision:"027a89e9ab733a145db70f09b8a18b42"},{url:"/_next/static/media/df0a9ae256c0569c-s.woff2",revision:"d54db44de5ccb18886ece2fda72bdfe0"},{url:"/_next/static/media/e4af272ccee01ff0-s.p.woff2",revision:"65850a373e258f1c897a2b3d75eb74de"},{url:"/apple-touch-icon.png",revision:"fa2d4d791b90148a18d49bc3bfd7a43a"},{url:"/check-updates.js",revision:"bc016a0ceb6c72a5fe9ba02ad05d78be"},{url:"/favicon-16x16.png",revision:"db2da3355c89a6149f6d9ee35ebe6bf3"},{url:"/favicon-32x32.png",revision:"0fd88d56aa584bd0546d05ffc63ef777"},{url:"/icon-192x192.png",revision:"b8ef7f117472c4399cceffea644eb8bd"},{url:"/icons/icon-128x128.png",revision:"96cff3b189d9c1daa1edf470290a90cd"},{url:"/icons/icon-144x144.png",revision:"b627c346c431d7e306005aec5f51baff"},{url:"/icons/icon-152x152.png",revision:"012071830c13d310e51f833baed531af"},{url:"/icons/icon-192x192.png",revision:"dfb20132ddb628237eccd4b0e2ee4aaa"},{url:"/icons/icon-384x384.png",revision:"d032b25376232878a2a29b5688992a8d"},{url:"/icons/icon-512x512.png",revision:"ffda0043571d60956f4e321cba706670"},{url:"/icons/icon-72x72.png",revision:"cc89e74126e7e1109f0186774b3c0d77"},{url:"/icons/icon-96x96.png",revision:"32813cdad5b636fc09eec01c7d705936"},{url:"/manifest.json",revision:"5cbf1ecd33b05c4772688ce7d00c2c23"},{url:"/next.svg",revision:"8e061864f388b47f33a1c3780831193e"},{url:"/vercel.svg",revision:"61c6b19abff40ea7acd577be818f3976"}],{ignoreURLParametersMatching:[]}),e.cleanupOutdatedCaches(),e.registerRoute("/",new e.NetworkFirst({cacheName:"start-url",plugins:[{cacheWillUpdate:async({request:e,response:a,event:s,state:c})=>a&&"opaqueredirect"===a.type?new Response(a.body,{status:200,statusText:"OK",headers:a.headers}):a}]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:gstatic)\.com\/.*/i,new e.CacheFirst({cacheName:"google-fonts-webfonts",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:31536e3})]}),"GET"),e.registerRoute(/^https:\/\/fonts\.(?:googleapis)\.com\/.*/i,new e.StaleWhileRevalidate({cacheName:"google-fonts-stylesheets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:eot|otf|ttc|ttf|woff|woff2|font.css)$/i,new e.StaleWhileRevalidate({cacheName:"static-font-assets",plugins:[new e.ExpirationPlugin({maxEntries:4,maxAgeSeconds:604800})]}),"GET"),e.registerRoute(/\.(?:jpg|jpeg|gif|png|svg|ico|webp)$/i,new e.StaleWhileRevalidate({cacheName:"static-image-assets",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/image\?url=.+$/i,new e.StaleWhileRevalidate({cacheName:"next-image",plugins:[new e.ExpirationPlugin({maxEntries:64,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp3|wav|ogg)$/i,new e.CacheFirst({cacheName:"static-audio-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:mp4)$/i,new e.CacheFirst({cacheName:"static-video-assets",plugins:[new e.RangeRequestsPlugin,new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:js)$/i,new e.StaleWhileRevalidate({cacheName:"static-js-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\.(?:css|less)$/i,new e.StaleWhileRevalidate({cacheName:"static-style-assets",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/_next\/data\/.+\/.+\.json$/i,new e.StaleWhileRevalidate({cacheName:"next-data",plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/\/api\/.*$/i,new e.NetworkFirst({cacheName:"apis",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:16,maxAgeSeconds:86400})]}),"GET"),e.registerRoute(/.*/i,new e.NetworkFirst({cacheName:"others",networkTimeoutSeconds:10,plugins:[new e.ExpirationPlugin({maxEntries:32,maxAgeSeconds:86400})]}),"GET")});
diff --git a/maternal-web/store/slices/childrenSlice.ts b/maternal-web/store/slices/childrenSlice.ts
index 965cd40..acf86ab 100644
--- a/maternal-web/store/slices/childrenSlice.ts
+++ b/maternal-web/store/slices/childrenSlice.ts
@@ -91,45 +91,18 @@ interface ChildrenState {
lastSyncTime: string | null;
}
-// Mock children for development (matches real data from andrei@cloudz.ro)
-const MOCK_CHILDREN: Child[] = process.env.NODE_ENV === 'development' ? [
- {
- id: 'child_alice123',
- familyId: 'fam_test123',
- name: 'Alice',
- birthDate: '2023-03-15',
- gender: 'female',
- displayColor: '#FF6B9D',
- sortOrder: 1,
- createdAt: new Date().toISOString(),
- },
- {
- id: 'child_bob456',
- familyId: 'fam_test123',
- name: 'Bob',
- birthDate: '2024-06-20',
- gender: 'male',
- displayColor: '#4A90E2',
- sortOrder: 2,
- createdAt: new Date().toISOString(),
- },
-] : [];
-
const childrenSlice = createSlice({
name: 'children',
- initialState: childrenAdapter.getInitialState(
- {
- loading: false,
- error: null,
- selectedChildId: MOCK_CHILDREN.length > 0 ? MOCK_CHILDREN[0].id : null,
- selectedChildIds: MOCK_CHILDREN.length > 0 ? [MOCK_CHILDREN[0].id] : [],
- defaultChildId: MOCK_CHILDREN.length > 0 ? MOCK_CHILDREN[0].id : null,
- viewMode: 'auto',
- lastSelectedPerScreen: {},
- lastSyncTime: null,
- },
- MOCK_CHILDREN
- ),
+ initialState: childrenAdapter.getInitialState({
+ loading: false,
+ error: null,
+ selectedChildId: null,
+ selectedChildIds: [],
+ defaultChildId: null,
+ viewMode: 'auto',
+ lastSelectedPerScreen: {},
+ lastSyncTime: null,
+ }),
reducers: {
// Optimistic operations
optimisticCreate: (state, action: PayloadAction) => {
diff --git a/maternal-web/styles/themes/purpleTheme.ts b/maternal-web/styles/themes/purpleTheme.ts
index e5926ec..2ef872c 100644
--- a/maternal-web/styles/themes/purpleTheme.ts
+++ b/maternal-web/styles/themes/purpleTheme.ts
@@ -6,9 +6,10 @@ import { createTheme } from '@mui/material/styles';
export const purpleTheme = createTheme({
palette: {
primary: {
- main: '#8b52ff', // Vibrant purple
- light: '#d194e6', // Light purple
- dark: '#6b3cc9', // Dark purple
+ main: '#7c3aed', // Adjusted purple for better contrast (WCAG AA compliant)
+ light: '#a78bfa', // Light purple
+ dark: '#5b21b6', // Dark purple
+ contrastText: '#ffffff', // Ensure white text
},
secondary: {
main: '#ff7094', // Pink