# Web Frontend Implementation Plan - Maternal Organization App
## Mobile-First Progressive Web Application

---

## Executive Summary

This document outlines the implementation plan for building the Maternal Organization App as a **mobile-first responsive web application** that will serve as the foundation for the native mobile apps. The web app will be built using **Next.js 14** with **TypeScript**, implementing all core features while maintaining a native app-like experience through PWA capabilities.

### Key Principles
- **Mobile-first design** with responsive scaling for tablets and desktop
- **PWA features** for offline support and app-like experience
- **Component reusability** for future React Native migration
- **Touch-optimized** interactions and gestures
- **Performance-first** with sub-2-second load times

---

## Technology Stack

### Core Framework
```javascript
// Package versions for consistency
{
  "next": "^14.2.0",
  "react": "^18.3.0",
  "react-dom": "^18.3.0",
  "typescript": "^5.4.0"
}
```

### UI Framework & Styling
```javascript
{
  // Material UI for consistent Material Design
  "@mui/material": "^5.15.0",
  "@mui/icons-material": "^5.15.0",
  "@emotion/react": "^11.11.0",
  "@emotion/styled": "^11.11.0",
  
  // Tailwind for utility classes
  "tailwindcss": "^3.4.0",
  
  // Animation libraries
  "framer-motion": "^11.0.0",
  "react-spring": "^9.7.0"
}
```

### State Management & Data
```javascript
{
  // Redux Toolkit for state management
  "@reduxjs/toolkit": "^2.2.0",
  "react-redux": "^9.1.0",
  "redux-persist": "^6.0.0",
  
  // API and real-time
  "@tanstack/react-query": "^5.28.0",
  "socket.io-client": "^4.7.0",
  "axios": "^1.6.0",
  
  // Forms and validation
  "react-hook-form": "^7.51.0",
  "zod": "^3.22.0"
}
```

### PWA & Performance
```javascript
{
  "workbox-webpack-plugin": "^7.0.0",
  "next-pwa": "^5.6.0",
  "@sentry/nextjs": "^7.100.0",
  "web-vitals": "^3.5.0"
}
```

---

## Project Structure

```
maternal-web/
├── src/
│   ├── app/                      # Next.js 14 app directory
│   │   ├── (auth)/               # Auth group routes
│   │   │   ├── login/
│   │   │   ├── register/
│   │   │   └── onboarding/
│   │   ├── (dashboard)/          # Protected routes
│   │   │   ├── page.tsx          # Family dashboard
│   │   │   ├── children/
│   │   │   │   ├── [id]/
│   │   │   │   └── new/
│   │   │   ├── track/
│   │   │   │   ├── feeding/
│   │   │   │   ├── sleep/
│   │   │   │   └── diaper/
│   │   │   ├── ai-assistant/
│   │   │   ├── insights/
│   │   │   └── settings/
│   │   ├── api/                  # API routes
│   │   │   └── auth/[...nextauth]/
│   │   ├── layout.tsx
│   │   └── global.css
│   │
│   ├── components/
│   │   ├── ui/                   # Base UI components
│   │   │   ├── Button/
│   │   │   ├── Card/
│   │   │   ├── Input/
│   │   │   ├── Modal/
│   │   │   └── ...
│   │   ├── features/             # Feature-specific components
│   │   │   ├── tracking/
│   │   │   ├── ai-chat/
│   │   │   ├── family/
│   │   │   └── analytics/
│   │   ├── layouts/
│   │   │   ├── MobileNav/
│   │   │   ├── TabBar/
│   │   │   └── AppShell/
│   │   └── common/
│   │       ├── ErrorBoundary/
│   │       ├── LoadingStates/
│   │       └── OfflineIndicator/
│   │
│   ├── hooks/
│   │   ├── useVoiceInput.ts
│   │   ├── useOfflineSync.ts
│   │   ├── useRealtime.ts
│   │   └── useMediaQuery.ts
│   │
│   ├── lib/
│   │   ├── api/
│   │   ├── websocket/
│   │   ├── storage/
│   │   └── utils/
│   │
│   ├── store/                   # Redux store
│   │   ├── slices/
│   │   ├── middleware/
│   │   └── store.ts
│   │
│   ├── styles/
│   │   ├── themes/
│   │   └── globals.css
│   │
│   └── types/
│
├── public/
│   ├── manifest.json
│   ├── service-worker.js
│   └── icons/
│
└── tests/
```

---

## Phase 1: Foundation & Setup (Week 1)

### 1.1 Project Initialization

```bash
# Create Next.js project with TypeScript
npx create-next-app@latest maternal-web --typescript --tailwind --app

# Install core dependencies
cd maternal-web
npm install @mui/material @emotion/react @emotion/styled
npm install @reduxjs/toolkit react-redux redux-persist
npm install @tanstack/react-query axios socket.io-client
npm install react-hook-form zod
npm install framer-motion
```

### 1.2 PWA Configuration

```javascript
// next.config.js
const withPWA = require('next-pwa')({
  dest: 'public',
  register: true,
  skipWaiting: true,
  disable: process.env.NODE_ENV === 'development',
  runtimeCaching: [
    {
      urlPattern: /^https?.*/,
      handler: 'NetworkFirst',
      options: {
        cacheName: 'offlineCache',
        expiration: {
          maxEntries: 200,
        },
      },
    },
  ],
});

module.exports = withPWA({
  reactStrictMode: true,
  images: {
    domains: ['api.maternalapp.com'],
  },
});
```

### 1.3 Theme Configuration

