dts-universal-report-module
v1.0.7
Published
> A fully featured, embeddable React reporting component for Digital Twin Solutions applications. > Drop it into any MERN-stack host app and get a complete data table with filtering, sorting, > grouping, views, presets, export, audit history, and schedu
Readme
Universal Report Module (URM)
A fully featured, embeddable React reporting component for Digital Twin Solutions applications. Drop it into any MERN-stack host app and get a complete data table with filtering, sorting, grouping, views, presets, export, audit history, and scheduled reports — out of the box.
Table of Contents
- Overview
- Features
- Tech Stack
- Architecture
- Folder Structure
- Prerequisites
- Installation & Setup
- Running the Development Server
- Building the Library
- Environment Configuration
- Usage — Integrating URM into a Host App
- Data Source Options
- Public API Reference
- Branching Strategy
- Commit Message Format
- Code Standards
- Developer Notes
Overview
The Universal Report Module (URM) is a reusable React + TypeScript npm package built for Digital Twin Solutions. It provides two primary surfaces:
URMBuilder— A full report creation and management interface. Users can upload JSON data, define column schemas, apply filters, sort, group, manage saved views, create presets, export data, and review a complete audit history.URMViewer— A read-optimised embedded viewer for host applications that already have data. It accepts data directly via props or an API source and renders the full table experience without the upload or report-creation workflow.
Both surfaces are backed by the same core engine (UseReportEngine) and can be configured,
themed, and extended by the host application.
Features
Data Ingestion
- JSON file upload with automatic schema detection
- Inline data via props (
data={myArray}) - API source with optional auto-refresh (
dataSource={{ type: 'api', url: '...' }}) - Async callback source (
dataSource={{ type: 'callback', fetch: async () => data }}) - Schema conflict detection and resolution UI
Table & Display
- Resizable, reorderable columns via drag-and-drop
- Card grid view toggle (alternative to table rows)
- Sticky header and frozen column support
- Row selection (single, range, select-all)
- Inline row detail, edit, and delete modals
- Favourited rows and favourites-only filter toggle
- Empty state and error state components
Filtering
- Multi-column filter panel
- Operators: equals, contains, startsWith, endsWith, gt, lt, gte, lte, isTrue, isFalse, NULL, NOT_NULL
- Per-column filter clear
- Filter conflict detection with user warnings
Sorting
- Multi-level sort stack (unlimited sort rules)
- Drag-to-reorder sort priority
- Column header click cycling (asc → desc → clear)
- Sort panel with full rule management
Grouping
- Multi-level row grouping (up to N levels)
- Collapsible group rows
- Group aggregate display (count, sum, min, max)
- Drag-to-reorder group levels
Views
- Named, saved view snapshots (filters + sort + columns + grouping)
- Default view designation
- Favourite views
- Dirty-state detection with diff summary modal
- View restore and permanent delete
Presets & Sharing
- Named saved presets (configuration snapshots)
- Shareable preset links via URL encoding
- Preset management panel (apply, delete, copy link)
- Active preset badge
Export
- Export to CSV, Excel (
.xlsx), and JSON - Configurable export options (selected rows, full data, current view)
- Dirty-view export warning
- Bulk export for selected rows
Scheduled Reports
- Schedule-based auto-export (daily, weekly, monthly)
- Email notification integration
- Scheduler management modal
Audit & History
- Full audit trail of all user actions (filters, sorts, views, presets, exports)
- Audit history panel with collapsible timeline
- Audit events written to report state
Search
- Global search across all visible columns
- Search history with favourites
- Persistent search history per report
Pagination
- Configurable page size (10, 25, 50, 100, all)
- Page navigation controls
- Total row count display
Permissions
- Role-based access:
owner,editor,viewer - Configurable permission gates per action
- View-only mode badge
Developer Tools
- Hidden dev config panel (Ctrl+Shift+D or
?devpanel=true) - Feature flags: audit, presets, export, permissions
- Default filter/sort/column overrides for testing
Tech Stack
| Layer | Technology | Version |
|---|---|---|
| UI Framework | React | ^18.3.1 |
| Language | TypeScript | via @types/react ^18.3.18 |
| Component Library | Material UI (MUI) | >=5 |
| Styling | MUI sx prop + Emotion | >=11 |
| Drag & Drop | react-dnd + HTML5Backend | 16.0.1 |
| Export | xlsx (SheetJS) | ^0.18.5 |
| Build Tool | Vite | ^6.4.2 |
| Linting | ESLint + TypeScript plugin | — |
| Formatting | Prettier | — |
| Git Hooks | Husky + lint-staged | — |
Peer dependencies required in the host app:
react >= 18react-dom >= 18@mui/material >= 5@emotion/react >= 11@emotion/styled >= 11
Architecture
URM follows a layered core engine architecture. All business logic lives in the core/
directory as custom hooks. Components in app/components/ are purely presentational — they
receive state and handlers from the engine via React Context, and never manage business logic
directly.
┌─────────────────────────────────────────────────────────────┐
│ Host Application │
│ │
│ <URMBuilder /> or <URMViewer /> or <URMProvider /> │
└───────────────────────┬─────────────────────────────────────┘
│
┌───────────────────────▼─────────────────────────────────────┐
│ ReportProvider (Context) │
│ Owns all state. Exposes engine via useReportContext(). │
└───────────────────────┬─────────────────────────────────────┘
│
┌───────────────────────▼─────────────────────────────────────┐
│ UseReportEngine │
│ Master hook — orchestrates all sub-engines below. │
│ │
│ ┌──────────────┐ ┌─────────────┐ ┌───────────────────────┐ │
│ │UseFilterEngine│ │UseSortEngine│ │ UseGroupingEngine │ │
│ └──────────────┘ └─────────────┘ └───────────────────────┘ │
│ ┌──────────────┐ ┌─────────────┐ ┌───────────────────────┐ │
│ │UseColumnEngine│ │UseViewEngine│ │ UseSearchEngine │ │
│ └──────────────┘ └─────────────┘ └───────────────────────┘ │
│ ┌──────────────┐ ┌─────────────┐ │
│ │UseTableEngine│ │UsePagination│ │
│ └──────────────┘ └─────────────┘ │
└───────────────────────┬─────────────────────────────────────┘
│
┌───────────────────────▼─────────────────────────────────────┐
│ UI Components (src/app/components/) │
│ Pure presentation. No local business logic. │
│ Read state from context. Call handlers from engine. │
└─────────────────────────────────────────────────────────────┘Data flow:
Host provides data → ReportProvider → UseReportEngine processes →
Context updated → Components re-render with new stateFolder Structure
universal-report-module/
├── src/
│ ├── index.ts # Public package API — only export from here
│ ├── main.tsx # Dev-only entry point (not included in build)
│ │
│ ├── app/
│ │ ├── app.tsx # Dev-only root with URL-based mode routing
│ │ │
│ │ ├── components/ # All UI components (44 files)
│ │ │ ├── data-table.tsx # Core table with all interaction features
│ │ │ ├── report-viewer.tsx # Orchestrator — renders panels and modals
│ │ │ ├── filter-panel.tsx # Filter rule management
│ │ │ ├── sort-panel.tsx # Sort stack management
│ │ │ ├── group-panel.tsx # Grouping level management
│ │ │ ├── manage-columns-panel.tsx
│ │ │ ├── manage-views-panel.tsx
│ │ │ ├── manage-presets-panel.tsx
│ │ │ ├── export-config-modal.tsx
│ │ │ ├── scheduled-reports-modal.tsx
│ │ │ ├── audit-history-panel.tsx
│ │ │ ├── error-boundary.tsx
│ │ │ └── ... # 32 additional focused components
│ │ │
│ │ ├── devconfig/ # Developer configuration panel (hidden)
│ │ │ ├── dev-config-context.ts
│ │ │ ├── dev-config-modal.tsx
│ │ │ └── urm-dev-config.ts
│ │ │
│ │ ├── modules/ # Public-facing entry surfaces
│ │ │ ├── builder/
│ │ │ │ ├── index.tsx # URMBuilder — default export with provider wrap
│ │ │ │ └── builder.tsx # Builder UI (requires context to be provided)
│ │ │ └── viewer/
│ │ │ ├── index.tsx # URMViewer — default export with provider wrap
│ │ │ ├── viewer.tsx # Viewer UI (requires context to be provided)
│ │ │ └── urm-viewer.tsx # Alternative viewer export
│ │ │
│ │ ├── types/ # TypeScript interfaces and types
│ │ │ ├── report.ts # Report, View, Filter, SortRule, ColumnSchema, etc.
│ │ │ ├── data-source.ts # DataSourceConfig, DataLoadState
│ │ │ ├── grouping.ts # GroupNode (for group tree traversal)
│ │ │ └── scheduler.ts # ScheduledReport, ScheduleFrequency
│ │ │
│ │ └── utils/ # Pure utility functions (no React)
│ │ ├── logger.ts # Centralised logging (replaces console.*)
│ │ ├── error-handling.ts # Error classification and recovery logic
│ │ ├── export.ts # CSV, Excel, JSON export helpers
│ │ ├── filtering.ts # Filter application logic
│ │ ├── sorting.ts # Multi-level sort comparators
│ │ ├── grouping.ts # Data grouping and aggregate calculation
│ │ ├── schema-detector.ts # JSON schema inference
│ │ ├── view-helpers.ts # View state diff and snapshot helpers
│ │ ├── presets.ts # Preset capture, apply, share-link encoding
│ │ ├── persistence.ts # localStorage read/write for report state
│ │ ├── audit.ts # Audit event creation and appending
│ │ ├── use-scheduler-engine.ts # Scheduled report execution hook
│ │ └── ... # 9 additional utility modules
│ │
│ ├── core/ # Engine layer — all business logic hooks
│ │ ├── index.ts # Core exports
│ │ ├── report-context.tsx # React Context + Provider
│ │ ├── use-report-engine.ts # Master orchestration hook (~2000 lines)
│ │ ├── use-filter-engine.ts
│ │ ├── use-sort-engine.ts
│ │ ├── use-grouping-engine.ts
│ │ ├── use-column-engine.ts
│ │ ├── use-view-engine.ts
│ │ ├── use-search-engine.ts
│ │ ├── use-table-engine.ts
│ │ └── use-pagination-engine.ts
│ │
│ └── styles/
│ ├── globals.css # Base CSS resets
│ └── index.css # Entry CSS
│
├── eslint.config.js # ESLint rules (TypeScript + React)
├── .prettierrc # Prettier formatting config
├── tsconfig.json # TypeScript compiler config (strict mode)
├── vite.config.ts # Vite build config (lib mode + dev mode)
├── package.json # Package metadata, scripts, lint-staged
├── global.d.ts # Global type declarations
├── index.html # Dev-only HTML entry
└── .gitignorePrerequisites
| Requirement | Minimum Version |
|---|---|
| Node.js | >= 18.x |
| npm | >= 9.x |
| Git | any recent version |
Installation & Setup
1. Clone the repository
git clone https://github.com/Digital-Twin-Operations/universal-report-module.git
cd universal-report-module2. Switch to the develop branch
All active development happens on develop. Never work directly on main.
git checkout develop
git pull origin develop3. Install dependencies
npm install4. Set up environment (if needed)
There are no required environment variables for local development of this library.
If you add one in future, copy .env.example and fill values locally:
cp .env.example .envNever commit .env — it is in .gitignore.
Running the Development Server
The project includes a main.tsx and app.tsx for local development and testing of
components without needing a host application.
npm run devOpen http://localhost:5173 in your browser.
URL parameters for switching modes:
| URL | What it shows |
|---|---|
| http://localhost:5173 | Builder surface (default) |
| http://localhost:5173?mode=viewer | Viewer surface |
| http://localhost:5173?devpanel=true | Builder + dev config gear icon visible |
Hidden developer panel keyboard shortcut:
Ctrl + Shift + D — opens the dev config panel in any mode.
Building the Library
npm run buildThis produces the distributable package in dist/:
dist/
├── index.js # CommonJS build
├── index.esm.js # ES module build
└── index.d.ts # TypeScript declarationsThe build is configured in vite.config.ts in library mode. All peer dependencies
(react, @mui/material, @emotion/*) are externalized and not bundled.
Environment Configuration
| Variable | Description | Example | |---|---|---| | (none required) | This library has no required env vars | — |
If you add environment variables in future:
- Prefix with
VITE_for client-side access - Document them here and in
.env.example - Never hardcode values in source files
Usage — Integrating URM into a Host App
Install the package
npm install dts-universal-report-moduleOption 1 — Builder (full report creation UI)
import { URMBuilder } from 'dts-universal-report-module';
function MyPage() {
return <URMBuilder />;
}Option 2 — Builder with inline data (skip upload)
import { URMBuilder } from 'dts-universal-report-module';
function MyPage() {
const data = [
{ id: 1, name: 'Device A', status: 'online', temperature: 72.4 },
{ id: 2, name: 'Device B', status: 'offline', temperature: 68.1 },
];
return <URMBuilder data={data} reportName="Device Status" />;
}Option 3 — Builder with API data source
import { URMBuilder } from 'dts-universal-report-module';
function MyPage() {
return (
<URMBuilder
dataSource={{
type: 'api',
url: '/api/v1/devices/readings',
headers: { Authorization: `Bearer ${token}` },
refreshInterval: 30000, // auto-refresh every 30 seconds
}}
reportName="Live Device Readings"
/>
);
}Option 4 — Viewer (read-only, data from host)
import { URMViewer } from 'dts-universal-report-module';
function Dashboard() {
const data = useSelector(selectDeviceData);
return <URMViewer data={data} />;
}Option 5 — Custom layout with URMProvider
For full control over the layout, use the provider directly:
import { URMProvider, useURMContext } from 'dts-universal-report-module';
function CustomReport() {
const engine = useURMContext();
return (
<div>
<MyCustomHeader reportCount={engine.reports.length} />
{/* render your own layout using engine state */}
</div>
);
}
function App() {
return (
<URMProvider mode="builder" data={myData}>
<CustomReport />
</URMProvider>
);
}Data Source Options
| Type | Props | Auto-refresh |
|---|---|---|
| JSON file upload | (no props needed) | No |
| Inline array | data={myArray} | No |
| API endpoint | dataSource={{ type: 'api', url: '...' }} | Yes (optional) |
| Async callback | dataSource={{ type: 'callback', fetch: async () => data }} | Yes (optional) |
Public API Reference
All public exports are defined in src/index.ts.
Components
| Export | Description |
|---|---|
| URMBuilder | Full report creation + management surface |
| URMViewer | Embedded read-only viewer |
| URMBuilderRaw | Builder without provider wrap — bring your own URMProvider |
| URMViewerRaw | Viewer without provider wrap — bring your own URMProvider |
| URMProvider | React context provider — use for custom layouts |
Hooks
| Export | Description |
|---|---|
| useURMContext | Access full engine state and handlers inside URMProvider |
Types
| Export | Description |
|---|---|
| Report | Full report object shape |
| DataSourceConfig | Union type for API and callback data sources |
| DataLoadState | 'idle' \| 'loading' \| 'ready' \| 'error' \| 'refreshing' |
Branching Strategy
| Branch | Purpose | Rules |
|---|---|---|
| main | Production-ready builds | Never commit directly. Merge from develop only when releasing. |
| develop | Active development | All feature work merges here. Always deployable. |
| feature/* | New features | Branch from develop. Merge back to develop via PR. |
| bugfix/* | Non-critical bug fixes | Branch from develop. Merge back to develop. |
| hotfix/* | Urgent production fixes | Branch from main. Merge to both main and develop. |
Creating a feature branch:
git checkout develop
git pull origin develop
git checkout -b feature/urm-123-short-descriptionMerging back to develop:
git checkout develop
git merge feature/urm-123-short-description
git push origin developCommit Message Format
All commits must follow Conventional Commits format:
type(scope): short description in imperative mood| Type | When to use |
|---|---|
| feat | New feature or capability |
| fix | Bug fix |
| refactor | Code restructure with no behaviour change |
| docs | Documentation only |
| chore | Tooling, dependencies, config |
| test | Tests added or updated |
| perf | Performance improvement |
Examples:
git commit -m "feat(export): add JSON export option to export config modal"
git commit -m "fix(filter): resolve null crash when filter value is empty string"
git commit -m "refactor(structure): rename all files to kebab-case convention"
git commit -m "docs(readme): add complete project overview and developer guide"
git commit -m "chore(toolchain): add eslint, prettier, and husky configuration"Rules:
- Subject line max 72 characters
- Use imperative mood: "add", "fix", "update" — not "added", "fixes"
- Scope = the module or area affected
Code Standards
This project follows the MERN Engineering Standards v1.0 document. Key rules enforced by ESLint and Prettier:
- All file names:
kebab-case(e.g.data-table.tsx) - All folder names:
kebab-case(e.g.modules/builder/) - React component names:
PascalCase(export name inside the file) - Hook names:
camelCasewithuseprefix - No
console.log— useloggerfromsrc/app/utils/logger.ts - No
console.errororconsole.warndirectly — uselogger - No inline
style={{}}— use MUIsxprop - No hardcoded colour values — use MUI theme tokens
- No function longer than 80 lines
- No commented-out dead code
- All code through
developbranch — never commit tomain
Developer Notes
Why is UseReportEngine.ts so large?
use-report-engine.ts (~2,000 lines) is the master orchestration hook. It intentionally
centralises all state management and handler definitions in one place so that the
ReportContext can expose a single, stable API to all components. This is a known area
for future decomposition — the sub-engines (use-filter-engine, use-sort-engine, etc.)
are already extracted; the remaining work is to move more handler logic into them.
Why does the build externalise so many dependencies?
URM is designed to be embedded in host applications that already have React and MUI installed.
Bundling them again would cause version conflicts and bloat the package. All peer dependencies
are listed in package.json > peerDependencies and must be present in the host app.
Dev panel
The developer configuration panel is deliberately hidden from production UI. It can be
accessed via Ctrl+Shift+D or the ?devpanel=true URL parameter. It allows toggling
feature flags, setting default filter/sort/column configs, and simulating different user roles.
It is excluded from the published package build.
Adding a new utility
- Create the file in
src/app/utils/with akebab-casename - Export only what other files need
- Import using relative paths, e.g.
import { myUtil } from '../utils/my-util' - If it needs logging, import from
./logger
Adding a new component
- Create the file in
src/app/components/with akebab-casename - Export the component as a named export using
PascalCase - Do not manage business logic inside components — use
useReportContext() - Do not use
style={{}}— use MUIsxprop only
