Skip to main content

Frontend Configuration Migration

Feature: Data-Driven Configuration System Date: October 2025 Status: ✅ Core Infrastructure Complete - Component Migration In Progress Purpose: Migrate from hardcoded constants to backend-driven configuration


Table of Contents

  1. Overview
  2. Architecture
  3. Implementation
  4. Migration Strategy
  5. Testing
  6. Rollout Plan

1. Overview

1.1 Project Goal

Migrate from hardcoded frontend constants to a data-driven backend configuration system, eliminating the need for hardcoded values in constants.jsx and providing a flexible, maintainable configuration architecture.

1.2 Problem Solved

Before:

// constants.jsx - Hardcoded values (hundreds of lines)
export const ENTITY_TYPES = {
'TANK': { id: 'TANK', label: 'Tank', ... },
'JUNCTION': { id: 'JUNCTION', label: 'Junction', ... },
// ... 20+ more entities
};

export const METRICS = {
'Flow': { id: 'Flow', label: 'Flow', aggregators: ['Avg','Min','Max'], ... },
// ... 30+ more metrics
};

Issues:

  • ❌ Configuration changes require code deployment
  • ❌ No central source of truth
  • ❌ Duplicate data between frontend and backend
  • ❌ Hard to maintain and synchronize
  • ❌ No runtime flexibility

After:

// Load configuration from API
const { config, loading } = useConfiguration();

// Use data-driven constants
const entityTypes = config.entityTypes;
const metrics = config.metrics;

Benefits:

  • ✅ Single source of truth (database)
  • ✅ Configuration changes without deployment
  • ✅ Consistent across frontend and backend
  • ✅ Easy to maintain and extend
  • ✅ Runtime flexibility

2. Architecture

2.1 Backend Configuration Models

Database Models:

  1. EntityTypeConfig - Network entity definitions

    • Id, Name, Category, PropertyTypes, AvailableMetrics
    • Example: Tank, Junction, Pipe, Pump, Valve
  2. MetricConfig - Measurement definitions

    • Id, Name, Label, Category, Aggregators, Precision
    • Example: Flow, Pressure, Demand, Quality
  3. VisualizationConfig - Heatmap and rendering settings

    • MetricId, ColorStops, RendererType, MinValue, MaxValue
    • Color gradients for map visualization
  4. ModelUnit - Measurement units

    • Id, FieldName, Context, USUnit, SIUnit, Precision
    • Enhanced with context support for duplicate field names

2.2 API Endpoints

Configuration Controller:

  • GET /api/Configuration/all - Complete configuration ⭐ (Recommended)
  • GET /api/Configuration/for-entity/{entityType} - Entity-specific
  • GET /api/Configuration/for-metric/{metricId} - Metric-specific
  • GET /api/Configuration/summary - Summary data

Specialized Controllers:

  • /api/EntityTypes/ - CRUD for entity types
  • /api/Metrics/ - CRUD for metrics
  • /api/VisualizationConfig/ - CRUD for viz configs
  • /api/Units/ - Unit management

2.3 JSON Seed Files

Configuration stored in JSON for easy maintenance:

  • entity-types.json - Entity definitions
  • metrics.json - Metric configurations
  • visualization-config.json - Viz settings
  • model-units.json - Unit definitions
  • constants.json - System constants (simplified)

3. Implementation

3.1 Frontend Infrastructure ✅

1. Configuration Context

File: src/contexts/ConfigurationContext.jsx

export const ConfigurationContext = createContext();

export const ConfigurationProvider = ({ children }) => {
const [config, setConfig] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
fetchConfiguration();
}, []);

const fetchConfiguration = async () => {
try {
const response = await fetch('/api/Configuration/all');
const data = await response.json();
setConfig(data);
// Cache for 1 hour
localStorage.setItem('dwd_config', JSON.stringify(data));
localStorage.setItem('dwd_config_timestamp', Date.now());
} catch (err) {
// Fallback to cached data
const cached = localStorage.getItem('dwd_config');
if (cached) setConfig(JSON.parse(cached));
setError(err);
} finally {
setLoading(false);
}
};

return (
<ConfigurationContext.Provider value={{ config, loading, error, fetchConfiguration }}>
{children}
</ConfigurationContext.Provider>
);
};

export const useConfiguration = () => {
const context = useContext(ConfigurationContext);
if (!context) {
throw new Error('useConfiguration must be used within ConfigurationProvider');
}
return context;
};

2. Migration Utilities

File: src/utils/configurationMigration.jsx

Provides backward compatibility layer:

// Legacy constant mappings
export const getLegacyEntityTypes = (config) => {
if (!config?.entityTypes) return LEGACY_ENTITY_TYPES;
// Map to legacy format
};

export const getLegacyMetrics = (config) => {
if (!config?.metrics) return LEGACY_METRICS;
// Map to legacy format
};

// Migration status checking
export const isMigrated = (componentName) => {
const migrated = getMigratedComponents();
return migrated.includes(componentName);
};

3. Updated Units Context

File: src/contexts/UnitsContext.jsx

// Now uses configuration data when available
const { config } = useConfiguration();