```typescript
// src/styles/themes/maternalTheme.ts
import { createTheme } from '@mui/material/styles';

export const maternalTheme = createTheme({
  palette: {
    primary: {
      main: '#FFB6C1', // Light pink/rose
      light: '#FFE4E1', // Misty rose
      dark: '#DB7093', // Pale violet red
    },
    secondary: {
      main: '#FFDAB9', // Peach puff
      light: '#FFE5CC',
      dark: '#FFB347', // Deep peach
    },
    background: {
      default: '#FFF9F5', // Warm white
      paper: '#FFFFFF',
    },
  },
  typography: {
    fontFamily: '"Inter", "Roboto", "Helvetica", "Arial", sans-serif',
    h1: {
      fontSize: '2rem',
      fontWeight: 600,
    },
  },
  shape: {
    borderRadius: 16,
  },
  components: {
    MuiButton: {
      styleOverrides: {
        root: {
          borderRadius: 24,
          textTransform: 'none',
          minHeight: 48, // Touch target size
          fontSize: '1rem',
          fontWeight: 500,
        },
      },
    },
    MuiTextField: {
      styleOverrides: {
        root: {
          '& .MuiInputBase-root': {
            minHeight: 48,
          },
        },
      },
    },
  },
});
```

### 1.4 Mobile-First Layout Component

```typescript
// src/components/layouts/AppShell/AppShell.tsx
import { useState, useEffect } from 'react';
import { Box, Container } from '@mui/material';
import { MobileNav } from '../MobileNav';
import { TabBar } from '../TabBar';
import { useMediaQuery } from '@/hooks/useMediaQuery';

export const AppShell: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const isMobile = useMediaQuery('(max-width: 768px)');
  const isTablet = useMediaQuery('(max-width: 1024px)');
  
  return (
    <Box sx={{ 
      display: 'flex', 
      flexDirection: 'column', 
      minHeight: '100vh',
      pb: isMobile ? '56px' : 0, // Space for tab bar
    }}>
      {!isMobile && <MobileNav />}
      
      <Container 
        maxWidth={isTablet ? 'md' : 'lg'}
        sx={{ 
          flex: 1, 
          px: isMobile ? 2 : 3,
          py: 3,
        }}
      >
        {children}
      </Container>
      
      {isMobile && <TabBar />}
    </Box>
  );
};
```

---

## Phase 2: Authentication & Onboarding (Week 1-2)

### 2.1 Auth Provider Setup

```typescript
// src/lib/auth/AuthContext.tsx
import { createContext, useContext, useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import { api } from '@/lib/api';

interface AuthContextType {
  user: User | null;
  login: (credentials: LoginCredentials) => Promise<void>;
  register: (data: RegisterData) => Promise<void>;
  logout: () => void;
  isLoading: boolean;
}

export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const [user, setUser] = useState<User | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const router = useRouter();

  useEffect(() => {
    // Check for stored token and validate
    const checkAuth = async () => {
      const token = localStorage.getItem('accessToken');
      if (token) {
        try {
          const response = await api.get('/auth/me');
          setUser(response.data);
        } catch {
          localStorage.removeItem('accessToken');
          localStorage.removeItem('refreshToken');
        }
      }
      setIsLoading(false);
    };
    
    checkAuth();
  }, []);

  // Implementation continues...
};
```

### 2.2 Mobile-Optimized Auth Screens

```typescript
// src/app/(auth)/login/page.tsx
'use client';

import { useState } from 'react';
import { 
  Box, 
  TextField, 
  Button, 
  Typography, 
  Paper,
  InputAdornment,
  IconButton,
  Divider
} from '@mui/material';
import { Visibility, VisibilityOff, Google, Apple } from '@mui/icons-material';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { motion } from 'framer-motion';
import * as z from 'zod';

const loginSchema = z.object({
  email: z.string().email('Invalid email'),
  password: z.string().min(8, 'Password must be at least 8 characters'),
});

export default function LoginPage() {
  const [showPassword, setShowPassword] = useState(false);
  const { register, handleSubmit, formState: { errors } } = useForm({
    resolver: zodResolver(loginSchema),
  });

  return (
    <Box
      sx={{
        minHeight: '100vh',
        display: 'flex',
        flexDirection: 'column',
        justifyContent: 'center',
        px: 3,
        background: 'linear-gradient(135deg, #FFE4E1 0%, #FFDAB9 100%)',
      }}
    >
      <motion.div
        initial={{ opacity: 0, y: 20 }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ duration: 0.5 }}
      >
        <Paper
          elevation={0}
          sx={{
            p: 4,
            borderRadius: 4,
            maxWidth: 400,
            mx: 'auto',
            background: 'rgba(255, 255, 255, 0.95)',
            backdropFilter: 'blur(10px)',
          }}
        >
          <Typography variant="h4" gutterBottom align="center" fontWeight="600">
            Welcome Back
          </Typography>
          
          <Box component="form" onSubmit={handleSubmit(onSubmit)} sx={{ mt: 3 }}>
            <TextField
              fullWidth
              label="Email"
              type="email"
              margin="normal"
              error={!!errors.email}
              helperText={errors.email?.message}
              {...register('email')}
              InputProps={{
                sx: { borderRadius: 3 }
              }}
            />
            
            <TextField
              fullWidth
              label="Password"
              type={showPassword ? 'text' : 'password'}
              margin="normal"
              error={!!errors.password}
              helperText={errors.password?.message}
              {...register('password')}
              InputProps={{
                sx: { borderRadius: 3 },
                endAdornment: (
                  <InputAdornment position="end">
                    <IconButton onClick={() => setShowPassword(!showPassword)}>
                      {showPassword ? <VisibilityOff /> : <Visibility />}
                    </IconButton>
                  </InputAdornment>
                ),
              }}
            />
            
            <Button
              fullWidth
              type="submit"
              variant="contained"
              size="large"
              sx={{ mt: 3, mb: 2 }}
            >
              Sign In
            </Button>
          </Box>

          <Divider sx={{ my: 3 }}>OR</Divider>

          <Button
            fullWidth
            variant="outlined"
            startIcon={<Google />}
            size="large"
            sx={{ mb: 2 }}
          >
            Continue with Google
          </Button>
          
          <Button
            fullWidth
            variant="outlined"
            startIcon={<Apple />}
            size="large"
          >
            Continue with Apple
          </Button>
        </Paper>
      </motion.div>
    </Box>
  );
}
```

