# Offline-First Redux Architecture

This directory contains the offline-first Redux store implementation for the Maternal App.

## Architecture Overview

The store is built with:
- **Redux Toolkit** - Modern Redux with less boilerplate
- **@redux-offline/redux-offline** - Offline-first middleware
- **redux-persist** - State persistence to IndexedDB
- **Entity Adapters** - Normalized state management

## Key Features

### 1. Normalized State
All entities (activities, children) are stored in a normalized format with `byId` and `allIds` structures for optimal performance and consistency.

### 2. Optimistic Updates
Actions are immediately applied to the UI, then synced with the server:
```typescript
// Component usage
const dispatch = useAppDispatch();

// Optimistic create - UI updates immediately
dispatch(optimisticCreate(newActivity));
// Actual API call - syncs with server
dispatch(createActivity(newActivity));
```

### 3. Offline Queue
When offline, mutations are queued and automatically synced when connection is restored:
- Automatic exponential backoff retry (1s, 2s, 4s, 8s, 16s)
- Discard after 5 retries or on 4xx errors
- Persisted to IndexedDB

### 4. Conflict Resolution
Multiple strategies available:
- `SERVER_WINS` - Server data takes precedence
- `CLIENT_WINS` - Local data takes precedence
- `LAST_WRITE_WINS` - Compare timestamps (default)
- `MERGE` - Merge local and server data

### 5. Network Detection
- Browser online/offline events
- Periodic server connectivity checks (every 30s)
- Latency-based connection quality (`excellent`, `good`, `poor`, `offline`)

## Directory Structure

```
store/
├── store.ts                    # Main store configuration
├── hooks.ts                    # Custom Redux hooks
├── slices/                     # Redux slices
│   ├── activitiesSlice.ts     # Activities with normalized state
│   ├── childrenSlice.ts       # Children with normalized state
│   ├── networkSlice.ts        # Network status
│   └── offlineSlice.ts        # Offline sync queue
└── middleware/                 # Custom middleware
    ├── offlineMiddleware.ts   # Offline action queuing
    └── syncMiddleware.ts      # Sync and conflict resolution
```

## Usage Examples

### Basic Component Usage

```typescript
import { useAppDispatch, useAppSelector, useIsOnline } from '@/store/hooks';
import { createActivity, optimisticCreate } from '@/store/slices/activitiesSlice';

function TrackingComponent() {
  const dispatch = useAppDispatch();
  const isOnline = useIsOnline();
  const activities = useAppSelector(activitiesSelectors.selectAll);

  const handleAddActivity = async (activity) => {
    // 1. Optimistic update (immediate UI feedback)
    const localId = `temp_${Date.now()}`;
    dispatch(optimisticCreate({ ...activity, id: localId }));

    // 2. Actual API call (will be queued if offline)
    try {
      await dispatch(createActivity(activity));
    } catch (error) {
      // Rollback handled automatically
      console.error('Failed to create activity:', error);
    }
  };

  return (
    <div>
      {!isOnline && <p>You are offline. Changes will sync when you reconnect.</p>}
      {/* ... */}
    </div>
  );
}
```

### Using Custom Hooks

```typescript
import { useOptimisticAction, useSyncStatus, useHasPendingSync } from '@/store/hooks';

function MyComponent() {
  // Check sync status
  const { syncing, pendingCount, lastSync } = useSyncStatus();
  const hasPending = useHasPendingSync();

  // Optimistic action hook (combines optimistic + actual)
  const handleCreate = useOptimisticAction(
    optimisticCreate,
    createActivity
  );

  return (
    <div>
      {syncing && <p>Syncing {pendingCount} changes...</p>}
      <button onClick={() => handleCreate(newActivity)}>
        Add Activity
      </button>
    </div>
  );
}
```

### Network Status Indicator

