Files
maternal-app/docs/ADMIN_DASHBOARD_IMPLEMENTATION.md
Andrei df7617638a
Some checks failed
ParentFlow CI/CD Pipeline / Backend Tests (push) Has been cancelled
ParentFlow CI/CD Pipeline / Frontend Tests (push) Has been cancelled
ParentFlow CI/CD Pipeline / Security Scanning (push) Has been cancelled
ParentFlow CI/CD Pipeline / Build Docker Images (map[context:maternal-app/maternal-app-backend dockerfile:Dockerfile.production name:backend]) (push) Has been cancelled
ParentFlow CI/CD Pipeline / Build Docker Images (map[context:maternal-web dockerfile:Dockerfile.production name:frontend]) (push) Has been cancelled
ParentFlow CI/CD Pipeline / Deploy to Development (push) Has been cancelled
ParentFlow CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / Build Application (push) Has been cancelled
CI/CD Pipeline / Lint and Test (push) Has been cancelled
CI/CD Pipeline / E2E Tests (push) Has been cancelled
feat: Update admin dashboard plan with microservice architecture and invite codes
- Redesigned as standalone microservice for managing web and mobile apps
- Added invite code registration system with enable/disable toggle
- Service will be deployed on separate server (10.0.0.241)
- Admin service at admin.parentflowapp.com
- Added database schema for invite codes and usage tracking
- Platform-specific code validation (web, iOS, Android)
- Service-to-service authentication for secure communication
- Batch code generation and export functionality
- Analytics for tracking invite code effectiveness

Architecture benefits:
- Centralized control for all ParentFlow platforms
- Independent scaling and deployment
- Better security isolation for admin functions
- Support for future mobile app management

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-06 21:30:23 +00:00

47 KiB

Admin Dashboard Implementation Plan

Created: October 3, 2025 Updated: October 6, 2025 - Separate microservice architecture Status: Planning Phase Priority: High - Multi-tenancy & Administration Estimated Effort: 6-8 weeks (120-160 hours)


📋 Executive Summary

Overview

Create a comprehensive standalone admin service for managing ParentFlow applications across multiple platforms (web, iOS, Android) with role-based access control (RBAC), user management, system monitoring, and configuration. This service will be deployed independently and manage all ParentFlow instances centrally.

Architecture Decision

The admin dashboard will be implemented as a separate microservice to:

  • Manage both web and future mobile applications
  • Scale independently from main application
  • Provide centralized control across all platforms
  • Enable separate deployment and security policies
  • Support multi-tenant administration

Key Features

  1. Invite Code Registration System: Control user registration with invite codes
  2. User Management: CRUD operations, data export, anonymization
  3. Role-Based Access Control: Parent, Guest, Admin roles
  4. Multi-Platform Support: Manage web, iOS, and Android users
  5. Multi-Profile Support: Multiple families and account switching
  6. Analytics Dashboard: Cross-platform usage metrics, AI/voice analytics
  7. System Health Monitoring: Service status across all platforms
  8. LLM Configuration: Model settings, API keys, pricing
  9. Content Management: Legal pages, subscriptions
  10. Email Configuration: Mailgun settings management

Goals

  • Security: Strict admin authentication and audit logging
  • Scalability: Support growing user base
  • Flexibility: Easy configuration updates without code changes
  • Compliance: GDPR/COPPA data management tools

🏗️ Architecture Overview

Microservice Architecture

┌─────────────────────────────────────────────────────────────┐
│                 ParentFlow Admin Service                     │
│                  (Standalone Application)                    │
│                   admin.parentflowapp.com                    │
│                                                               │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐      │
│  │   Invite     │  │   Users      │  │  Analytics   │      │
│  │   Codes      │  │  Management  │  │   Dashboard  │      │
│  └──────────────┘  └──────────────┘  └──────────────┘      │
│                                                               │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐      │
│  │   Platform   │  │    System    │  │     LLM      │      │
│  │   Manager    │  │    Health    │  │   Settings   │      │
│  └──────────────┘  └──────────────┘  └──────────────┘      │
└─────────────────────────────────────────────────────────────┘
                              │
                        Service-to-Service
                          Communication
                              │
        ┌─────────────────────┼─────────────────────┐
        ▼                     ▼                     ▼
┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│   ParentFlow  │     │  ParentFlow  │     │  ParentFlow  │
│      Web      │     │     iOS      │     │   Android    │
│   (NestJS)    │     │   (Swift)    │     │   (Kotlin)   │
├──────────────┤     ├──────────────┤     ├──────────────┤
│ Validates     │     │ Validates    │     │ Validates    │
│ invite codes  │     │ invite codes │     │ invite codes │
│ with admin    │     │ with admin   │     │ with admin   │
│ service       │     │ service      │     │ service      │
└──────────────┘     └──────────────┘     └──────────────┘
        │                     │                     │
        ▼                     ▼                     ▼
┌─────────────────────────────────────────────────────────────┐
│              Shared Database Infrastructure                  │
│                                                               │
│  • users (cross-platform)                                    │
│  • invite_codes (registration control)                       │
│  • platform_sessions (web/ios/android tracking)              │
│  • admin_audit_log (all admin actions)                       │
│  • system_configurations (per-platform settings)             │
└─────────────────────────────────────────────────────────────┘

Admin Service Components