### 2.3 Progressive Onboarding Flow

```typescript
// src/app/(auth)/onboarding/page.tsx
import { useState } from 'react';
import { Stepper, Step, StepLabel } from '@mui/material';
import { WelcomeStep } from '@/components/onboarding/WelcomeStep';
import { AddChildStep } from '@/components/onboarding/AddChildStep';
import { InviteFamilyStep } from '@/components/onboarding/InviteFamilyStep';
import { NotificationStep } from '@/components/onboarding/NotificationStep';

const steps = ['Welcome', 'Add Child', 'Invite Family', 'Notifications'];

export default function OnboardingPage() {
  const [activeStep, setActiveStep] = useState(0);
  
  return (
    <Box sx={{ width: '100%', p: 3 }}>
      {/* Mobile-optimized stepper */}
      <Stepper activeStep={activeStep} alternativeLabel>
        {steps.map((label) => (
          <Step key={label}>
            <StepLabel>{label}</StepLabel>
          </Step>
        ))}
      </Stepper>
      
      <Box sx={{ mt: 4 }}>
        {activeStep === 0 && <WelcomeStep onNext={handleNext} />}
        {activeStep === 1 && <AddChildStep onNext={handleNext} onBack={handleBack} />}
        {activeStep === 2 && <InviteFamilyStep onNext={handleNext} onBack={handleBack} onSkip={handleSkip} />}
        {activeStep === 3 && <NotificationStep onComplete={handleComplete} onBack={handleBack} />}
      </Box>
    </Box>
  );
}
```

---

## Phase 3: Core Tracking Features (Week 2-3)

### 3.1 Quick Action FAB

```typescript
// src/components/features/tracking/QuickActionFAB.tsx
import { useState } from 'react';
import { SpeedDial, SpeedDialAction, SpeedDialIcon } from '@mui/material';
import { 
  Restaurant, 
  Hotel, 
  BabyChangingStation, 
  Mic,
  Add 
} from '@mui/icons-material';
import { motion, AnimatePresence } from 'framer-motion';

export const QuickActionFAB = () => {
  const [open, setOpen] = useState(false);
  
  const actions = [
    { icon: <Restaurant />, name: 'Feeding', color: '#FFB6C1', route: '/track/feeding' },
    { icon: <Hotel />, name: 'Sleep', color: '#B6D7FF', route: '/track/sleep' },
    { icon: <BabyChangingStation />, name: 'Diaper', color: '#FFE4B5', route: '/track/diaper' },
    { icon: <Mic />, name: 'Voice', color: '#E6E6FA', action: 'voice' },
  ];

  return (
    <SpeedDial
      ariaLabel="Quick actions"
      sx={{ 
        position: 'fixed', 
        bottom: 72, // Above tab bar
        right: 16,
        '& .MuiSpeedDial-fab': {
          bgcolor: 'secondary.main',
          width: 64,
          height: 64,
        }
      }}
      icon={<SpeedDialIcon icon={<Add />} />}
      onClose={() => setOpen(false)}
      onOpen={() => setOpen(true)}
      open={open}
    >
      {actions.map((action) => (
        <SpeedDialAction
          key={action.name}
          icon={action.icon}
          tooltipTitle={action.name}
          onClick={() => handleAction(action)}
          sx={{
            bgcolor: action.color,
            '&:hover': {
              bgcolor: action.color,
              transform: 'scale(1.1)',
            }
          }}
        />
      ))}
    </SpeedDial>
  );
};
```

### 3.2 Voice Input Hook

```typescript
// src/hooks/useVoiceInput.ts
import { useState, useEffect, useCallback } from 'react';

interface VoiceInputOptions {
  language?: string;
  continuous?: boolean;
  onResult?: (transcript: string) => void;
  onEnd?: () => void;
}

export const useVoiceInput = (options: VoiceInputOptions = {}) => {
  const [isListening, setIsListening] = useState(false);
  const [transcript, setTranscript] = useState('');
  const [error, setError] = useState<string | null>(null);
  
  const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
  const recognition = new SpeechRecognition();
  
  useEffect(() => {
    recognition.continuous = options.continuous ?? false;
    recognition.interimResults = true;
    recognition.lang = options.language ?? 'en-US';
    
    recognition.onresult = (event) => {
      const current = event.resultIndex;
      const transcript = event.results[current][0].transcript;
      setTranscript(transcript);
      
      if (event.results[current].isFinal) {
        options.onResult?.(transcript);
      }
    };
    
    recognition.onerror = (event) => {
      setError(event.error);
      setIsListening(false);
    };
    
    recognition.onend = () => {
      setIsListening(false);
      options.onEnd?.();
    };
  }, []);
  
  const startListening = useCallback(() => {
    setTranscript('');
    setError(null);
    recognition.start();
    setIsListening(true);
  }, [recognition]);
  
  const stopListening = useCallback(() => {
    recognition.stop();
    setIsListening(false);
  }, [recognition]);
  
  return {
    isListening,
    transcript,
    error,
    startListening,
    stopListening,
  };
};
```

