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
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:
EntityTypeConfig- Network entity definitions- Id, Name, Category, PropertyTypes, AvailableMetrics
- Example: Tank, Junction, Pipe, Pump, Valve
MetricConfig- Measurement definitions- Id, Name, Label, Category, Aggregators, Precision
- Example: Flow, Pressure, Demand, Quality
VisualizationConfig- Heatmap and rendering settings- MetricId, ColorStops, RendererType, MinValue, MaxValue
- Color gradients for map visualization
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-specificGET /api/Configuration/for-metric/{metricId}- Metric-specificGET /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 definitionsmetrics.json- Metric configurationsvisualization-config.json- Viz settingsmodel-units.json- Unit definitionsconstants.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:
NetworkConfigurationService- Database access for config modelsNetworkConfigurationSeedingService- JSON file loading and seeding
Integration:
- Services moved to
EN2PostgresContextproject - 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
/aboutpage - 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:
- ✅ Verify configuration loads
- ✅ Check dropdowns populate correctly
- ✅ Verify metrics display properly
- ✅ Test with cached data (offline mode)
- ✅ Verify error handling
- ✅ Check backward compatibility
6. Rollout Plan
6.1 High-Priority Components
Component Migration Order:
Entity Type Components (Foundational)
EntityTypeDropdown.jsxEntitySearchModal.jsxGenericDropdown.jsx
Metric Components (High Usage)
QualityHydraulicsDropdowns.jsxHydrQualChart.jsxScadaChart.jsx
Visualization Components (Map Features)
UnifiedMapLegend.jsxElevationLegend.jsxNetworkLegend.jsx
Units Components (Already Done ✅)
UnitsContext.jsxUnitsDisplay.jsx
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:
- Test configuration API endpoint
- Verify ConfigurationTest component shows data
- Begin migrating high-priority components
- Track migration progress
- 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