Skip to content

Contextual Documentation Link Implementation Plan

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: Add a dynamic documentation link to the header that changes based on the current page.

Architecture: Route-based configuration maps paths to GitHub doc URLs. A PageDocsLink component reads the current route via useLocation() and renders a styled link. Component is placed in Header before the health indicator.

Tech Stack: React, React Router v7, TypeScript, Tailwind CSS, Lucide icons


Task 1: Create Page Documentation Config

Files:

  • Create: frontend/src/config/pageDocumentation.ts
  • Test: frontend/src/config/pageDocumentation.test.ts

Step 1: Write the test

// frontend/src/config/pageDocumentation.test.ts
import { describe, it, expect } from 'vitest';
import { PAGE_DOCUMENTATION, PageDocConfig } from './pageDocumentation';

describe('pageDocumentation', () => {
  it('exports PAGE_DOCUMENTATION config object', () => {
    expect(PAGE_DOCUMENTATION).toBeDefined();
    expect(typeof PAGE_DOCUMENTATION).toBe('object');
  });

  it('has config for dashboard route', () => {
    const config = PAGE_DOCUMENTATION['/'];
    expect(config).toBeDefined();
    expect(config.label).toBe('Dashboard');
    expect(config.docPath).toMatch(/docs\/ui\/dashboard\.md$/);
  });

  it('has config for all main routes', () => {
    const expectedRoutes = [
      '/',
      '/timeline',
      '/entities',
      '/alerts',
      '/audit',
      '/analytics',
      '/jobs',
      '/ai-audit',
      '/ai',
      '/operations',
      '/trash',
      '/logs',
      '/settings',
    ];

    expectedRoutes.forEach((route) => {
      expect(PAGE_DOCUMENTATION[route]).toBeDefined();
      expect(PAGE_DOCUMENTATION[route].label).toBeTruthy();
      expect(PAGE_DOCUMENTATION[route].docPath).toMatch(/^docs\/ui\/.+\.md$/);
    });
  });

  it('does not have config for dev-tools route', () => {
    expect(PAGE_DOCUMENTATION['/dev-tools']).toBeUndefined();
  });
});

Step 2: Run test to verify it fails

Run: cd frontend && npm test -- src/config/pageDocumentation.test.ts Expected: FAIL - module not found

Step 3: Write the implementation

// frontend/src/config/pageDocumentation.ts
export interface PageDocConfig {
  /** Display name shown in link (e.g., "Alerts", "Jobs") */
  label: string;
  /** Path to documentation file relative to repo root */
  docPath: string;
  /** Optional tooltip description */
  description?: string;
}

/**
 * Maps routes to their documentation configuration.
 * Used by PageDocsLink to render contextual help links.
 */
export const PAGE_DOCUMENTATION: Record<string, PageDocConfig> = {
  '/': {
    label: 'Dashboard',
    docPath: 'docs/ui/dashboard.md',
    description: 'Live monitoring and risk overview',
  },
  '/timeline': {
    label: 'Timeline',
    docPath: 'docs/ui/timeline.md',
    description: 'Chronological event history',
  },
  '/entities': {
    label: 'Entities',
    docPath: 'docs/ui/entities.md',
    description: 'Tracked people and objects',
  },
  '/alerts': {
    label: 'Alerts',
    docPath: 'docs/ui/alerts.md',
    description: 'Alert management and configuration',
  },
  '/audit': {
    label: 'Audit Log',
    docPath: 'docs/ui/audit-log.md',
    description: 'System audit trail',
  },
  '/analytics': {
    label: 'Analytics',
    docPath: 'docs/ui/analytics.md',
    description: 'Insights and trends',
  },
  '/jobs': {
    label: 'Jobs',
    docPath: 'docs/ui/jobs.md',
    description: 'Background job monitoring',
  },
  '/ai-audit': {
    label: 'AI Audit',
    docPath: 'docs/ui/ai-audit.md',
    description: 'AI decision explanations',
  },
  '/ai': {
    label: 'AI Performance',
    docPath: 'docs/ui/ai-performance.md',
    description: 'Model metrics and performance',
  },
  '/operations': {
    label: 'Operations',
    docPath: 'docs/ui/operations.md',
    description: 'System health and resources',
  },
  '/trash': {
    label: 'Trash',
    docPath: 'docs/ui/trash.md',
    description: 'Deleted event recovery',
  },
  '/logs': {
    label: 'Logs',
    docPath: 'docs/ui/logs.md',
    description: 'Application log viewer',
  },
  '/settings': {
    label: 'Settings',
    docPath: 'docs/ui/settings.md',
    description: 'Application configuration',
  },
};