### 3.3 Tracking Form Component

```typescript
// src/components/features/tracking/FeedingTracker.tsx
import { useState } from 'react';
import { 
  Box, 
  Paper, 
  TextField, 
  ToggleButtonGroup, 
  ToggleButton,
  Button,
  Typography,
  Chip,
  IconButton
} from '@mui/material';
import { Timer, Mic, MicOff } from '@mui/icons-material';
import { useVoiceInput } from '@/hooks/useVoiceInput';
import { motion } from 'framer-motion';

export const FeedingTracker = () => {
  const [feedingType, setFeedingType] = useState<'breast' | 'bottle' | 'solid'>('breast');
  const [duration, setDuration] = useState(0);
  const [isTimerRunning, setIsTimerRunning] = useState(false);
  
  const { isListening, transcript, startListening, stopListening } = useVoiceInput({
    onResult: (text) => {
      // Parse voice input for commands
      parseVoiceCommand(text);
    },
  });

  return (
    <motion.div
      initial={{ opacity: 0, scale: 0.95 }}
      animate={{ opacity: 1, scale: 1 }}
      transition={{ duration: 0.3 }}
    >
      <Paper
        elevation={0}
        sx={{
          p: 3,
          borderRadius: 4,
          background: 'linear-gradient(135deg, #FFE4E1 0%, #FFF 100%)',
        }}
      >
        <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
          <Typography variant="h5" fontWeight="600">
            Track Feeding
          </Typography>
          
          <IconButton
            color={isListening ? 'primary' : 'default'}
            onClick={isListening ? stopListening : startListening}
            sx={{
              bgcolor: isListening ? 'primary.light' : 'background.paper',
              animation: isListening ? 'pulse 1.5s infinite' : 'none',
            }}
          >
            {isListening ? <Mic /> : <MicOff />}
          </IconButton>
        </Box>

        <ToggleButtonGroup
          value={feedingType}
          exclusive
          onChange={(e, value) => value && setFeedingType(value)}
          fullWidth
          sx={{ mb: 3 }}
        >
          <ToggleButton value="breast" sx={{ borderRadius: 3 }}>
            Breastfeeding
          </ToggleButton>
          <ToggleButton value="bottle" sx={{ borderRadius: 3 }}>
            Bottle
          </ToggleButton>
          <ToggleButton value="solid" sx={{ borderRadius: 3 }}>
            Solid Food
          </ToggleButton>
        </ToggleButtonGroup>

        {/* Timer component for breastfeeding */}
        {feedingType === 'breast' && (
          <Box sx={{ textAlign: 'center', my: 4 }}>
            <Typography variant="h2" fontWeight="300">
              {formatDuration(duration)}
            </Typography>
            <Button
              variant="contained"
              size="large"
              startIcon={<Timer />}
              onClick={toggleTimer}
              sx={{ mt: 2 }}
            >
              {isTimerRunning ? 'Stop Timer' : 'Start Timer'}
            </Button>
          </Box>
        )}

        {/* Form fields for bottle/solid */}
        {feedingType !== 'breast' && (
          <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
            {/* Additional form fields */}
          </Box>
        )}

        <Button
          fullWidth
          variant="contained"
          size="large"
          sx={{ mt: 3 }}
          onClick={handleSave}
        >
          Save Feeding
        </Button>
      </Paper>
    </motion.div>
  );
};
```

---

## Phase 4: AI Assistant Integration (Week 3-4)

### 4.1 AI Chat Interface

