← Back to projects

Energy Tech

Energy Analytics Dashboard

2025 – 2026

Overview

Customer-facing analytics portal for an AI-powered energy optimisation platform targeting commercial real estate portfolios. The application gives facility managers and sustainability officers a real-time view of energy consumption, AI-driven savings, and regulatory compliance across their entire building estate — from portfolio level down to individual device measurement points.

Architecture

React SPA (custom Webpack — no meta-framework)
  ├── OpenAPI-generated TypeScript client
  │     └── Energy Platform REST API (IoT device telemetry)
  │
  ├── Analytics module
  │     └── Recharts — adaptive multi-axis trend charts
  │           dynamic X-tick rotation · dual Y-axis scaling
  │
  ├── GEG Compliance module (German Building Energy Act)
  │     └── Document management · structured PDF downloads
  │
  └── Auth module
        └── 2FA flow · DE/EN i18n (i18next) · Mitt event bus

Org tree: Portfolio → Building → Floor → Device → Measurement

Key Contributions

Code Examples

Adaptive X-Axis Tick Configuration

Charts with dense time-series data need different label strategies at different viewport widths. This function measures available space per tick at runtime and returns optimal rotation angle, tick interval, and offsets — preventing label overlap at any screen size.

← drag to resizehorizontal
JanFebMarAprMayJunJulAugSepOctNovDec
components/molecules/TrendsChart/TrendsChart.tsx
export const xTickParams = (numValues: number | undefined) => {
  if (!numValues)
    return { angle: 0, margin: 10, interval: 0, space: 0, bottom: 0, dx: 0, dy: 0 } as const;

  const parentElement = document.querySelector("main") || document.querySelector("body");
  const width = parentElement?.clientWidth || 400;
  const space = width / numValues;

  if (space < 27)
    return { bottom: 70, angle: -90, margin: 40, interval: Math.round(27 / space), dx: -5, dy: -32, space } as const;
  if (space < 40)
    return { bottom: 70, angle: -90, margin: 40, interval: 0, dx: -5, dy: -32, space } as const;
  if (space < 100)
    return { bottom: 55, angle: -40, margin: 36, interval: 0, dx: 0, dy: -28, space } as const;

  return { bottom: 15, angle: 0, margin: 10, interval: 0, dx: 0, dy: 0, space } as const;
};

// In TrendsChart component — recalculates on resize:
const [xTick, setXTick] = useState(xTickParams(chartData?.length));

useEffect(() => {
  const handleResize = debounce(() => setXTick(xTickParams(chartData?.length)), 100);
  window.addEventListener("resize", handleResize);
  return () => window.removeEventListener("resize", handleResize);
}, [chartData?.length]);

Tech Stack

Framework
  • React
  • TypeScript
  • Custom Webpack
Data & State
  • React Router
  • OpenAPI (generated client)
  • Mitt
Charts & UI
  • Recharts
  • Tailwind CSS
i18n & Auth
  • i18next
  • 2FA flow
Testing
  • Jest
  • React Testing Library
  • React Cosmos