Step 4: Run test to verify it passes

Run: cd frontend && npm test -- src/config/pageDocumentation.test.ts Expected: PASS

Step 5: Commit

git add frontend/src/config/pageDocumentation.ts frontend/src/config/pageDocumentation.test.ts
git commit -m "feat: add page documentation config mapping routes to docs"

Files:

  • Create: frontend/src/components/layout/PageDocsLink.tsx
  • Test: frontend/src/components/layout/PageDocsLink.test.tsx

Step 1: Write the test

// frontend/src/components/layout/PageDocsLink.test.tsx
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { describe, it, expect } from 'vitest';
import { PageDocsLink } from './PageDocsLink';

const renderWithRouter = (initialRoute: string) => {
  return render(
    <MemoryRouter initialEntries={[initialRoute]}>
      <PageDocsLink />
    </MemoryRouter>
  );
};

describe('PageDocsLink', () => {
  it('renders dashboard documentation link on root route', () => {
    renderWithRouter('/');

    const link = screen.getByRole('link', { name: /dashboard documentation/i });
    expect(link).toBeInTheDocument();
    expect(link).toHaveAttribute('href', expect.stringContaining('docs/ui/dashboard.md'));
    expect(link).toHaveAttribute('target', '_blank');
    expect(link).toHaveAttribute('rel', 'noopener noreferrer');
  });

  it('renders alerts documentation link on alerts route', () => {
    renderWithRouter('/alerts');

    const link = screen.getByRole('link', { name: /alerts documentation/i });
    expect(link).toBeInTheDocument();
    expect(link).toHaveAttribute('href', expect.stringContaining('docs/ui/alerts.md'));
  });

  it('renders jobs documentation link on jobs route', () => {
    renderWithRouter('/jobs');

    const link = screen.getByRole('link', { name: /jobs documentation/i });
    expect(link).toBeInTheDocument();
    expect(link).toHaveAttribute('href', expect.stringContaining('docs/ui/jobs.md'));
  });

  it('renders nothing for unmapped routes', () => {
    renderWithRouter('/dev-tools');

    expect(screen.queryByRole('link')).not.toBeInTheDocument();
  });

  it('includes BookOpen icon', () => {
    renderWithRouter('/');

    // The link should contain an svg (the BookOpen icon)
    const link = screen.getByRole('link');
    const svg = link.querySelector('svg');
    expect(svg).toBeInTheDocument();
  });

  it('has NVIDIA green styling', () => {
    renderWithRouter('/');

    const link = screen.getByRole('link');
    expect(link).toHaveClass('text-[#76B900]');
  });

  it('shows short text on mobile (via responsive class)', () => {
    renderWithRouter('/');

    // Check that "Docs" text exists for mobile
    expect(screen.getByText('Docs')).toBeInTheDocument();
    // Check that full text exists for desktop
    expect(screen.getByText(/dashboard documentation/i)).toBeInTheDocument();
  });
});

Step 2: Run test to verify it fails

Run: cd frontend && npm test -- src/components/layout/PageDocsLink.test.tsx Expected: FAIL - module not found

Step 3: Write the implementation

// frontend/src/components/layout/PageDocsLink.tsx
import { BookOpen } from 'lucide-react';
import { useLocation } from 'react-router-dom';

import { PAGE_DOCUMENTATION } from '../../config/pageDocumentation';

const GITHUB_BASE_URL =
  'https://github.com/mikesvoboda/nemotron-v3-home-security-intelligence/blob/main/';

/**
 * Renders a contextual documentation link based on the current route.
 * The link text and destination change as the user navigates between pages.
 *
 * Returns null for routes without configured documentation.
 */