```typescript
// src/components/features/ai-chat/AIChatInterface.tsx
import { useState, useRef, useEffect } from 'react';
import { 
  Box, 
  TextField, 
  IconButton, 
  Paper, 
  Typography,
  Avatar,
  Chip,
  CircularProgress 
} from '@mui/material';
import { Send, Mic, AttachFile } from '@mui/icons-material';
import { motion, AnimatePresence } from 'framer-motion';
import { useAIChat } from '@/hooks/useAIChat';

export const AIChatInterface = () => {
  const [input, setInput] = useState('');
  const [isTyping, setIsTyping] = useState(false);
  const messagesEndRef = useRef<null | HTMLDivElement>(null);
  
  const { messages, sendMessage, isLoading } = useAIChat();
  
  const scrollToBottom = () => {
    messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
  };
  
  useEffect(() => {
    scrollToBottom();
  }, [messages]);

  return (
    <Box sx={{ height: 'calc(100vh - 120px)', display: 'flex', flexDirection: 'column' }}>
      {/* Quick action chips */}
      <Box sx={{ p: 2, display: 'flex', gap: 1, overflowX: 'auto' }}>
        {['Sleep tips', 'Feeding schedule', 'Developmental milestones', 'Emergency'].map((action) => (
          <Chip
            key={action}
            label={action}
            onClick={() => handleQuickAction(action)}
            sx={{ 
              bgcolor: 'background.paper',
              '&:hover': { bgcolor: 'primary.light' }
            }}
          />
        ))}
      </Box>

      {/* Messages */}
      <Box sx={{ flex: 1, overflow: 'auto', p: 2 }}>
        <AnimatePresence>
          {messages.map((message, index) => (
            <motion.div
              key={index}
              initial={{ opacity: 0, y: 20 }}
              animate={{ opacity: 1, y: 0 }}
              transition={{ duration: 0.3 }}
            >
              <Box
                sx={{
                  display: 'flex',
                  justifyContent: message.role === 'user' ? 'flex-end' : 'flex-start',
                  mb: 2,
                }}
              >
                {message.role === 'assistant' && (
                  <Avatar sx={{ bgcolor: 'primary.main', mr: 1 }}>AI</Avatar>
                )}
                
                <Paper
                  sx={{
                    p: 2,
                    maxWidth: '70%',
                    bgcolor: message.role === 'user' ? 'primary.light' : 'background.paper',
                    borderRadius: message.role === 'user' ? '18px 18px 4px 18px' : '18px 18px 18px 4px',
                  }}
                >
                  <Typography variant="body1">{message.content}</Typography>
                  {message.disclaimer && (
                    <Typography variant="caption" color="text.secondary" sx={{ mt: 1, display: 'block' }}>
                      ⚠️ {message.disclaimer}
                    </Typography>
                  )}
                </Paper>
                
                {message.role === 'user' && (
                  <Avatar sx={{ bgcolor: 'secondary.main', ml: 1 }}>U</Avatar>
                )}
              </Box>
            </motion.div>
          ))}
        </AnimatePresence>
        
        {isTyping && (
          <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
            <Avatar sx={{ bgcolor: 'primary.main' }}>AI</Avatar>
            <Box sx={{ display: 'flex', gap: 0.5 }}>
              <CircularProgress size={8} />
              <CircularProgress size={8} />
              <CircularProgress size={8} />
            </Box>
          </Box>
        )}
        
        <div ref={messagesEndRef} />
      </Box>

      {/* Input area */}
      <Paper
        elevation={3}
        sx={{
          p: 2,
          borderRadius: 0,
          borderTop: '1px solid',
          borderColor: 'divider',
        }}
      >
        <Box sx={{ display: 'flex', gap: 1, alignItems: 'flex-end' }}>
          <IconButton>
            <AttachFile />
          </IconButton>
          
          <TextField
            fullWidth
            multiline
            maxRows={4}
            value={input}
            onChange={(e) => setInput(e.target.value)}
            placeholder="Ask about your child's development, sleep, feeding..."
            variant="outlined"
            sx={{
              '& .MuiOutlinedInput-root': {
                borderRadius: 3,
              },
            }}
            onKeyPress={(e) => {
              if (e.key === 'Enter' && !e.shiftKey) {
                e.preventDefault();
                handleSend();
              }
            }}
          />
          
          <IconButton>
            <Mic />
          </IconButton>
          
          <IconButton
            color="primary"
            disabled={!input.trim() || isLoading}
            onClick={handleSend}
          >
            <Send />
          </IconButton>
        </Box>
      </Paper>
    </Box>
  );
};
```

### 4.2 AI Context Provider

```typescript
// src/lib/ai/AIContextProvider.tsx
import { createContext, useContext, useState } from 'react';
import { api } from '@/lib/api';

interface AIContextType {
  childContext: ChildContext[];
  recentActivities: Activity[];
  updateContext: (data: Partial<AIContextType>) => void;
  getRelevantContext: (query: string) => Promise<any>;
}

export const AIContextProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const [childContext, setChildContext] = useState<ChildContext[]>([]);
  const [recentActivities, setRecentActivities] = useState<Activity[]>([]);
  
  const getRelevantContext = async (query: string) => {
    // Implement context prioritization based on query
    const context = {
      children: childContext,
      recentActivities: recentActivities.slice(0, 10),
      timestamp: new Date().toISOString(),
    };
    
    return context;
  };
  
  return (
    <AIContext.Provider value={{
      childContext,
      recentActivities,
      updateContext,
      getRelevantContext,
    }}>
      {children}
    </AIContext.Provider>
  );
};
```

---

## Phase 5: Family Dashboard & Analytics (Week 4-5)

### 5.1 Responsive Dashboard Grid

```typescript
// src/app/(dashboard)/page.tsx
import { Grid, Box } from '@mui/material';
import { ChildCard } from '@/components/features/family/ChildCard';
import { ActivityTimeline } from '@/components/features/tracking/ActivityTimeline';
import { SleepPrediction } from '@/components/features/analytics/SleepPrediction';
import { QuickStats } from '@/components/features/analytics/QuickStats';
import { useMediaQuery } from '@/hooks/useMediaQuery';

export default function DashboardPage() {
  const isMobile = useMediaQuery('(max-width: 768px)');
  const { children, activities } = useDashboardData();
  
  return (
    <Box sx={{ pb: 8 }}>
      {/* Child selector carousel for mobile */}
      {isMobile && (
        <Box sx={{ mb: 3, overflow: 'auto', display: 'flex', gap: 2, pb: 1 }}>
          {children.map((child) => (
            <ChildCard key={child.id} child={child} compact />
          ))}
        </Box>
      )}
      
      <Grid container spacing={3}>
        {/* Desktop child cards */}
        {!isMobile && children.map((child) => (
          <Grid item xs={12} md={6} lg={4} key={child.id}>
            <ChildCard child={child} />
          </Grid>
        ))}
        
        {/* Sleep prediction */}
        <Grid item xs={12} md={6}>
          <SleepPrediction />
        </Grid>
        
        {/* Quick stats */}
        <Grid item xs={12} md={6}>
          <QuickStats />
        </Grid>
        
        {/* Activity timeline */}
        <Grid item xs={12}>
          <ActivityTimeline activities={activities} />
        </Grid>
      </Grid>
    </Box>
  );
}
```

