Progressive Data View

Business Central schema explorer providing interactive visualization of 4,200+ tables, 21,000+ field relationships, and page coverage analysis. Connects to live BC metadata APIs with static JSON fallback.


Overview

Progressive Data View is a React single-page application that explores the full Business Central database schema. It fetches table, field, relation, and page control metadata from custom BC API endpoints, then renders them across five interactive views.

FeatureDescription
Table ListSearchable, sortable list of all BC tables with field counts and relation badges
Force GraphInteractive force-directed graph of table relationships (expandable fullscreen)
ERD ViewCanvas-based entity-relationship diagram with field-level detail
Pages ViewBrowse all BC page objects and their source table bindings
Coverage ViewPage coverage dashboard — orphan tables, field-to-page mapping stats
Command PaletteCmd+K quick search across all tables with recent history
Focus ModeIsolate a table and its N-hop neighborhood in any view
CopilotBuilt-in AI assistant for natural language schema queries
PropertyValue
RepositoryProgressiveSurface/progressive-data-view
HostingAzure App Service (Node.js)
AuthAzure EasyAuth (Entra ID)
AccessInternal only (VPN required)

Data Pipeline

The app loads schema data through a multi-stage pipeline with caching:

┌─────────────────────────────────────────────────────────┐
│                    Browser (SPA)                         │
│                                                         │
│  1. Check IndexedDB cache (24h TTL)                     │
│     ├─ Hit  → Load instantly                            │
│     └─ Miss → Continue to step 2                        │
│                                                         │
│  2. Authenticate via EasyAuth (/.auth/me)                │
│     ├─ Success → Fetch BC API token                     │
│     └─ Fail    → Static JSON fallback (step 4)          │
│                                                         │
│  3. Fetch from BC Metadata API (4 endpoints)             │
│     ├─ Tables + Pages     (parallel, single page)       │
│     ├─ Fields + Controls  (parallel, paginated)         │
│     └─ Enrich fields with relations + page usage        │
│                                                         │
│  4. Static fallback: /data/bc-table-relationships.json   │
│     └─ Used when: local dev, BC API 401/404, 0 relations│
│                                                         │
│  5. Cache result in IndexedDB (~7.6 MB)                  │
└─────────────────────────────────────────────────────────┘

BC API Endpoints

All endpoints are custom API pages from the BCMetadataAPI AL extension:

EndpointRecordsPaginated
tableMetadata~4,200No
pageMetadata~5,600No
fields~85,000+Yes (OData @odata.nextLink)
pageControlMetadata~50,000+Yes (OData @odata.nextLink)

Fields and page controls are fetched in parallel to cut load time roughly in half.


Deployment

Production Configuration

SettingValue
App Serviceprogressive-data-view
Resource GroupPS-WEBAPPS
RuntimeNode 20 LTS (Linux)
Auth ProviderAzure EasyAuth (Entra ID)
Token StoreEnabled (BC API scope in loginParameters)
Public AccessDisabled (private endpoint only)

Environment Variables

VITE_BC_TENANT_ID=<azure-tenant-id>
VITE_BC_ENVIRONMENT=Sandbox
VITE_BC_COMPANY=CRONUS USA, Inc.
VITE_BC_API_PUBLISHER=progressivesurface

CI/CD

GitHub Actions workflow deploys on push to main:

  1. Checkout code
  2. Setup Node.js 20
  3. npm ci + npm run build
  4. Deploy to Azure App Service

Tech Stack

TechnologyPurpose
React 18UI framework
TypeScriptType safety
ViteBuild tool and dev server
TailwindCSS 3Styling (Material Design 3 theme)
shadcn/uiComponent library (Radix primitives)
react-force-graph-2dForce-directed graph visualization
cmdkCommand palette (Cmd+K)
Lucide ReactIcons
IndexedDBClient-side caching (replaced localStorage)
Azure EasyAuthAuthentication (Entra ID tokens)

Design System

The app uses Material Design 3 with a green tonal palette (hue ~158). All theming is CSS-first via custom properties in index.css.

TokenUsage
--primaryButtons, active states, progress bars
--surfaceBackground layers
--surface-containerCard backgrounds, input fields
--outline-variantBorders, dividers
--muted-foregroundSecondary text

Shape system uses rounded-2xl (16px) for cards and rounded-full for pills/badges.


Project Structure