const units = useMemo(() => {
if (config?.modelUnits) {
return processUnitsFromConfig(config.modelUnits);
}
// Fallback to direct API call
return fetchUnitsDirectly();
}, [config]);

4. App Integration

File: src/App.jsx

<ConfigurationProvider>
<UnitsProvider>
<AuthProvider>
{/* ... other providers */}
</AuthProvider>
</UnitsProvider>
</ConfigurationProvider>

3.2 Backend Services ✅

Services Created:

  1. NetworkConfigurationService - Database access for config models
  2. NetworkConfigurationSeedingService - JSON file loading and seeding

Integration:

  • Services moved to EN2PostgresContext project
  • No new project dependencies required
  • Integrated into NetworkBuilder

4. Migration Strategy

4.1 Phased Approach

Phase 1: Infrastructure ✅ COMPLETE

  • Configuration context created
  • API endpoints implemented
  • Backend models and services
  • JSON seed files
  • Caching strategy

Phase 2: Component Migration 🔄 IN PROGRESS

  • Migrate components one by one
  • Each component gets updated to use configuration context
  • Maintain backward compatibility during transition

Phase 3: Cleanup ⏸️ PENDING

  • Remove old constants.jsx
  • Remove migration utilities
  • Remove legacy fallbacks

4.2 Component Migration Pattern

General Pattern:

Before:

import { ENTITY_TYPES, METRICS } from '../constants';

const MyComponent = () => {
const entityTypes = ENTITY_TYPES;
const metrics = METRICS;
// ...
};

After:

import { useConfiguration } from '../contexts/ConfigurationContext';

const MyComponent = () => {
const { config, loading } = useConfiguration();

if (loading) return <CircularProgress />;

const entityTypes = config.entityTypes;
const metrics = config.metrics;
// ...
};

4.3 Backward Compatibility

During migration, components can use both approaches:

import { useConfiguration } from '../contexts/ConfigurationContext';
import { ENTITY_TYPES } from '../constants'; // Fallback

const MyComponent = () => {
const { config } = useConfiguration();
const entityTypes = config?.entityTypes || ENTITY_TYPES;
// Component works with either data source
};

5. Testing

5.1 Test Components

ConfigurationTest Component:

File: src/components/ConfigurationTest.jsx

Features:

  • Visual verification of configuration loading
  • Migration status display
  • Data validation
  • Error state display

Usage:

  • Navigate to /about page
  • View configuration loading status
  • Inspect loaded data
  • Check migration progress

5.2 API Testing

Test Configuration Endpoint:

curl http://localhost:5000/api/Configuration/all

# Expected response:
{
"entityTypes": [...],
"metrics": [...],
"visualizationConfigs": [...],
"constants": {...},
"modelUnits": [...]
}

5.3 Component Testing

For each migrated component:

  1. ✅ Verify configuration loads
  2. ✅ Check dropdowns populate correctly
  3. ✅ Verify metrics display properly
  4. ✅ Test with cached data (offline mode)
  5. ✅ Verify error handling
  6. ✅ Check backward compatibility

6. Rollout Plan

6.1 High-Priority Components

Component Migration Order:

  1. Entity Type Components (Foundational)

    • EntityTypeDropdown.jsx
    • EntitySearchModal.jsx
    • GenericDropdown.jsx
  2. Metric Components (High Usage)

    • QualityHydraulicsDropdowns.jsx
    • HydrQualChart.jsx
    • ScadaChart.jsx
  3. Visualization Components (Map Features)

    • UnifiedMapLegend.jsx
    • ElevationLegend.jsx
    • NetworkLegend.jsx
  4. Units Components (Already Done ✅)

    • UnitsContext.jsx
    • UnitsDisplay.jsx
  5. Low-Priority Components (As Needed)

    • Other components as discovered

6.2 Migration Checklist (Per Component)

- [ ] Import useConfiguration hook
- [ ] Add loading state handling
- [ ] Replace hardcoded constants with config data
- [ ] Add fallback for backward compatibility
- [ ] Test with API connection
- [ ] Test with cached data
- [ ] Test error states
- [ ] Update unit tests if applicable
- [ ] Document migration in code comments
- [ ] Update migration tracking

Summary

Infrastructure Complete:

  • Configuration context and provider
  • API integration
  • Caching strategy
  • Migration utilities
  • Backward compatibility layer
  • Test components

Backend Complete:

  • Database models
  • API endpoints
  • JSON seed files
  • Services architecture
  • Database seeding

🔄 Migration In Progress:

  • Units context migrated
  • Test component created
  • Ready for component-by-component migration
  • Backward compatible during transition

⏸️ Future Work:

  • Complete component migration
  • Remove legacy constants
  • Cleanup migration utilities
  • Optimize caching strategy

Next Steps:

  1. Test configuration API endpoint
  2. Verify ConfigurationTest component shows data
  3. Begin migrating high-priority components
  4. Track migration progress
  5. Remove old constants when complete

Related Documentation:

  • Configuration Models: See backend architecture docs
  • API Reference: Available in Swagger UI
  • Component Documentation: See architecture directory

Status: ✅ Infrastructure Complete - Ready for Component Migration Last Updated: October 2025