### 5.2 Interactive Charts

```typescript
// src/components/features/analytics/SleepChart.tsx
import { Line } from 'react-chartjs-2';
import { Box, Paper, Typography, ToggleButtonGroup, ToggleButton } from '@mui/material';
import { useState } from 'react';

export const SleepChart = () => {
  const [timeRange, setTimeRange] = useState<'week' | 'month'>('week');
  
  const chartData = {
    labels: generateLabels(timeRange),
    datasets: [
      {
        label: 'Sleep Duration',
        data: sleepData,
        fill: true,
        backgroundColor: 'rgba(182, 215, 255, 0.2)',
        borderColor: '#B6D7FF',
        tension: 0.4,
      },
      {
        label: 'Predicted',
        data: predictedData,
        borderColor: '#FFB6C1',
        borderDash: [5, 5],
        fill: false,
      },
    ],
  };
  
  const options = {
    responsive: true,
    maintainAspectRatio: false,
    plugins: {
      legend: {
        display: false,
      },
      tooltip: {
        mode: 'index',
        intersect: false,
      },
    },
    scales: {
      y: {
        beginAtZero: true,
        grid: {
          display: false,
        },
      },
      x: {
        grid: {
          display: false,
        },
      },
    },
  };
  
  return (
    <Paper sx={{ p: 3, height: '100%' }}>
      <Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2 }}>
        <Typography variant="h6" fontWeight="600">
          Sleep Patterns
        </Typography>
        
        <ToggleButtonGroup
          value={timeRange}
          exclusive
          onChange={(e, value) => value && setTimeRange(value)}
          size="small"
        >
          <ToggleButton value="week">Week</ToggleButton>
          <ToggleButton value="month">Month</ToggleButton>
        </ToggleButtonGroup>
      </Box>
      
      <Box sx={{ height: 300 }}>
        <Line data={chartData} options={options} />
      </Box>
    </Paper>
  );
};
```

---

## Phase 6: Offline Support & PWA Features (Week 5)

### 6.1 Service Worker Setup

```javascript
// public/service-worker.js
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { NetworkFirst, StaleWhileRevalidate, CacheFirst } from 'workbox-strategies';
import { Queue } from 'workbox-background-sync';

// Precache all static assets
precacheAndRoute(self.__WB_MANIFEST);

// Create sync queue for offline requests
const queue = new Queue('maternal-sync-queue', {
  onSync: async ({ queue }) => {
    let entry;
    while ((entry = await queue.shiftRequest())) {
      try {
        await fetch(entry.request);
      } catch (error) {
        await queue.unshiftRequest(entry);
        throw error;
      }
    }
  },
});

// API routes - network first with offline queue
registerRoute(
  /^https:\/\/api\.maternalapp\.com\/api/,
  async (args) => {
    try {
      const response = await new NetworkFirst({
        cacheName: 'api-cache',
        networkTimeoutSeconds: 5,
      }).handle(args);
      return response;
    } catch (error) {
      await queue.pushRequest({ request: args.request });
      return new Response(
        JSON.stringify({ 
          offline: true, 
          message: 'Your request has been queued and will be synced when online' 
        }),
        { headers: { 'Content-Type': 'application/json' } }
      );
    }
  }
);

// Static assets - cache first
registerRoute(
  /\.(?:png|jpg|jpeg|svg|gif|webp)$/,
  new CacheFirst({
    cacheName: 'image-cache',
    plugins: [
      new ExpirationPlugin({
        maxEntries: 100,
        maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
      }),
    ],
  })
);
```

### 6.2 Offline State Management

```typescript
// src/store/slices/offlineSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { REHYDRATE } from 'redux-persist';

interface OfflineState {
  isOnline: boolean;
  pendingActions: PendingAction[];
  lastSyncTime: string | null;
  syncInProgress: boolean;
}

const offlineSlice = createSlice({
  name: 'offline',
  initialState: {
    isOnline: true,
    pendingActions: [],
    lastSyncTime: null,
    syncInProgress: false,
  } as OfflineState,
  reducers: {
    setOnlineStatus: (state, action: PayloadAction<boolean>) => {
      state.isOnline = action.payload;
      if (action.payload && state.pendingActions.length > 0) {
        state.syncInProgress = true;
      }
    },
    addPendingAction: (state, action: PayloadAction<PendingAction>) => {
      state.pendingActions.push({
        ...action.payload,
        id: generateId(),
        timestamp: new Date().toISOString(),
      });
    },
    removePendingAction: (state, action: PayloadAction<string>) => {
      state.pendingActions = state.pendingActions.filter(a => a.id !== action.payload);
    },
    setSyncInProgress: (state, action: PayloadAction<boolean>) => {
      state.syncInProgress = action.payload;
    },
    updateLastSyncTime: (state) => {
      state.lastSyncTime = new Date().toISOString();
    },
  },
  extraReducers: (builder) => {
    builder.addCase(REHYDRATE, (state, action) => {
      // Handle rehydration from localStorage
      if (action.payload) {
        return {
          ...state,
          ...action.payload.offline,
          isOnline: navigator.onLine,
        };
      }
    });
  },
});

export const offlineActions = offlineSlice.actions;
export default offlineSlice.reducer;
```

### 6.3 Offline Indicator Component