progressive-data-view/
├── package.json
├── server.cjs               # Express production server
├── vite.config.ts            # Dev server config
├── tailwind.config.ts        # M3 color tokens
├── index.html
├── public/
│   └── data/
│       ├── bc-table-relationships.json   # Static fallback (4,211 tables)
│       └── bc-page-metadata.json         # Page metadata
├── src/
│   ├── main.tsx
│   ├── App.tsx
│   ├── index.css             # CSS custom properties (M3 theme)
│   ├── components/
│   │   ├── SchemaExplorer.tsx    # Main orchestrator
│   │   ├── TableList.tsx         # Searchable table list
│   │   ├── TableDetail.tsx       # Field detail panel
│   │   ├── GraphView.tsx         # Force-directed graph (lazy)
│   │   ├── ErdView.tsx           # Canvas ERD (lazy)
│   │   ├── PagesList.tsx         # BC pages browser
│   │   ├── CoverageView.tsx      # Page coverage dashboard
│   │   ├── CommandPalette.tsx    # Cmd+K search
│   │   ├── ConnectionStatus.tsx  # Header status bar
│   │   ├── LoadingScreen.tsx     # Progress screen
│   │   ├── StatsBar.tsx          # Summary statistics
│   │   ├── Copilot.tsx           # AI assistant
│   │   └── ...                   # Filters, breadcrumbs, export, etc.
│   ├── hooks/
│   │   ├── useBCSchemaData.ts    # Main data loader (auth + cache + API)
│   │   ├── useSchemaData.ts      # Filtering and subgraph logic
│   │   ├── usePageMetadata.ts    # Page-field mapping
│   │   └── useRecentTables.ts    # Recent table history
│   ├── services/
│   │   ├── authService.ts        # Azure EasyAuth client
│   │   ├── bcApiService.ts       # BC metadata API client
│   │   ├── cacheService.ts       # IndexedDB cache layer
│   │   └── types.ts              # BC API type definitions
│   └── types/
│       └── schema.ts             # Frontend schema types
└── dist/                         # Build output

Running Locally

Prerequisites

  • Node.js 18+
  • No Azure/BC access needed (falls back to static JSON)

Install and Run

cd C:\Users\ORL\BC\progressive-data-view
 
# Install dependencies
npm install
 
# Start dev server
npm run dev

Access at http://localhost:5173. The app will load static JSON data locally (4,211 tables, 21,053 relations).

Build for Production

npm run build
npm start

Key Architecture

Data Loading (useBCSchemaData)

The main hook manages authentication, caching, and API fallback:

  1. IndexedDB cache — Checks for valid cached data (24h TTL, version-stamped)
  2. EasyAuth — Calls /.auth/me for user info + BC API access token
  3. BC API — Fetches tables, pages, fields, and page controls (fields + controls in parallel)
  4. Enrichment — Joins field relations and page control usage onto table metadata
  5. Fallback — If BC API fails or returns 0 relations, loads static JSON instead

Caching (IndexedDB)

Previous localStorage implementation hit the ~5 MB quota (schema data is ~7.6 MB). The IndexedDB cache handles hundreds of MB and makes subsequent loads near-instant.

PropertyValue
StorageIndexedDB (bc-schema-cache database)
TTL24 hours
VersionBumped on schema changes to auto-invalidate
Size~7.6 MB (structured clone, not JSON string)

OData Pagination

BC API caps responses at 20,000 records. The fetchAllPages method follows @odata.nextLink headers to retrieve complete datasets. Fields (~85,000) and page controls (~50,000) both require multiple pages.


Views

Tables (List View)

Two-column layout: scrollable table list on the left, field detail panel on the right. Tables show field count, relation count, and type badge. Click to see all fields, relations, and page usage.

Graph (Force Graph)

Interactive force-directed graph powered by react-force-graph-2d. Nodes are tables, edges are field relations. Supports zoom, pan, click-to-select, and fullscreen expand mode with slide-in detail panel.

ERD (Entity-Relationship Diagram)

Custom canvas renderer showing tables as cards with field lists. Draws relationship lines between related fields. Configurable: filter to PK relations only, limit fields shown per table.

Pages

Browse all ~5,600 BC page objects. Shows page name, caption, type (Card, List, Document, etc.), and source table binding. Links back to the table detail when clicked.

Coverage

Page coverage dashboard analyzing which tables are exposed on BC pages:

  • Stats row: Tables with pages, orphan tables, coverage %, total field-page mappings
  • Sortable table: Per-table page count, field coverage ratio, visible/enabled breakdowns
  • Filters: Show orphans only, sort by coverage %, page count, or field count
  • Click any table to navigate to it in the Tables view

Known Issues

  • BC custom API endpoints (tableMetadata, fields, pageControlMetadata, pageMetadata) return 404 when the BCMetadataAPI AL extension is not deployed to the target environment — app gracefully falls back to static JSON
  • Static JSON (bc-page-metadata.json) lacks the required field property — the infrastructure exists in code and will work automatically once BC API data includes it
  • The nul phantom file in the repo root (Windows reserved filename) requires using git add <files> instead of git add .

Recent Changes

  • c248856 - Parallel fetch fields+controls and switch cache to IndexedDB
  • 06c1f55 - Add step-by-step loading progress with status messages
  • a1e167e - Fix field property name: relationTableNo not relatedTableNo
  • 6891d4e - Add OData pagination to fetch all fields and page controls
  • c8517f7 - Restyle entire app to Material Design 3 with green tonal palette


Last updated: February 2026