parentflow-admin-service/
├── src/
│   ├── modules/
│   │   ├── invite-codes/        # Invite code generation & validation
│   │   ├── user-management/     # Cross-platform user management
│   │   ├── platform-manager/    # Web/iOS/Android platform configs
│   │   ├── analytics/           # Unified analytics across platforms
│   │   ├── system-health/       # Monitor all platform services
│   │   ├── llm-config/          # AI configuration
│   │   ├── subscriptions/       # Subscription management
│   │   └── audit/               # Audit logging
│   ├── common/
│   │   ├── guards/              # Authentication guards
│   │   ├── interceptors/        # Service communication
│   │   └── decorators/          # Custom decorators
│   └── database/
│       └── migrations/          # Admin-specific migrations
├── admin-ui/                    # React admin dashboard
│   ├── pages/
│   │   ├── invite-codes/        # Manage invite codes
│   │   ├── users/               # User management
│   │   ├── platforms/           # Platform-specific settings
│   │   └── analytics/           # Analytics dashboard
│   └── components/
│       └── shared/              # Shared UI components
└── docker-compose.admin.yml    # Separate deployment

---

## 👥 Role-Based Access Control (RBAC)

### Role Definitions

#### 1. Parent (Default Role)
**Permissions**:
- Full access to their own families
- Create/manage children
- View all family data (activities, analytics)
- Invite guests to their families
- AI chat, voice commands
- Manage profile and settings