```typescript
// src/components/common/OfflineIndicator.tsx
import { Alert, Snackbar, LinearProgress } from '@mui/material';
import { useSelector } from 'react-redux';
import { motion, AnimatePresence } from 'framer-motion';

export const OfflineIndicator = () => {
  const { isOnline, pendingActions, syncInProgress } = useSelector(state => state.offline);
  
  return (
    <AnimatePresence>
      {!isOnline && (
        <motion.div
          initial={{ y: -100, opacity: 0 }}
          animate={{ y: 0, opacity: 1 }}
          exit={{ y: -100, opacity: 0 }}
        >
          <Alert 
            severity="warning" 
            sx={{ 
              position: 'fixed', 
              top: 0, 
              left: 0, 
              right: 0,
              borderRadius: 0,
              zIndex: 9999,
            }}
          >
            You're offline. {pendingActions.length} actions will sync when you're back online.
          </Alert>
        </motion.div>
      )}
      
      {syncInProgress && (
        <LinearProgress 
          sx={{ 
            position: 'fixed', 
            top: 0, 
            left: 0, 
            right: 0,
            zIndex: 9998,
          }} 
        />
      )}
    </AnimatePresence>
  );
};
```

---

## Phase 7: Performance Optimization (Week 6)

### 7.1 Code Splitting & Lazy Loading

```typescript
// src/app/(dashboard)/layout.tsx
import { lazy, Suspense } from 'react';
import { LoadingFallback } from '@/components/common/LoadingFallback';

// Lazy load heavy components
const AIChatInterface = lazy(() => import('@/components/features/ai-chat/AIChatInterface'));
const AnalyticsDashboard = lazy(() => import('@/components/features/analytics/AnalyticsDashboard'));

export default function DashboardLayout({ children }) {
  return (
    <Suspense fallback={<LoadingFallback />}>
      {children}
    </Suspense>
  );
}
```

### 7.2 Image Optimization

```typescript
// src/components/common/OptimizedImage.tsx
import Image from 'next/image';
import { useState } from 'react';
import { Skeleton } from '@mui/material';

export const OptimizedImage = ({ src, alt, ...props }) => {
  const [isLoading, setIsLoading] = useState(true);
  
  return (
    <Box position="relative">
      {isLoading && (
        <Skeleton 
          variant="rectangular" 
          width={props.width} 
          height={props.height}
          sx={{ position: 'absolute', top: 0, left: 0 }}
        />
      )}
      <Image
        src={src}
        alt={alt}
        {...props}
        onLoadingComplete={() => setIsLoading(false)}
        placeholder="blur"
        blurDataURL={generateBlurDataURL()}
      />
    </Box>
  );
};
```

### 7.3 Performance Monitoring

```typescript
// src/lib/performance/monitoring.ts
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';

export const initPerformanceMonitoring = () => {
  // Send metrics to analytics
  const sendToAnalytics = (metric: any) => {
    // Send to your analytics service
    if (window.gtag) {
      window.gtag('event', metric.name, {
        value: Math.round(metric.value),
        metric_id: metric.id,
        metric_value: metric.value,
        metric_delta: metric.delta,
      });
    }
  };
  
  getCLS(sendToAnalytics);
  getFID(sendToAnalytics);
  getFCP(sendToAnalytics);
  getLCP(sendToAnalytics);
  getTTFB(sendToAnalytics);
};
```

---

## Phase 8: Testing & Deployment (Week 6-7)

### 8.1 Testing Setup

```typescript
// jest.config.js
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1',
    '\\.(css|less|scss|sass)$': 'identity-obj-proxy',
  },
  collectCoverageFrom: [
    'src/**/*.{ts,tsx}',
    '!src/**/*.d.ts',
    '!src/**/*.stories.tsx',
  ],
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
      statements: 80,
    },
  },
};
```

### 8.2 E2E Testing with Playwright

```typescript
// tests/e2e/tracking.spec.ts
import { test, expect } from '@playwright/test';

test.describe('Activity Tracking Flow', () => {
  test('should track feeding activity', async ({ page }) => {
    // Login
    await page.goto('/login');
    await page.fill('[name="email"]', 'test@example.com');
    await page.fill('[name="password"]', 'password123');
    await page.click('button[type="submit"]');
    
    // Navigate to tracking
    await page.waitForSelector('[data-testid="quick-action-fab"]');
    await page.click('[data-testid="quick-action-fab"]');
    await page.click('[data-testid="action-feeding"]');
    
    // Fill form
    await page.click('[value="bottle"]');
    await page.fill('[name="amount"]', '120');
    await page.click('[data-testid="save-feeding"]');
    
    // Verify
    await expect(page.locator('[data-testid="success-toast"]')).toBeVisible();
  });
});
```

### 8.3 Deployment Configuration

```yaml
# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
      - run: npm ci
      - run: npm test
      - run: npm run test:e2e

  build-and-deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm ci
      - run: npm run build
      
      # Deploy to Vercel
      - uses: amondnet/vercel-action@v20
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.ORG_ID }}
          vercel-project-id: ${{ secrets.PROJECT_ID }}
          working-directory: ./
```

---

## Migration Path to Mobile

### Component Compatibility Strategy

