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.
| Feature | Description |
|---|---|
| Table List | Searchable, sortable list of all BC tables with field counts and relation badges |
| Force Graph | Interactive force-directed graph of table relationships (expandable fullscreen) |
| ERD View | Canvas-based entity-relationship diagram with field-level detail |
| Pages View | Browse all BC page objects and their source table bindings |
| Coverage View | Page coverage dashboard — orphan tables, field-to-page mapping stats |
| Command Palette | Cmd+K quick search across all tables with recent history |
| Focus Mode | Isolate a table and its N-hop neighborhood in any view |
| Copilot | Built-in AI assistant for natural language schema queries |
| Property | Value |
|---|---|
| Repository | ProgressiveSurface/progressive-data-view |
| Hosting | Azure App Service (Node.js) |
| Auth | Azure EasyAuth (Entra ID) |
| Access | Internal 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:
| Endpoint | Records | Paginated |
|---|---|---|
tableMetadata | ~4,200 | No |
pageMetadata | ~5,600 | No |
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
| Setting | Value |
|---|---|
| App Service | progressive-data-view |
| Resource Group | PS-WEBAPPS |
| Runtime | Node 20 LTS (Linux) |
| Auth Provider | Azure EasyAuth (Entra ID) |
| Token Store | Enabled (BC API scope in loginParameters) |
| Public Access | Disabled (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=progressivesurfaceCI/CD
GitHub Actions workflow deploys on push to main:
- Checkout code
- Setup Node.js 20
npm ci+npm run build- Deploy to Azure App Service
Tech Stack
| Technology | Purpose |
|---|---|
| React 18 | UI framework |
| TypeScript | Type safety |
| Vite | Build tool and dev server |
| TailwindCSS 3 | Styling (Material Design 3 theme) |
| shadcn/ui | Component library (Radix primitives) |
| react-force-graph-2d | Force-directed graph visualization |
| cmdk | Command palette (Cmd+K) |
| Lucide React | Icons |
| IndexedDB | Client-side caching (replaced localStorage) |
| Azure EasyAuth | Authentication (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.
| Token | Usage |
|---|---|
--primary | Buttons, active states, progress bars |
--surface | Background layers |
--surface-container | Card backgrounds, input fields |
--outline-variant | Borders, dividers |
--muted-foreground | Secondary 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 devAccess 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 startKey Architecture
Data Loading (useBCSchemaData)
The main hook manages authentication, caching, and API fallback:
- IndexedDB cache — Checks for valid cached data (24h TTL, version-stamped)
- EasyAuth — Calls
/.auth/mefor user info + BC API access token - BC API — Fetches tables, pages, fields, and page controls (fields + controls in parallel)
- Enrichment — Joins field relations and page control usage onto table metadata
- 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.
| Property | Value |
|---|---|
| Storage | IndexedDB (bc-schema-cache database) |
| TTL | 24 hours |
| Version | Bumped 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 theBCMetadataAPIAL extension is not deployed to the target environment — app gracefully falls back to static JSON - Static JSON (
bc-page-metadata.json) lacks therequiredfield property — the infrastructure exists in code and will work automatically once BC API data includes it - The
nulphantom file in the repo root (Windows reserved filename) requires usinggit add <files>instead ofgit 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
Related
- Data Brain - ERP and data systems overview
- ERP Migration Tool - AFTEC to Business Central migration
- PSI Portal - Central app launcher
- PSI Explorer - Similar React+Vite+Tailwind stack
Last updated: February 2026