Introduction, Purpose and Design
Nigeria's electricity system is plagued with acute reliability problems. Incessant power outages result in businesses and households self-generating 59% of their electricity from diesel (or gasoline) powered generators 1.
Wyre is a company with a mission to alleviate the issues that come with this unpredictable power supply. Essentially, the company plants devices in homes and offices to track, predict and report energy data. I was approached to build the frontend for the company's first web app based on this mockup 2. As only desktop screens were provided in the mockup I was given, I had to devise mobile screens while building the frontend.

The mobile screens are provided below. Drag or swipe to navigate the images.



2. The design (certain components and the color scheme) has been modified since I completed the project. Also, only a few screens are shown in the linked document. ↩
Feature Spotlight: Selected Data Manipulation
Peculiarities in the Data Sent from The Server
The application's sidebar contains its most crucial elements—a list of checkboxes that manipulate what data is displayed. These checkboxes represent the branches of an organisation and the devices installed within them.

I initially assumed the implementation of this feature to be an uncomplicated task. However, I changed this presumption after I saw the data with which I was going to build it. A simplified cross section of this data is shown in the following snippet (comments have been added for more clarity).
"data": {
"name": "Sample organisation",
"branches": [
{
"name": "Branch 1",
"devices": [
{
"name": "Utility",
"dashboardData": {
"total_kwh": {
"unit": "kWh",
"value": 1000
},
"min_demand": {
"unit": "kW",
"value": 200
}
// Other data for dashboard
},
"scoreCardData": {
"is_generator": false,
"baseline_energy": {
"unit": "kWh",
"forecast": 1000,
"used": 500
}
// Other data for score card
}
// Data for other pages (billing, cost tracker etc.)
}
// Data for other devices
]
}
// Data for other Branches
]
}
As show above, the data provided is only for the devices. Therefore, to obtain data for a whole branch, I had to sum up the data for each device within it. In a similar fashion, data for the whole organization was obtained by summing up the data for all of its branches. Furthermore, each time the checkbox associated with a branch or device is checked or unchecked, the UI had to reflect the change.
Sample Data Manipulation
The snippet below is provided to illustrate the complexity of these data summations needed for the UI. It contains the provided daily kwH values needed to plot the dashboard's stacked bar chart (shown in the introduction).
"daily_kwh": {
"dates": ["Jan 1","Jan 2","Jan 3","Jan 4","Jan 5","Jan 6","Jan 7",
"Jan 8","Jan 9","Jan 10","Jan 11","Jan 12","Jan 13","Jan 14",
"Jan 15","Jan 16","Jan 17","Jan 18","Jan 19","Jan 10","Jan 21",
"Jan 22","Jan 23","Jan 24","Jan 25","Jan 26","Jan 27","Jan 28",
"Jan 29","Jan 30","Jan 31"
],
"Utility": [
203, 852, 863, 355, 290, 664, 315, 866, 828, 102, 728, 267, 138,
848, 330, 78, 207, 164, 426, 702, 262, 822, 63, 889, 278, 547, 844,
794, 227, 857, 729
],
"Gen1": [
203, 852, 863, 355, 290, 664, 315, 866, 828, 102, 728, 267, 138,
848, 330, 78, 207, 164, 426, 702, 262, 822, 63, 889, 278, 547, 844,
794, 227, 857, 729
],
"Gen2": [
203, 852, 863, 355, 290, 664, 315, 866, 828, 102, 728, 267, 138,
848, 330, 78, 207, 164, 426, 702, 262, 822, 63, 889, 278, 547, 844,
794, 227, 857, 729
]
},
The shape of the daily kWh data is completely different from those of the other dashboard data. Therefore, custom methods were needed to manipulate it. One of these methods, used to calculate the daily kWh values for a branch, is provided below.
const getBranchDailyKwh = data => {
let branchDailyKwh = data.daily_kwh;
const { dates, ...rest } = branchDailyKwh;
const allDevicesDailyKwh = Object.values(rest);
// Add total
const totalBranchDailyKwh = sumArrayOfArrays(allDevicesDailyKwh);
branchDailyKwh[data.name] = totalBranchDailyKwh;
return branchDailyKwh;
};
The organization's daily kWh values were obtained by looping over the calculated branch values and performing a very similar calculation.
The 'Rendered Data' State
The data feeding the application at any specific time is piece of React state
held in context called renderedData.
Rendered data could either be data for a branch, the whole organization or a
selection based on the conditions shown below:

The video below shows how these conditions operate in the working application.
Lessons Learned and Future Improvements
One decision I made when building out this application was to create my own methods for all data manipulation in lieu of libraries like Lodash or Ramda. This was a premature optimization that made for a much more complex codebase. I now fully understand the benefits of following the conventions laid out by such libraries.
Also, the complex data manipulation needed for the application to work could have been carried out on the server. This will have made for a leaner client with easier-to-manage UI states.