```typescript
// Shared component interface example
// src/components/shared/Button.tsx

interface ButtonProps {
  variant?: 'contained' | 'outlined' | 'text';
  color?: 'primary' | 'secondary';
  size?: 'small' | 'medium' | 'large';
  onPress?: () => void; // Mobile
  onClick?: () => void; // Web
  children: React.ReactNode;
}

// Web implementation
export const Button: React.FC<ButtonProps> = ({ 
  onClick, 
  onPress, 
  children, 
  ...props 
}) => {
  return (
    <MuiButton onClick={onClick || onPress} {...props}>
      {children}
    </MuiButton>
  );
};

// Future React Native implementation
// export const Button: React.FC<ButtonProps> = ({ 
//   onClick, 
//   onPress, 
//   children, 
//   ...props 
// }) => {
//   return (
//     <TouchableOpacity onPress={onPress || onClick}>
//       <Text>{children}</Text>
//     </TouchableOpacity>
//   );
// };
```

### Shared Business Logic

```typescript
// src/lib/shared/tracking.logic.ts
// This file can be shared between web and mobile

export const calculateFeedingDuration = (start: Date, end: Date): number => {
  return Math.round((end.getTime() - start.getTime()) / 60000);
};

export const validateFeedingData = (data: FeedingData): ValidationResult => {
  const errors = [];
  
  if (!data.type) {
    errors.push('Feeding type is required');
  }
  
  if (data.type === 'bottle' && !data.amount) {
    errors.push('Amount is required for bottle feeding');
  }
  
  return {
    isValid: errors.length === 0,
    errors,
  };
};

export const formatActivityForDisplay = (activity: Activity): DisplayActivity => {
  // Shared formatting logic
  return {
    ...activity,
    displayTime: formatRelativeTime(activity.timestamp),
    icon: getActivityIcon(activity.type),
    color: getActivityColor(activity.type),
  };
};
```

---

## Performance Targets

### Web Vitals Goals
- **LCP** (Largest Contentful Paint): < 2.5s
- **FID** (First Input Delay): < 100ms
- **CLS** (Cumulative Layout Shift): < 0.1
- **TTI** (Time to Interactive): < 3.5s
- **Bundle Size**: < 200KB initial JS

### Mobile Experience Metrics
- Touch target size: minimum 44x44px
- Font size: minimum 16px for body text
- Scroll performance: 60fps
- Offline capability: Full CRUD operations
- Data usage: < 1MB per session average

---

## Security Considerations

### Authentication & Authorization
```typescript
// src/lib/auth/security.ts
export const securityHeaders = {
  'Content-Security-Policy': `
    default-src 'self';
    script-src 'self' 'unsafe-eval' 'unsafe-inline';
    style-src 'self' 'unsafe-inline';
    img-src 'self' data: https:;
    connect-src 'self' https://api.maternalapp.com wss://api.maternalapp.com;
  `,
  'X-Frame-Options': 'DENY',
  'X-Content-Type-Options': 'nosniff',
  'Referrer-Policy': 'strict-origin-when-cross-origin',
  'Permissions-Policy': 'camera=(), microphone=(self), geolocation=()',
};
```

### Data Protection
- Implement field-level encryption for sensitive data
- Use secure HttpOnly cookies for auth tokens
- Sanitize all user inputs
- Implement rate limiting on all API calls
- Regular security audits with OWASP guidelines

---

## Monitoring & Analytics

### Analytics Implementation
```typescript
// src/lib/analytics/index.ts
export const trackEvent = (eventName: string, properties?: any) => {
  // PostHog
  if (window.posthog) {
    window.posthog.capture(eventName, properties);
  }
  
  // Google Analytics
  if (window.gtag) {
    window.gtag('event', eventName, properties);
  }
};

// Usage
trackEvent('feeding_tracked', {
  type: 'bottle',
  amount: 120,
  duration: 15,
});
```

### Error Tracking
```typescript
// src/lib/monitoring/sentry.ts
import * as Sentry from '@sentry/nextjs';

Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
  environment: process.env.NODE_ENV,
  tracesSampleRate: 0.1,
  beforeSend(event, hint) {
    // Filter sensitive data
    if (event.request) {
      delete event.request.cookies;
    }
    return event;
  },
});
```

---

## Launch Checklist

### Pre-Launch
- [ ] Cross-browser testing (Chrome, Safari, Firefox, Edge)
- [ ] Mobile device testing (iOS Safari, Chrome Android)
- [ ] Accessibility audit (WCAG AA compliance)
- [ ] Performance audit (Lighthouse score > 90)
- [ ] Security audit
- [ ] SEO optimization
- [ ] Analytics implementation verified
- [ ] Error tracking configured
- [ ] SSL certificate installed
- [ ] CDN configured
- [ ] Backup system tested

### Post-Launch
- [ ] Monitor error rates
- [ ] Track Core Web Vitals
- [ ] Gather user feedback
- [ ] A/B testing setup
- [ ] Performance monitoring dashboard
- [ ] User behavior analytics
- [ ] Conversion funnel tracking
- [ ] Support system ready

---

## Conclusion

This web-first approach provides a solid foundation for the Maternal Organization App while maintaining the flexibility to expand to native mobile platforms. The progressive web app capabilities ensure users get a native-like experience on mobile devices, while the component architecture and shared business logic will facilitate the eventual migration to React Native.

Key advantages of this approach:
1. **Faster time to market** - Single codebase to maintain initially
2. **Broader reach** - Works on any device with a modern browser
3. **Easy updates** - No app store approval process for web updates
4. **Cost-effective** - Reduced development and maintenance costs
5. **SEO benefits** - Web app can be indexed by search engines
6. **Progressive enhancement** - Can add native features gradually

The architecture is designed to support the future mobile app development by keeping business logic separate and using compatible patterns wherever possible.