Problemet: Uforutsigbar datadybde Ulike oljeselskaper strukturerer sine boreoperasjoner med vidt forskjellige hierarkinivåer. AkerBP kan bruke en 3-nivåers struktur (Operasjon → Fase → Aktivitet), mens Equinor bruker 6 nivåer (Brønn → Seksjon → Operasjon → Underoperasjon → Oppgave → Steg). For å gjøre vondt verre, bruker de forskjellige begreper når de refererer til de samme konseptene. Å hardkode brukergrensesnittet for en spesifikk dybde var umulig - systemet måtte tilpasse seg vilkårlig nesting.
Ytterligere kompliserende forhold: hvert nivå kunne ha forskjellige felttyper. Toppnivå-operasjoner kan ha et 'Startdato'-felt, mens oppgaver på dypeste nivå kan ha 'Estimert varighet i timer'. Systemet måtte rendre forskjellige input-typer dynamisk basert på nivåets skjema.
Ytelsesutfordringen var enorm. En enkelt boreplan kunne inneholde 500+ aktiviteter, hver med 10-20 felter. Å rendre alt dette i en tabell (5 000+ DOM-noder) ville fryse nettleseren. Likevel måtte UI-et føles øyeblikkelig - riggoperatører tolererer ikke forsinkelser når de oppdaterer kritiske boreparametere.
Rekursiv komponentarkitektur Jeg designet en `ActivityRow`-komponent som rekursivt rendrer seg selv. Hver rad sjekker: 'Har jeg barn?' Hvis ja, render en nestet tabell med en annen `ActivityRow` for hvert barn. Hvis nei, render en bladnode. Denne rekursjonen fortsetter til dataene tar slutt, og håndterer naturlig enhver dybde.
Trikset var ytelsesoptimalisering. Å rendre 500 nestede komponenter naivt ville vært tregt. Jeg implementerte en virtualiseringsstrategi hvor bare synlige rader er i DOM-en. Kollapsede rader (hvor brukeren ikke har utvidet barna) rendrer et placeholder-element, og utsetter rendering av barn til det er nødvendig.
Tilstandshåndtering var vanskelig. Jeg brukte Redux for å lagre tabelldataene i en normalisert struktur (aktiviteter lagret etter ID, ikke som nestede objekter). Dette tillot effektive oppdateringer - endring av en dyp nestet aktivitet krever bare oppdatering av én Redux-slice, ikke traversering av hele treet. Komponenttreet re-rendrer, men Reacts reconciliation håndterer det effektivt.
For UI-responsivitet implementerte jeg 'debounced' input-felter. Når en bruker skriver i en celle, oppdateres verdien umiddelbart i lokal tilstand (optimistisk UI), mens Redux-dispatch debounces med 300ms. Dette gjør at tabellen føles øyeblikkelig samtidig som det forhindrer Redux fra å behandle hundrevis av handlinger per sekund under rask skriving.
Brukerdefinerte skjemaer og forretningsregler For å gjøre verktøyet fleksibelt bygde jeg en 'skjemamotor' som lar klienter definere sine egne felttyper og valideringsregler. En klient logger inn på et admin-panel og kan si: 'Nivå 3-aktiviteter skal ha et Dybde-felt (number input, validert mellom 0-10000).'
Dette skjemaet lagres som JSON og lastes ved app-initialisering. `ActivityRow`-komponenten leser skjemaet for sitt nåværende nivå og rendrer dynamisk de passende input-typene. Trenger du en dropdown? Skjemaet inkluderer alternativene. Trenger du en datoplukker? Skjemaet spesifiserer formatet.
Forretningsregler var enda mer komplekse. En klient kan definere: 'Hvis aktivitetstypen er Boring, må Dybde-feltet være større enn forrige aktivitets dybde.' Disse reglene lagres som JavaScript-funksjoner (serialisert som strenger) og evalueres ved kjøretid.
Sikkerhetsrisikoen ved å evaluere vilkårlig JavaScript er åpenbar. Jeg reduserte dette ved å kjøre regelevaluering i en sandkasset kontekst ved bruk av Function-konstruktøren med begrenset scope. Regelfunksjonene har bare tilgang til gjeldende aktivitetsdata og verktøyfunksjoner jeg tilbyr - ingen tilgang til global tilstand eller DOM.
D3.js-visualiseringer: Dual-Axis-grafer Utover tabellen krevde systemet komplekse visualiseringer for å hjelpe riggoperatører med å forstå fremdriften deres. Jeg bygde en to-akset graf som plotter Planlagt vs. Faktisk fremdrift på to dimensjoner samtidig: Tid (X-akse) og Dybde (Y-akse).
Utfordringen var å overlappe flere dataserier på samme graf samtidig som man holder den lesbar. Jeg brukte D3s dual-scale-system, og opprettet separate skalaer for Planlagt (linje med markører) og Faktisk (fylt område). Fargekoding og interaktive legender lar brukere slå serier av/på for å fokusere på spesifikke aspekter.
En annen visualisering var et hierarkisk 'Gantt-diagram' som viste forholdet mellom aktiviteter på forskjellige nivåer. Jeg brukte D3s tree-layout for å posisjonere noder, deretter tegnet jeg koblingslinjer for å vise foreldre-barn-relasjoner. Klikking på en node filtrerer tabellen for å vise bare den aktiviteten og dens barn, noe som gir et kraftig navigasjonsverktøy.
PDF-eksport for regulatorisk overholdelse Boreplaner må sendes til reguleringsmyndigheter som PDF-dokumenter. Jeg bygde et eksportsystem som konverterer hele tabellen og grafene til en multi-side PDF ved bruk av jsPDF.
Utfordringen var paginering. Tabellen kunne være vilkårlig stor og strekke seg over dusinvis av sider. Jeg implementerte et 'virtuelt side'-system som måler høyden på hver rad, beregner sideskift og deler tabellen over sider samtidig som hierarkiet bevares (en foreldrerekke og dens barn holdes sammen).
For grafer rendret jeg dem til høyoppløselige Canvas-elementer (2x-skala for printkvalitet), deretter bygde jeg Canvas inn i PDF-en som bilder. Dette produserte profesjonelle rapporter som oppfylte reguleringsmyndighetenes formateringskrav.