export function PageDocsLink() {
  const { pathname } = useLocation();
  const pageDoc = PAGE_DOCUMENTATION[pathname];

  // Don't render if no docs configured for this page
  if (!pageDoc) {
    return null;
  }

  const url = `${GITHUB_BASE_URL}${pageDoc.docPath}`;

  return (
    <a
      href={url}
      target="_blank"
      rel="noopener noreferrer"
      className="flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium text-[#76B900] transition-colors hover:bg-[#76B900]/20 hover:text-white"
      title={pageDoc.description}
    >
      <BookOpen className="h-4 w-4" aria-hidden="true" />
      <span className="hidden sm:inline">{pageDoc.label} Documentation</span>
      <span className="sm:hidden">Docs</span>
    </a>
  );
}

Step 4: Run test to verify it passes

Run: cd frontend && npm test -- src/components/layout/PageDocsLink.test.tsx Expected: PASS

Step 5: Commit

git add frontend/src/components/layout/PageDocsLink.tsx frontend/src/components/layout/PageDocsLink.test.tsx
git commit -m "feat: add PageDocsLink component for contextual documentation"

Files:

  • Modify: frontend/src/components/layout/Header.tsx
  • Modify: frontend/src/components/layout/Header.test.tsx

Step 1: Write the test

Add to existing Header tests:

// Add to frontend/src/components/layout/Header.test.tsx

// Add import at top
import { PAGE_DOCUMENTATION } from '../../config/pageDocumentation';

// Add new describe block
describe('PageDocsLink integration', () => {
  it('renders documentation link in header', () => {
    render(
      <MemoryRouter initialEntries={['/']}>
        <Header />
      </MemoryRouter>
    );

    const docsLink = screen.getByRole('link', { name: /documentation/i });
    expect(docsLink).toBeInTheDocument();
  });

  it('documentation link changes based on route', () => {
    const { rerender } = render(
      <MemoryRouter initialEntries={['/alerts']}>
        <Header />
      </MemoryRouter>
    );

    expect(screen.getByRole('link', { name: /alerts documentation/i })).toBeInTheDocument();

    rerender(
      <MemoryRouter initialEntries={['/jobs']}>
        <Header />
      </MemoryRouter>
    );

    expect(screen.getByRole('link', { name: /jobs documentation/i })).toBeInTheDocument();
  });
});

Step 2: Run test to verify it fails

Run: cd frontend && npm test -- src/components/layout/Header.test.tsx Expected: FAIL - documentation link not found

Step 3: Modify Header.tsx

Add import at top of file (around line 9):

import { PageDocsLink } from './PageDocsLink';

Add PageDocsLink component inside the right-side flex container, before the health indicator (around line 255):

<div className="flex items-center gap-2 px-3 md:gap-6 md:px-6">
  {/* Contextual documentation link */}
  <PageDocsLink />

  {/* System Health Indicator with Tooltip */}
  <div
    ref={containerRef}
    // ... rest of health indicator

Step 4: Run test to verify it passes

Run: cd frontend && npm test -- src/components/layout/Header.test.tsx Expected: PASS

Step 5: Commit

git add frontend/src/components/layout/Header.tsx frontend/src/components/layout/Header.test.tsx
git commit -m "feat: integrate PageDocsLink into Header component"

Task 4: Create Documentation Files Scaffold

Files:

  • Create: docs/ui/README.md
  • Create: docs/ui/dashboard.md (template)

Step 1: Create docs/ui/README.md

# UI Documentation

Page-specific documentation for the Nemotron Home Security dashboard.

## Pages

| Page                                | Description                                                          |
| ----------------------------------- | -------------------------------------------------------------------- |
| [Dashboard](dashboard.md)           | Main monitoring view with risk gauge, camera grid, and activity feed |
| [Timeline](timeline.md)             | Chronological event history with filtering                           |
| [Entities](entities.md)             | Tracked people and objects                                           |
| [Alerts](alerts.md)                 | Alert configuration and history                                      |
| [Audit Log](audit-log.md)           | System audit trail                                                   |
| [Analytics](analytics.md)           | Insights and trend analysis                                          |
| [Jobs](jobs.md)                     | Background job monitoring                                            |
| [AI Audit](ai-audit.md)             | AI decision explanations                                             |
| [AI Performance](ai-performance.md) | Model metrics and performance                                        |
| [Operations](operations.md)         | System health and resources                                          |
| [Trash](trash.md)                   | Deleted event recovery                                               |
| [Logs](logs.md)                     | Application log viewer                                               |
| [Settings](settings.md)             | Application configuration                                            |

