The Problem: Unpredictable Data Depth Different oil companies structure their drilling operations with vastly different hierarchy levels. AkerBP might use a 3-level structure (Operation → Phase → Activity), while Equinor uses 6 levels (Well → Section → Operation → Sub-operation → Task → Step). To make matters worse, they use different terms when referring to the same concepts. Hardcoding the UI for a specific depth was impossible - the system needed to adapt to arbitrary nesting.
Further complicating matters: each level could have different field types. Top-level operations might have a 'Start Date' field, while tasks at the deepest level might have 'Estimated Duration in Hours'. The system needed to render different input types dynamically based on the level's schema.
The performance challenge was enormous. A single drilling plan could contain 500+ activities, each with 10-20 fields. Rendering all of this in a table (5,000+ DOM nodes) would freeze the browser. Yet the UI needed to feel instant - rig operators don't tolerate lag when updating critical drilling parameters.
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 trick was performance optimization. Rendering 500 nested components naively would be slow. I implemented a virtualization strategy where only visible rows are in the DOM. Collapsed rows (where the user hasn't expanded the children) render a placeholder element, 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 'schema engine' that lets clients define their own field types and validation rules. A client logs into an admin panel and can say: 'Level 3 activities should have a Depth field (number input, validated between 0-10000).'
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.
Business rules were even more complex. A client might define: 'If the Activity Type is Drilling, the Depth field must be greater than the previous activity's depth.' These rules are stored as JavaScript functions (serialized as strings) and evaluated at runtime.
The security risk of evaluating arbitrary JavaScript is obvious. I mitigated this by running rule evaluation in a sandboxed context using the Function constructor with a restricted scope. The rule functions only have access to the current activity data and utility functions I provide - no access to global state or DOM.
D3.js Visualizations: Dual-Axis Graphs Beyond the table, the system required complex visualizations to help rig operators understand their progress. I built a dual-axis graph that plots Planned vs. Actual progress on two dimensions simultaneously: Time (X-axis) and Depth (Y-axis).
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 Drilling plans must be submitted to regulatory agencies as PDF documents. I built an export system that converts the entire 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 high-resolution Canvas elements (2x scale for print quality), then embedded the Canvas into the PDF as images. This produced professional-looking reports that met the regulatory agencies' formatting requirements.