```typescript
import { NetworkStatusIndicator, NetworkStatusBadge } from '@/components/common/NetworkStatusIndicator';

function AppLayout() {
  return (
    <>
      <NetworkStatusIndicator /> {/* Snackbar at top */}
      <AppBar>
        <NetworkStatusBadge /> {/* Small badge in app bar */}
      </AppBar>
    </>
  );
}
```

### Checking if Entity is Optimistic

```typescript
import { useIsOptimistic } from '@/store/hooks';

function ActivityCard({ activityId }) {
  const isOptimistic = useIsOptimistic('activities', activityId);

  return (
    <Card>
      {isOptimistic && (
        <Chip label="Syncing..." size="small" />
      )}
      {/* ... */}
    </Card>
  );
}
```

## State Shape

```typescript
{
  activities: {
    ids: ['act_1', 'act_2', 'temp_123'],
    entities: {
      'act_1': {
        id: 'act_1',
        childId: 'child_1',
        type: 'feeding',
        timestamp: '2025-10-01T12:00:00Z',
        data: { amount: 120, unit: 'ml' },
        _optimistic: false,
        _version: 1
      },
      'temp_123': {
        id: 'temp_123',
        childId: 'child_1',
        type: 'sleep',
        timestamp: '2025-10-01T14:00:00Z',
        data: { duration: 90 },
        _optimistic: true,  // Not yet synced
        _localId: 'temp_123',
        _version: 1
      }
    },
    loading: false,
    error: null,
    syncStatus: 'synced',
    lastSyncTime: '2025-10-01T14:05:00Z'
  },
  children: { /* similar structure */ },
  network: {
    isOnline: true,
    isConnected: true,
    lastOnlineTime: '2025-10-01T14:00:00Z',
    connectionQuality: 'excellent',
    latency: 45
  },
  offline: {
    isOnline: true,
    pendingActions: [],
    lastSyncTime: '2025-10-01T14:05:00Z',
    syncInProgress: false
  }
}
```

## Metadata Fields

All entities have optional metadata fields for offline support:

- `_optimistic` (boolean) - True if not yet synced with server
- `_localId` (string) - Temporary ID before server assigns real ID
- `_version` (number) - Version counter for conflict detection

## Best Practices

1. **Always use optimistic updates** for write operations (create, update, delete)
2. **Use entity selectors** instead of accessing state directly
3. **Handle offline state** in your UI
4. **Show sync indicators** to users
5. **Test offline scenarios** thoroughly
6. **Use normalized state** for related entities
7. **Implement proper error handling** and rollback logic

## Testing Offline Behavior

### In Chrome DevTools:
1. Open DevTools → Network tab
2. Change "Online" dropdown to "Offline"
3. Try creating/updating activities
4. Go back "Online"
5. Watch automatic sync happen

### Programmatically:
```typescript
// Simulate offline
dispatch(setOnlineStatus(false));

// Simulate online
dispatch(setOnlineStatus(true));
```

## Performance Considerations

- **IndexedDB** used for persistence (faster than localStorage)
- **Entity adapters** provide memoized selectors
- **Normalized state** prevents duplication
- **Selective persistence** (blacklist for sensitive data)
- **Batched updates** when syncing multiple actions

## Security Notes

- Sensitive data can be excluded from persistence using `persistOptions.blacklist`
- Auth tokens should NOT be stored in Redux (use secure cookie or AuthContext)
- Offline queue is cleared on logout
- All API calls still require valid authentication

## Troubleshooting

### Actions not being queued
- Check if action type includes '/create', '/update', or '/delete'
- Ensure `meta.requestId` exists on the action

### Sync not happening
- Check browser console for network errors
- Verify API endpoints are correct
- Check if `isOnline` state is accurate

### State not persisting
- Check browser IndexedDB (DevTools → Application → IndexedDB)
- Verify localforage is configured correctly
- Check for storage quota issues

## Future Enhancements

- [ ] Batch sync for multiple actions
- [ ] Conflict UI for user resolution
- [ ] Sync progress indicators
- [ ] Selective sync (by entity type)
- [ ] Background sync (Service Workers)
- [ ] Delta sync (only changed fields)