## Documentation Structure

Each page doc follows this structure:

1. **What You're Looking At** - Plain language overview
2. **Key Components** - UI element explanations
3. **Settings & Configuration** - Configurable options
4. **Troubleshooting** - Common issues
5. **Technical Deep Dive** - Architecture links for developers

Step 2: Create docs/ui/dashboard.md (template for other pages)

# Dashboard

The main monitoring view showing real-time security status across all cameras.

## What You're Looking At

The Dashboard is your central hub for home security monitoring. It provides:

- **Risk Gauge** - Current overall threat level (0-100)
- **Camera Grid** - Live status of all connected cameras
- **Activity Feed** - Recent detection events

## Key Components

### Risk Gauge

The circular gauge in the top-left shows the current risk score from 0-100:

- **0-30 (Green)**: Normal activity, no concerns
- **31-60 (Yellow)**: Elevated activity, worth monitoring
- **61-100 (Red)**: High-risk event detected, review immediately

The risk score is calculated by the Nemotron AI model analyzing detected objects, time of day, and historical patterns.

### Camera Grid

Each camera card shows:

- **Camera name** - Location identifier
- **Last activity** - Time since last detection
- **Status indicator** - Green (active), Yellow (idle), Red (offline)

Click any camera to view its recent events in the Timeline.

### Activity Feed

The right panel shows the most recent detection events:

- **Timestamp** - When the event occurred
- **Camera** - Which camera captured it
- **Detection** - What was detected (person, vehicle, animal, etc.)
- **Risk score** - AI-assigned threat level

## Settings & Configuration

Dashboard settings are available in [Settings > Dashboard](settings.md#dashboard):

- **Refresh interval** - How often to poll for updates (default: 5 seconds)
- **Camera grid layout** - Grid size (2x2, 3x3, or auto)
- **Activity feed limit** - Number of recent events to show

## Troubleshooting

### Risk Gauge shows "--"

The AI service may be starting up or disconnected. Check the health indicator in the header.

### Camera shows "Offline"

1. Verify the camera is powered on
2. Check FTP upload settings on the camera
3. Ensure the camera's folder exists at `/export/foscam/{camera_name}/`

### Activity Feed is empty

No events have been detected in the selected time range. Try expanding the time filter.

---

## Technical Deep Dive

For developers wanting to understand the underlying systems.

### Architecture

- **Event Processing**: [AI Pipeline Architecture](../architecture/ai-pipeline.md)
- **AI Risk Scoring**: [Risk Analysis](../developer/risk-analysis.md)
- **Real-time Updates**: [Real-time Architecture](../architecture/real-time.md)

### Related Code

- Frontend: `frontend/src/pages/DashboardPage.tsx`
- Components: `frontend/src/components/dashboard/`
- Backend API: `backend/api/routes/dashboard.py`
- Risk Service: `backend/services/risk_scoring_service.py`

Step 3: Commit

git add docs/ui/README.md docs/ui/dashboard.md
git commit -m "docs: add UI documentation scaffold with dashboard template"

Task 5: Run Full Validation

Step 1: Run all frontend tests

Run: cd frontend && npm test Expected: All tests pass

Step 2: Run type check

Run: cd frontend && npm run typecheck Expected: No errors

Step 3: Run linter

Run: cd frontend && npm run lint Expected: No errors

Step 4: Run full validation script

Run: ./scripts/validate.sh Expected: All checks pass


Summary

After completing all tasks:

  • 3 new files: pageDocumentation.ts, PageDocsLink.tsx, tests
  • 1 modified file: Header.tsx
  • 2 documentation files: docs/ui/README.md, docs/ui/dashboard.md
  • 4 commits: Config, component, integration, docs

The feature will be functional immediately. Remaining documentation files can be added incrementally.