The Problem: Unpredictable Data Depth Different organizations structure their operations with different hierarchy depths and terminology. Hardcoding the UI for one structure was not practical; the system needed to adapt to arbitrary nesting.
Each level could also use different field types and validation rules. The interface had to render those fields dynamically from configuration.
The data sets were large enough that rendering every row at once would hurt responsiveness. The UI therefore needed to defer work until users expanded or reached the relevant parts of the hierarchy.
Recursive Component Architecture I designed an `ActivityRow` component that recursively renders itself. Each row checks: 'Do I have children?' If yes, render a nested table with another `ActivityRow` for each child. If no, render a leaf node. This recursion continues until the data runs out, naturally handling any depth.
The key was performance optimization. I implemented a virtualization strategy where only visible rows are in the DOM. Collapsed rows render a placeholder, deferring child rendering until needed.
State management was tricky. I used Redux to store the table data in a normalized structure (activities stored by ID, not as nested objects). This allowed efficient updates - changing a deep nested activity only requires updating one Redux slice, not traversing the entire tree. The component tree re-renders, but React's reconciliation handles it efficiently.
For UI responsiveness, I implemented debounced input fields. When a user types in a cell, the value updates immediately in local state (optimistic UI), while the Redux dispatch is debounced by 300ms. This makes the table feel instant while preventing Redux from processing hundreds of actions per second during rapid typing.
User-Defined Schemas & Business Rules To make the tool flexible, I built a configurable schema layer that lets administrators define field types and validation rules without changing the table components.
This schema is stored as JSON and loaded at app initialization. The `ActivityRow` component reads the schema for its current level and dynamically renders the appropriate input types. Need a dropdown? The schema includes the options. Need a date picker? The schema specifies the format.
The validation layer supports rules that depend on other fields and activities, while keeping those rules separate from the table's rendering logic.
D3.js Visualizations: Dual-Axis Graphs Beyond the table, the system required visualizations to help operators understand progress. I built graphs that compare planned and actual progress across multiple dimensions.
The challenge was overlaying multiple data series on the same graph while keeping it readable. I used D3's dual-scale system, creating separate scales for Planned (line with markers) and Actual (filled area). Color-coding and interactive legends let users toggle series on/off to focus on specific aspects.
Another visualization was a hierarchical 'Gantt chart' that showed the relationship between activities at different levels. I used D3's tree layout to position nodes, then drew connecting lines to show parent-child relationships. Clicking a node filters the table to show only that activity and its children, providing a powerful navigation tool.
PDF Export for Regulatory Compliance Operational plans also needed to be shared as PDF documents. I built an export system that converts the table and graphs into a multi-page PDF using jsPDF.
The challenge was pagination. The table could be arbitrarily large, spanning dozens of pages. I implemented a 'virtual page' system that measures the height of each row, calculates page breaks, and splits the table across pages while preserving the hierarchy (a parent row and its children stay together).
For graphs, I rendered them to Canvas at an appropriate resolution and embedded the result in the PDF alongside the tabular data.