**Database**:
```typescript
enum UserRole {
  PARENT = 'parent',
  GUEST = 'guest', 
  ADMIN = 'admin',
}

2. Guest (Limited Access)

Permissions:

  • CAN DO:
    • Add activities (feeding, sleep, diaper, medication) - manual or voice
    • View real-time data for assigned family
    • Receive notifications
  • CANNOT DO:
    • View historical data (>24 hours)
    • View analytics/insights
    • Edit or delete activities (except their own, within 1 hour)
    • Manage children profiles
    • Access AI chat
    • Invite other users
    • Change family settings

Use Cases:

  • Nanny tracking daily activities
  • Grandparents helping during visits
  • Babysitter logging quick updates

3. Admin (Full System Access)

Permissions:

  • Access admin dashboard
  • Manage all users (CRUD, anonymize, export)
  • View system-wide analytics
  • Configure LLM models
  • Manage subscriptions and trials
  • Edit legal pages
  • Configure email settings
  • View system health
  • Audit logs access

Security:

  • Admins CANNOT access user data without explicit audit trail
  • All admin actions logged to admin_audit_log
  • Require 2FA for admin accounts

🎟️ Invite Code Registration System

Overview

Control user registration through invite codes to limit initial app access. This feature allows controlled rollout and beta testing across all platforms (web, iOS, Android).

Features

  • Generate invite codes: Batch or individual generation
  • Code types: Single-use, multi-use, unlimited
  • Expiration dates: Time-limited codes
  • Platform restrictions: Limit codes to specific platforms
  • Usage tracking: See who used which code
  • Enable/Disable: Toggle registration requirement globally

Database Schema

-- Invite codes table
CREATE TABLE invite_codes (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  code VARCHAR(20) UNIQUE NOT NULL, -- e.g., "BETA-2025-X7K9"
  type VARCHAR(20) NOT NULL, -- 'single', 'multi', 'unlimited'
  max_uses INTEGER DEFAULT 1, -- NULL for unlimited
  current_uses INTEGER DEFAULT 0,
  platform_restrictions VARCHAR(20)[], -- ['web', 'ios', 'android'] or NULL for all
  expires_at TIMESTAMP,
  metadata JSONB, -- Custom data: { "campaign": "beta", "referrer": "influencer1" }
  created_by UUID REFERENCES users(id),
  is_active BOOLEAN DEFAULT true,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_invite_codes_code ON invite_codes(code);
CREATE INDEX idx_invite_codes_active ON invite_codes(is_active) WHERE is_active = true;

-- Invite code usage tracking
CREATE TABLE invite_code_usage (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  invite_code_id UUID NOT NULL REFERENCES invite_codes(id),
  used_by_user_id UUID NOT NULL REFERENCES users(id),
  platform VARCHAR(20) NOT NULL, -- 'web', 'ios', 'android'
  device_info JSONB,
  ip_address INET,
  used_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_invite_code_usage_code ON invite_code_usage(invite_code_id);
CREATE INDEX idx_invite_code_usage_user ON invite_code_usage(used_by_user_id);

-- System configuration for registration
CREATE TABLE registration_config (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  require_invite_code BOOLEAN DEFAULT false,
  registration_enabled BOOLEAN DEFAULT true,
  custom_message TEXT, -- Message shown when registration is disabled
  whitelist_domains VARCHAR(255)[], -- Email domains that bypass invite requirement
  updated_by UUID REFERENCES users(id),
  updated_at TIMESTAMP DEFAULT NOW()
);

API Endpoints

Admin Endpoints

// Admin Service API
POST   /api/v1/admin/invite-codes                 // Generate codes
GET    /api/v1/admin/invite-codes                 // List all codes
GET    /api/v1/admin/invite-codes/:id             // Get code details
PATCH  /api/v1/admin/invite-codes/:id             // Update code
DELETE /api/v1/admin/invite-codes/:id             // Deactivate code
GET    /api/v1/admin/invite-codes/:id/usage       // Get usage history

// Batch operations
POST   /api/v1/admin/invite-codes/batch           // Generate multiple codes
POST   /api/v1/admin/invite-codes/export          // Export codes as CSV

// Configuration
GET    /api/v1/admin/registration/config          // Get registration settings
PATCH  /api/v1/admin/registration/config          // Update settings

Application Endpoints (Web/Mobile)

// Public endpoint for all platforms
POST   /api/v1/auth/validate-invite-code
{
  "code": "BETA-2025-X7K9",
  "platform": "ios"
}

// Response
{
  "valid": true,
  "message": "Code accepted",
  "metadata": {
    "campaign": "beta"
  }
}

// Registration endpoint (modified)
POST   /api/v1/auth/register
{
  "email": "user@example.com",
  "password": "...",
  "inviteCode": "BETA-2025-X7K9",  // Required if enabled
  "platform": "web"
}

Admin UI Components

// pages/invite-codes/page.tsx
export default function InviteCodesPage() {
  const [requireInvite, setRequireInvite] = useState(false);

  return (
    <div>
      {/* Global toggle */}
      <Card>
        <h3>Registration Settings</h3>
        <Switch
          label="Require invite code for registration"
          checked={requireInvite}
          onChange={setRequireInvite}
        />
        <Text>Currently: {requireInvite ? 'Invite only' : 'Open registration'}</Text>
      </Card>

      {/* Code generator */}
      <Card>
        <h3>Generate Invite Codes</h3>
        <Form>
          <Input label="Prefix" placeholder="BETA-2025" />
          <Select label="Type">
            <option value="single">Single use</option>
            <option value="multi">Multi-use</option>
            <option value="unlimited">Unlimited</option>
          </Select>
          <Input label="Max uses" type="number" />
          <DatePicker label="Expires at" />
          <MultiSelect label="Platform restrictions">
            <option value="web">Web</option>
            <option value="ios">iOS</option>
            <option value="android">Android</option>
          </MultiSelect>
          <Input label="Quantity" type="number" defaultValue="1" />
          <Button>Generate Codes</Button>
        </Form>
      </Card>

      {/* Codes list */}
      <DataTable
        columns={[
          { key: 'code', label: 'Code' },
          { key: 'type', label: 'Type' },
          { key: 'uses', label: 'Uses', render: (row) => `${row.current_uses}/${row.max_uses || '∞'}` },
          { key: 'platforms', label: 'Platforms' },
          { key: 'expires_at', label: 'Expires' },
          { key: 'actions', label: 'Actions' }
        ]}
        data={inviteCodes}
      />
    </div>
  );
}

Implementation in Main Application

// src/modules/auth/auth.service.ts (ParentFlow Web/API)
@Injectable()
export class AuthService {

  async register(dto: RegisterDto) {
    // Check if invite codes are required
    const config = await this.getRegistrationConfig();

    if (config.requireInviteCode) {
      if (!dto.inviteCode) {
        throw new BadRequestException('Invite code is required for registration');
      }

      // Validate with admin service
      const validation = await this.adminService.validateInviteCode(
        dto.inviteCode,
        dto.platform || 'web'
      );

      if (!validation.valid) {
        throw new BadRequestException(validation.message || 'Invalid invite code');
      }

      // Track usage
      await this.adminService.recordInviteCodeUsage(
        dto.inviteCode,
        userId,
        dto.platform
      );
    }

    // Continue with normal registration...
  }

  private async getRegistrationConfig() {
    // Cache this for performance
    return await this.adminService.getRegistrationConfig();
  }
}

Mobile Integration

// iOS - RegistrationViewController.swift
func validateInviteCode(_ code: String) async throws -> Bool {
    let response = try await AdminAPI.validateInviteCode(
        code: code,
        platform: "ios"
    )
    return response.valid
}

func register() async throws {
    // Check if invite code is required
    if registrationConfig.requireInviteCode {
        guard let inviteCode = inviteCodeTextField.text, !inviteCode.isEmpty else {
            throw RegistrationError.inviteCodeRequired
        }

        // Validate before proceeding
        let isValid = try await validateInviteCode(inviteCode)
        if !isValid {
            throw RegistrationError.invalidInviteCode
        }
    }

    // Continue with registration
}

Analytics & Reporting

Track invite code effectiveness:

  • Conversion rate: Codes generated vs. used
  • Platform distribution: Usage by platform
  • Time to use: Average time from generation to use
  • Referral tracking: Which campaigns/sources are most effective
  • Geographic distribution: Where users are registering from

🗄️ Database Schema Changes

New Tables

1. User Role Enhancement

-- Modify existing users table
ALTER TABLE users 
  ADD COLUMN global_role VARCHAR(20) DEFAULT 'parent',
  ADD COLUMN is_admin BOOLEAN DEFAULT false,
  ADD COLUMN admin_permissions JSONB DEFAULT '[]';

-- Add index for admin queries
CREATE INDEX idx_users_global_role ON users(global_role);
CREATE INDEX idx_users_is_admin ON users(is_admin) WHERE is_admin = true;

2. Family Membership Roles

-- Modify family_memberships table
ALTER TABLE family_memberships
  ADD COLUMN family_role VARCHAR(20) DEFAULT 'parent', -- parent or guest
  ADD COLUMN permissions JSONB DEFAULT '{}',
  ADD COLUMN invited_by UUID REFERENCES users(id),
  ADD COLUMN access_granted_at TIMESTAMP DEFAULT NOW(),
  ADD COLUMN access_expires_at TIMESTAMP NULL;

-- Constraints
ALTER TABLE family_memberships
  ADD CONSTRAINT valid_family_role 
  CHECK (family_role IN ('parent', 'guest'));

3. User Profiles (Multi-Profile Support)

CREATE TABLE user_profiles (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  profile_name VARCHAR(100) NOT NULL,
  profile_type VARCHAR(20) NOT NULL, -- 'primary', 'work', 'personal'
  default_family_id UUID REFERENCES families(id),
  is_active BOOLEAN DEFAULT true,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW(),
  
  UNIQUE(user_id, profile_name)
);

CREATE INDEX idx_user_profiles_user_id ON user_profiles(user_id);

4. Admin Audit Log

CREATE TABLE admin_audit_log (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  admin_user_id UUID NOT NULL REFERENCES users(id),
  action VARCHAR(50) NOT NULL, -- 'user.create', 'user.delete', 'config.update'
  target_entity VARCHAR(50), -- 'user', 'subscription', 'config'
  target_id UUID,
  changes JSONB, -- before/after values
  ip_address INET,
  user_agent TEXT,
  created_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_admin_audit_log_admin_user ON admin_audit_log(admin_user_id);
CREATE INDEX idx_admin_audit_log_created_at ON admin_audit_log(created_at DESC);
CREATE INDEX idx_admin_audit_log_action ON admin_audit_log(action);

5. LLM Configuration

CREATE TABLE llm_config (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  provider VARCHAR(50) NOT NULL, -- 'azure', 'openai', 'anthropic'
  service_type VARCHAR(50) NOT NULL, -- 'chat', 'whisper', 'embeddings'
  endpoint_url TEXT NOT NULL,
  api_key_encrypted TEXT NOT NULL, -- Encrypted with AES-256
  model_name VARCHAR(100) NOT NULL,
  deployment_name VARCHAR(100), -- For Azure
  api_version VARCHAR(20),
  price_per_1m_input_tokens DECIMAL(10, 4), -- e.g., 0.0015 for $1.50/1M
  price_per_1m_output_tokens DECIMAL(10, 4),
  max_tokens INTEGER DEFAULT 4000,
  temperature DECIMAL(3, 2) DEFAULT 0.7,
  is_active BOOLEAN DEFAULT true,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_llm_config_provider_service ON llm_config(provider, service_type);

6. Subscription Plans

CREATE TABLE subscription_plans (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  name VARCHAR(100) NOT NULL UNIQUE,
  description TEXT,
  price_monthly DECIMAL(10, 2),
  price_yearly DECIMAL(10, 2),
  features JSONB, -- Feature flags: { "ai_queries": "unlimited", "voice_commands": 500 }
  max_children INTEGER,
  max_family_members INTEGER,
  ai_query_limit INTEGER, -- NULL = unlimited
  voice_command_limit INTEGER,
  trial_period_days INTEGER DEFAULT 14,
  is_active BOOLEAN DEFAULT true,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

-- Add subscription to users
ALTER TABLE users
  ADD COLUMN subscription_plan_id UUID REFERENCES subscription_plans(id),
  ADD COLUMN subscription_status VARCHAR(20) DEFAULT 'trial', -- trial, active, expired, cancelled
  ADD COLUMN subscription_started_at TIMESTAMP,
  ADD COLUMN subscription_expires_at TIMESTAMP,
  ADD COLUMN trial_ends_at TIMESTAMP;

7. Email Configuration

CREATE TABLE email_config (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  provider VARCHAR(50) DEFAULT 'mailgun',
  region VARCHAR(10) DEFAULT 'US', -- US or EU
  api_key_encrypted TEXT NOT NULL,
  domain VARCHAR(255) NOT NULL,
  sender_email VARCHAR(255) NOT NULL,
  sender_name VARCHAR(255) NOT NULL,
  is_active BOOLEAN DEFAULT true,
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE legal_pages (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  slug VARCHAR(100) NOT NULL UNIQUE, -- 'privacy', 'terms', 'eula', 'cookies'
  title VARCHAR(255) NOT NULL,
  content TEXT NOT NULL, -- Markdown or HTML
  language VARCHAR(5) DEFAULT 'en',
  version INTEGER DEFAULT 1,
  is_published BOOLEAN DEFAULT false,
  last_updated_by UUID REFERENCES users(id),
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW(),
  
  UNIQUE(slug, language)
);

🔐 Security & Permissions

Admin Guard Implementation

File: src/common/guards/admin.guard.ts

import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';
import { JwtAuthGuard } from './jwt-auth.guard';

@Injectable()
export class AdminGuard extends JwtAuthGuard {
  async canActivate(context: ExecutionContext): Promise<boolean> {
    // First check JWT authentication
    const isAuthenticated = await super.canActivate(context);
    if (!isAuthenticated) {
      return false;
    }

    const request = context.switchToHttp().getRequest();
    const user = request.user;

    // Check if user is admin
    if (!user.isAdmin) {
      throw new ForbiddenException('Admin access required');
    }

    return true;
  }
}

Family Role Guard

File: src/common/guards/family-role.guard.ts

import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

export const RequireFamilyRole = (...roles: string[]) => 
  SetMetadata('familyRoles', roles);

@Injectable()
export class FamilyRoleGuard implements CanActivate {
  constructor(private reflector: Reflector, private familyService: FamilyService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const requiredRoles = this.reflector.get<string[]>('familyRoles', context.getHandler());
    if (!requiredRoles) {
      return true;
    }

    const request = context.switchToHttp().getRequest();
    const user = request.user;
    const familyId = request.params.familyId || request.body.familyId;

    const membership = await this.familyService.getUserFamilyRole(user.id, familyId);

    if (!membership) {
      throw new ForbiddenException('Not a member of this family');
    }

    if (!requiredRoles.includes(membership.familyRole)) {
      throw new ForbiddenException(`Requires family role: ${requiredRoles.join(' or ')}`);
    }

    // Attach to request for use in controllers
    request.familyRole = membership.familyRole;
    return true;
  }
}

Usage Example

@Controller('api/v1/analytics')
export class AnalyticsController {
  
  @Get(':childId/insights')
  @UseGuards(JwtAuthGuard, FamilyRoleGuard)
  @RequireFamilyRole('parent') // Guests cannot access analytics
  async getInsights(@Param('childId') childId: string) {
    // Only parents can access
  }

  @Post(':childId/activities')
  @UseGuards(JwtAuthGuard, FamilyRoleGuard)
  @RequireFamilyRole('parent', 'guest') // Both can add activities
  async createActivity(@Param('childId') childId: string) {
    // Both parents and guests can add
  }
}

📊 Admin Dashboard Features

1. User Management

Endpoint: GET /api/v1/admin/users

Features:

  • List Users: Paginated, searchable, filterable
  • User Details: Full profile, activity history, subscription
  • Create User: Manual user creation (for demos, test accounts)
  • Edit User: Update profile, subscription, role
  • Delete User: Soft delete with anonymization option
  • Anonymize User: GDPR compliance - replace PII with anonymized data
  • Export User Data: Full data export in JSON/CSV
  • Verify User: Manual email verification override
  • Manage Subscription: Change plan, extend trial, apply discount

UI Components:

maternal-web/app/admin/users/
├── page.tsx                    # User list with search/filter
├── [userId]/
│   ├── page.tsx                # User detail view
│   ├── edit/page.tsx           # Edit user form
│   └── export/page.tsx         # Data export interface
└── create/page.tsx             # Create user form

Backend Module:

// src/modules/admin/user-management/user-management.service.ts
@Injectable()
export class UserManagementService {
  
  async listUsers(filters: UserFilters, pagination: Pagination) {
    // Query with filters, sorting, pagination
  }

  async getUserDetails(userId: string) {
    // Full user details including:
    // - Profile info
    // - Families and roles
    // - Subscription status
    // - Activity stats
    // - AI usage stats
  }

  async anonymizeUser(userId: string, adminId: string) {
    // GDPR compliance
    // Replace: name → "Anonymized User 12345"
    // Replace: email → "anon_12345@maternal.local"
    // Keep: aggregated analytics (anonymized)
    // Log action in audit_log
  }

  async exportUserData(userId: string) {
    // Full GDPR export
    // - User profile
    // - Children data
    // - Activities
    // - AI conversations
    // - Photos (URLs)
    return { json, csv };
  }

  async changeSubscription(userId: string, planId: string, adminId: string) {
    // Update subscription
    // Log in audit trail
  }
}

2. Role Management UI

Components:

// components/admin/RoleSelector.tsx
export function RoleSelector({ userId, currentRole, onRoleChange }) {
  return (
    <Select value={currentRole} onChange={onRoleChange}>
      <option value="parent">Parent (Full Access)</option>
      <option value="guest">Guest (Limited Access)</option>
      <option value="admin">Admin (System Access)</option>
    </Select>
  );
}

// components/admin/FamilyRoleManager.tsx
export function FamilyRoleManager({ userId, families }) {
  // Show user's role in each family
  // Allow changing role per family
  return (
    <Table>
      {families.map(family => (
        <TableRow key={family.id}>
          <TableCell>{family.name}</TableCell>
          <TableCell>
            <RoleSelector 
              userId={userId} 
              familyId={family.id}
              currentRole={family.userRole}
            />
          </TableCell>
        </TableRow>
      ))}
    </Table>
  );
}

3. Multi-Profile Support

User Stories:

Use Case 1: Multiple Families, One Account

User: Sarah (Parent)
├── Profile: "Work Family" (childcare at work)
│   └── Family: "ABC Daycare"
│       ├── Child: Emma (not hers, she's a caregiver)
│       └── Role: Guest
└── Profile: "Home Family" (her own kids)
    └── Family: "Smith Family"
        ├── Child: Liam
        ├── Child: Olivia
        └── Role: Parent

Use Case 2: Multiple Accounts

User: John
├── Account 1: john@personal.com (Parent in "Doe Family")
└── Account 2: john@work.com (Guest in "Work Daycare")

Implementation:

// components/layout/ProfileSwitcher.tsx
export function ProfileSwitcher() {
  const { user, profiles, currentProfile, switchProfile } = useAuth();

  return (
    <Menu>
      <MenuButton>
        <Avatar src={currentProfile.avatar} />
        {currentProfile.name}
      </MenuButton>
      <MenuList>
        {profiles.map(profile => (
          <MenuItem 
            key={profile.id} 
            onClick={() => switchProfile(profile.id)}
            isActive={profile.id === currentProfile.id}
          >
            <Avatar src={profile.avatar} size="sm" />
            <Box>
              <Text>{profile.name}</Text>
              <Text fontSize="xs" color="gray.500">
                {profile.familyName} · {profile.role}
              </Text>
            </Box>
          </MenuItem>
        ))}
        <MenuDivider />
        <MenuItem onClick={() => router.push('/add-profile')}>
          + Add Profile
        </MenuItem>
      </MenuList>
    </Menu>
  );
}

Backend API:

// POST /api/v1/profiles
async createProfile(userId: string, dto: CreateProfileDto) {
  // Create new profile
  // Link to family
  // Set default
}

// PATCH /api/v1/profiles/:id/switch
async switchProfile(userId: string, profileId: string) {
  // Update user's active profile
  // Return new auth context
}

4. Analytics Dashboard

Metrics to Track:

User Analytics

  • Total users (active, inactive, trial, paid)
  • New users per day/week/month
  • User growth rate
  • Churn rate
  • Average session duration
  • Daily/Monthly Active Users (DAU/MAU)

Family & Children Analytics

  • Total families
  • Average children per family
  • Average family size (members)
  • Most active families

Feature Usage

  • Activities logged per day (by type)
  • AI queries per day
  • Voice commands per day
  • Photo uploads per day
  • Most used features

AI/LLM Analytics

  • Total AI queries (by model)
  • Average tokens per query (input/output)
  • Estimated costs per model
  • Average response time
  • Error rate

Voice Analytics

  • Total voice commands
  • Success rate (transcription accuracy)
  • Most common voice intents
  • Average processing time

UI Components:

// app/admin/analytics/page.tsx
export default function AnalyticsDashboard() {
  return (
    <Grid columns={3}>
      <StatCard 
        label="Total Users" 
        value={stats.totalUsers}
        change={stats.userGrowth}
      />
      <StatCard 
        label="Active Families" 
        value={stats.activeFamilies}
      />
      <StatCard 
        label="AI Queries Today" 
        value={stats.aiQueriesToday}
        cost={stats.estimatedCost}
      />
      
      <Chart 
        title="User Growth"
        data={stats.userGrowthData}
        type="line"
      />
      
      <Chart 
        title="Feature Usage"
        data={stats.featureUsageData}
        type="bar"
      />
      
      <Chart 
        title="AI Cost Breakdown"
        data={stats.aiCostData}
        type="pie"
      />
    </Grid>
  );
}

Backend:

// src/modules/admin/analytics/analytics-admin.service.ts
@Injectable()
export class AnalyticsAdminService {
  
  async getSystemStats() {
    return {
      users: await this.getUserStats(),
      families: await this.getFamilyStats(),
      usage: await this.getUsageStats(),
      ai: await this.getAIStats(),
      voice: await this.getVoiceStats(),
    };
  }

  async getAIStats() {
    // Query from ai_conversations table
    // Calculate token usage
    // Estimate costs based on llm_config pricing
    return {
      totalQueries: 15234,
      totalInputTokens: 2450000,
      totalOutputTokens: 1850000,
      estimatedCost: 5.47, // in USD
      byModel: [
        { model: 'gpt-4o-mini', queries: 12000, cost: 3.20 },
        { model: 'gpt-4', queries: 3234, cost: 2.27 },
      ],
    };
  }
}

5. System Health Monitoring

Metrics:

  • Service status (backend, database, redis, mongodb)
  • API response times (p50, p95, p99)
  • Error rates
  • Database connection pool status
  • Redis cache hit/miss ratio
  • Queue depths (background jobs)
  • Memory/CPU usage
  • Disk space

Integration with Existing Health Endpoints:

// Reuse existing health endpoints
GET /health                  # Overall health
GET /health/liveness         # Liveness probe
GET /health/readiness        # Readiness probe

// New admin-specific endpoint
GET /api/v1/admin/system/health

// Response:
{
  "status": "healthy",
  "uptime": 345600, // seconds
  "services": {
    "postgres": { "status": "up", "responseTime": "15ms" },
    "redis": { "status": "up", "responseTime": "2ms" },
    "mongodb": { "status": "up", "responseTime": "18ms" },
    "minio": { "status": "up", "responseTime": "45ms" },
    "azureOpenAI": { "status": "up", "responseTime": "120ms" }
  },
  "metrics": {
    "apiResponseTime": { "p50": 85, "p95": 250, "p99": 450 },
    "errorRate": 0.02,
    "requestsPerMinute": 1250,
    "cacheHitRatio": 0.85
  },
  "resources": {
    "memoryUsage": { "used": "2.1GB", "total": "4GB", "percentage": 52.5 },
    "cpuUsage": 35.2,
    "diskSpace": { "used": "45GB", "total": "100GB", "percentage": 45 }
  }
}

UI:

// app/admin/system/health/page.tsx
export default function SystemHealthPage() {
  const { data, refetch } = useQuery('/api/v1/admin/system/health', {
    refetchInterval: 10000, // Refresh every 10s
  });

  return (
    <>
      <HealthStatusBadge status={data.status} />
      
      <ServiceStatusGrid services={data.services} />
      
      <MetricsChart metrics={data.metrics} />
      
      <ResourceUsageCards resources={data.resources} />
    </>
  );
}

6. LLM Configuration Management

UI Features:

  • Add/Edit LLM endpoints
  • Manage API keys (encrypted storage)
  • Set model names and deployments
  • Configure pricing (per 1M tokens)
  • Enable/disable models
  • Test connections

Backend:

// src/modules/admin/llm-config/llm-config.service.ts
@Injectable()
export class LLMConfigService {
  
  async createConfig(dto: CreateLLMConfigDto, adminId: string) {
    // Encrypt API key
    const encryptedKey = await this.encryptionService.encrypt(dto.apiKey);
    
    const config = await this.llmConfigRepository.save({
      ...dto,
      apiKeyEncrypted: encryptedKey,
    });

    // Audit log
    await this.auditService.log({
      adminUserId: adminId,
      action: 'llm_config.create',
      targetId: config.id,
      changes: { provider: dto.provider, serviceType: dto.serviceType },
    });

    return config;
  }

  async testConnection(configId: string) {
    const config = await this.llmConfigRepository.findOne(configId);
    const apiKey = await this.encryptionService.decrypt(config.apiKeyEncrypted);

    try {
      // Test API call
      const response = await axios.post(config.endpointUrl, {
        messages: [{ role: 'user', content: 'Test' }],
        max_tokens: 10,
      }, {
        headers: { 'api-key': apiKey },
      });

      return { success: true, latency: response.duration };
    } catch (error) {
      return { success: false, error: error.message };
    }
  }

  async getEstimatedCosts() {
    // Calculate monthly costs based on usage
    const usage = await this.getMonthlyTokenUsage();
    
    return usage.map(model => ({
      modelName: model.name,
      inputTokens: model.inputTokens,
      outputTokens: model.outputTokens,
      estimatedCost: (
        (model.inputTokens / 1000000) * model.pricePerMInput +
        (model.outputTokens / 1000000) * model.pricePerMOutput
      ),
    }));
  }
}

Features:

  • Create/Edit legal pages (Privacy Policy, Terms, EULA, Cookies)
  • Markdown editor with preview
  • Multi-language support
  • Version history
  • Publish/Unpublish
  • SEO metadata

UI:

// app/admin/pages/[slug]/edit/page.tsx
export default function EditLegalPage({ params }) {
  const [content, setContent] = useState('');
  const [language, setLanguage] = useState('en');

  return (
    <Form>
      <Input label="Title" />
      <Select label="Language" value={language} onChange={setLanguage}>
        <option value="en">English</option>
        <option value="es">Spanish</option>
        <option value="fr">French</option>
      </Select>
      
      <MarkdownEditor 
        value={content} 
        onChange={setContent}
        height="600px"
      />
      
      <MarkdownPreview content={content} />
      
      <ButtonGroup>
        <Button variant="outline">Save Draft</Button>
        <Button>Publish</Button>
      </ButtonGroup>
    </Form>
  );
}

8. Subscription Management

Features:

  • Create subscription plans
  • Set pricing (monthly/yearly)
  • Define feature limits
  • Trial period configuration
  • Apply to users
  • Discount codes

Data Model:

interface SubscriptionPlan {
  id: string;
  name: string; // 'Free', 'Pro', 'Family'
  description: string;
  priceMonthly: number;
  priceYearly: number;
  features: {
    aiQueries: 'unlimited' | number;
    voiceCommands: 'unlimited' | number;
    maxChildren: number;
    maxFamilyMembers: number;
    analytics: boolean;
    prioritySupport: boolean;
  };
  trialPeriodDays: number;
}

UI:

// app/admin/subscriptions/page.tsx
export default function SubscriptionPlansPage() {
  return (
    <>
      <PlanList plans={plans} />
      <Button onClick={openCreateDialog}>Create Plan</Button>
      
      <CreatePlanDialog>
        <Input label="Plan Name" />
        <Input label="Monthly Price" type="number" prefix="$" />
        <Input label="Yearly Price" type="number" prefix="$" />
        <Input label="Trial Days" type="number" />
        
        <FeatureLimits>
          <Input label="Max Children" />
          <Input label="AI Queries/Month" />
          <Toggle label="Analytics Access" />
        </FeatureLimits>
      </CreatePlanDialog>
    </>
  );
}

9. Email Configuration

UI:

// app/admin/settings/email/page.tsx
export default function EmailSettingsPage() {
  return (
    <Form>
      <Select label="Region">
        <option value="US">US (api.mailgun.net)</option>
        <option value="EU">EU (api.eu.mailgun.net)</option>
      </Select>
      
      <Input 
        label="API Key" 
        type="password" 
        placeholder="key-xxxxx"
      />
      
      <Input label="Domain" placeholder="mg.maternal.com" />
      
      <Input label="Sender Email" placeholder="noreply@maternal.com" />
      
      <Input label="Sender Name" placeholder="Maternal App" />
      
      <Button onClick={testEmail}>Send Test Email</Button>
      <Button>Save Configuration</Button>
    </Form>
  );
}

Backend:

// src/modules/admin/email-config/email-config.service.ts
@Injectable()
export class EmailConfigService {
  
  async updateConfig(dto: UpdateEmailConfigDto, adminId: string) {
    const encryptedKey = await this.encryptionService.encrypt(dto.apiKey);
    
    const config = await this.emailConfigRepository.save({
      ...dto,
      apiKeyEncrypted: encryptedKey,
    });

    // Update EmailService to use new config
    await this.emailService.reloadConfig();

    // Audit log
    await this.auditService.log({
      adminUserId: adminId,
      action: 'email_config.update',
      changes: { region: dto.region, domain: dto.domain },
    });

    return config;
  }

  async sendTestEmail(recipientEmail: string) {
    try {
      await this.emailService.sendEmail({
        to: recipientEmail,
        subject: 'Test Email from Maternal App Admin',
        text: 'If you received this, email configuration is working!',
      });
      return { success: true };
    } catch (error) {
      return { success: false, error: error.message };
    }
  }
}

🚀 Deployment Strategy

Admin Service Deployment

The admin service will be deployed as a separate microservice:

Infrastructure

# docker-compose.admin.yml
version: '3.8'

services:
  admin-api:
    build: ./parentflow-admin-service
    container_name: parentflow-admin-api
    ports:
      - "4000:4000"  # Admin API on separate port
    environment:
      NODE_ENV: production
      DATABASE_URL: postgresql://...
      JWT_SECRET: ${ADMIN_JWT_SECRET}
      SERVICE_AUTH_KEY: ${SERVICE_AUTH_KEY}  # For service-to-service auth
    networks:
      - parentflow-network

  admin-ui:
    build: ./parentflow-admin-service/admin-ui
    container_name: parentflow-admin-ui
    ports:
      - "4001:3000"  # Admin UI
    environment:
      REACT_APP_API_URL: http://admin-api:4000
    networks:
      - parentflow-network

networks:
  parentflow-network:
    external: true

Service Communication

// Service-to-service authentication
export class AdminServiceClient {
  private readonly serviceKey: string;
  private readonly adminApiUrl: string;

  constructor() {
    this.serviceKey = process.env.SERVICE_AUTH_KEY;
    this.adminApiUrl = process.env.ADMIN_API_URL || 'http://localhost:4000';
  }

  async validateInviteCode(code: string, platform: string): Promise<ValidationResult> {
    const response = await axios.post(
      `${this.adminApiUrl}/api/v1/internal/validate-invite-code`,
      { code, platform },
      {
        headers: {
          'X-Service-Auth': this.serviceKey,
          'X-Service-Name': 'parentflow-web'
        }
      }
    );
    return response.data;
  }

  async getRegistrationConfig(): Promise<RegistrationConfig> {
    // Cache this for 5 minutes
    return this.cache.get('registration-config', async () => {
      const response = await axios.get(
        `${this.adminApiUrl}/api/v1/internal/registration-config`,
        {
          headers: {
            'X-Service-Auth': this.serviceKey,
            'X-Service-Name': 'parentflow-web'
          }
        }
      );
      return response.data;
    }, 300); // 5 minutes TTL
  }
}

Nginx Configuration

# admin.parentflowapp.com
server {
    listen 443 ssl http2;
    server_name admin.parentflowapp.com;

    # SSL configuration
    ssl_certificate /etc/letsencrypt/live/admin.parentflowapp.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/admin.parentflowapp.com/privkey.pem;

    # IP whitelist for admin access (optional)
    allow 10.0.0.0/24;  # Office network
    allow 192.168.1.0/24;  # VPN
    deny all;

    # Admin UI
    location / {
        proxy_pass http://localhost:4001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }

    # Admin API
    location /api/ {
        proxy_pass http://localhost:4000;
        proxy_http_version 1.1;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Deployment Servers

Production Environment:
├── Admin Service
│   ├── Server: 10.0.0.241 (separate from main app)
│   ├── URL: admin.parentflowapp.com
│   ├── Ports: 4000 (API), 4001 (UI)
│   └── Database: Shared with main app (read/write to admin tables)
│
├── Main Applications
│   ├── Web: 10.0.0.240:3030 → web.parentflowapp.com
│   ├── API: 10.0.0.240:3020 → api.parentflowapp.com
│   └── Mobile APIs: Will connect to api.parentflowapp.com
│
└── Shared Infrastructure
    ├── PostgreSQL: 10.0.0.240:5432
    ├── Redis: 10.0.0.240:6379
    └── MongoDB: 10.0.0.240:27017

🚀 Implementation Timeline

Phase 1: Foundation (Week 1-2)

Week 1: Database & Backend Core

  • Create database migrations for new tables
  • Implement role enums and guards
  • Create AdminGuard and FamilyRoleGuard
  • Set up admin API module structure
  • Implement audit logging service

Week 2: User Management

  • User management endpoints (CRUD)
  • Anonymization logic
  • Data export functionality
  • Subscription management endpoints
  • Unit tests for user management

Phase 2: Admin UI (Week 3-4)

Week 3: Admin Dashboard Layout

  • Admin layout component
  • Navigation sidebar
  • User management UI (list, detail, edit)
  • Role management UI
  • Multi-profile UI components

Week 4: Analytics & Monitoring

  • Analytics dashboard UI
  • System health monitoring UI
  • Charts and visualizations
  • Real-time metric updates

Phase 3: Configuration (Week 5-6)

Week 5: LLM & Email Config

  • LLM configuration UI
  • API key management (encrypted)
  • Connection testing
  • Email settings UI
  • Test email functionality

Week 6: Content & Subscriptions

  • Legal pages CMS
  • Markdown editor integration
  • Subscription plan management UI
  • Trial period configuration
  • Discount codes

Phase 4: Security & Testing (Week 7-8)

Week 7: Security Hardening

  • 2FA for admin accounts
  • Admin session timeout (15 min)
  • IP whitelisting option
  • Audit log viewer UI
  • Security testing

Week 8: Final Testing & Documentation

  • Integration tests for all admin endpoints
  • E2E tests for critical admin flows
  • Performance testing
  • Admin user documentation
  • Developer API documentation

Acceptance Criteria

Security

  • Only users with isAdmin=true can access admin dashboard
  • All admin actions logged to audit trail
  • API keys encrypted at rest (AES-256)
  • Admin sessions expire after 15 minutes of inactivity
  • 2FA required for admin accounts

User Management

  • Admin can create/edit/delete users
  • Admin can anonymize user data (GDPR)
  • Admin can export user data in JSON/CSV
  • Admin can change user subscriptions
  • Admin can verify user emails manually

Role Management

  • Parents have full access to their families
  • Guests can only add activities, no historical data
  • Admins have system-wide access
  • Family roles can be changed per user per family

Multi-Profile

  • Users can switch between multiple families
  • Users can manage multiple accounts
  • Profile switcher in user menu
  • Default profile saved per user

Analytics

  • Admin can view user growth metrics
  • Admin can view feature usage stats
  • Admin can view AI/LLM costs and usage
  • Admin can view voice command analytics
  • Real-time metrics with 10s refresh

System Health

  • Admin can view service status
  • Admin can view API performance metrics
  • Admin can view resource usage
  • Alerts for service degradation

Configuration

  • Admin can add/edit LLM models
  • Admin can test LLM connections
  • Admin can view estimated AI costs
  • Admin can update email settings
  • Admin can edit legal pages
  • Admin can create subscription plans

📚 Additional Features (Future Enhancements)

Advanced Analytics

  • Custom report builder
  • Data export scheduler
  • Cohort analysis
  • Funnel visualization
  • A/B testing dashboard

Automation

  • Automated user onboarding emails
  • Trial expiration reminders
  • Inactive user re-engagement
  • Subscription renewal reminders

Advanced Security

  • IP whitelisting for admin access
  • Admin activity anomaly detection
  • Automated threat response
  • SIEM integration

Support Tools

  • In-app user support chat
  • Impersonate user (for debugging)
  • Feature flag management
  • Rollback deployments


Last Updated: October 3, 2025
Next Review: After Phase 1 completion
Owner: Backend Team + Admin Team