Building better student tracking in rural areas

Josh Glantz-Hucks
5 min readJan 7, 2021

Village Book Builder aims to decrease poverty by improving education in rural villages around the world. They partner with villages to build a library and pair local students with mentors. Currently, records of students, staff, and more are kept across a range of spreadsheets that must be manually referenced. As VBB grows, more and more individuals need access to those records. The userbase also requires a very straightforward, uncomplicated UX. My team created a flexible portal that not only allows access to the new database being built but will also grow to accommodate various user roles accessing different user interfaces. Our biggest concerns going into the project were that their client had several key features that were poorly explained, that there was no functional backend for us to work with and that we were expected to integrate an existing login component without having access to said login.

Features Shipped

  • Mobile-first design, extremely user-friendly UX
  • User roles — each user has a single role that determines both the information and the UX/UI they can access at login. Login, Authentification, Admin/Headmaster roles are currently implemented
  • Headmasters can do easily view/add/edit/delete Student profiles, and do limited operations on Village, Library, School, and Headmaster profiles.
  • Admin can create, view, and edit Library profiles

Personal Contributions

Since we were a remote team, I created a master notion doc of all the planning/documentation & coordinated our notes. During the planning phase, I advocated for not just picking the “coolest” feature implementation, but what would be best for a “KISS mobile UX” as our stakeholder put it. Once our team began coding, I created the Admin UI, the login/routing workflow, and provided clarity on how to best structure the app for future releases.

Technical Challenges

The biggest technical challenge I came across was how to incorporate private routing (where certain URLs are only accessible if a user has proper permission) with user-roles. Our client asked for an extremely streamlined process where any user could log in to the app, and only see the UI that matches their role. Our initial response was to make role-specific routing, where every URL included the role of the user inline /admin/libraries for example. This allowed us to at least create and test our assorted components but introduced a level of complexity to URLs that could confuse users, or create a problem if a user typed the wrong role. (Admittedly, we were designing a SPA where a user should never have needed to type a URL, but it was a good edge case that the stakeholder was concerned about) We agreed to later remove all references to roles by taking care of it entirely within our routing. Originally, we planned on simply using standard PrivateRoutes to protect our routes and then protecting each URL-role with a secondary set of protected routes.

const PrivateRoute = ({ component: Component, ...rest }) => (     
<Route
{...rest}
render={props =>
localStorage.getItem("token") ? (
<Component {...props} />
) : (
<Redirect to="/login" />
)
}
/>
);

However, if we wanted a single /dashboard URL that dynamically displayed different navigations and UI's after being logged in, we had to go consider a few more things. The ideal option was to keep the standard private routing and dynamically display only the components relevant to that role. Due to initial miscommunications with the client and later time/manpower constraints, we had entirely separate dashboards that were not built with reusable components, and not enough time to refactor them appropriately. To balance both our current predicament with the app's future needs, I decided to first protect our routing with a login check and then display a dedicated dashboard according to the role.

Switch>         
<Route path="/login">
<Login />
</Route>
<Route path="/">
{/* Look for token in case a user refreshes the page & clears redux store, then repopulate the redux store with userId, role & loggedIn status with checkToken()*/}
{localStorage.getItem('token') ? (
<>
{checkToken()}
{/* Reusable dashboard component will go here w/ role*/}
{role === 'headmaster' && <HeadmasterDashboard />}
{role === 'admin' && <AdminDashboard />}
</>
) : (
<Redirect to="/login" />
)}
</Route>
</Switch>

Note in case it seems odd: to have multiple conditionals running inside the middle of the ternary statement a jsx fragment is required. Similar to how a return statement needs to be on a single line, with the workaround that a () beginning on that line can contain as many lines as needed without breaking functionality.

Another interesting technical challenge was the lack of a working backend. After a week of conflicting messages, we learned that the backend would be built after our frontend was finished. We needed not only to create functional CRUD operations but to create a working routing system dependent on user roles after login. One of our teammates managed to create a fully functional mini-backend using JSON-server that met all of our needs while providing plenty of seed data and data relationships to test our features. Even though I wasn’t directly involved in that portion, it’s still one of the most impressive solutions to a problem from my perspective.

Team Feedback:

My favorite bit of feedback was that we were not responsible for the entire app, but only our roadmap. This was something we had to consistently remind each other about as we kept experiencing scope creep. For myself, it had the biggest impact when I was in the zone writing user stories for the entire app and breaking them down into lots of tasks but didn’t’ realize that most of those user stories were outside of our sprint scope. Once it was brought to my attention, I had to stop, take a break, and return with the new clarified mindset of focusing only on what was in our sprint scope. (We did decide to keep the user stories as a bonus for the next teams working on the project, but agreed to mark them as unimportant in the meantime) After our planning phase, I would often be the one reminding others that they had begun working on (out of scope) features from the next phase before finishing our current phase. Our stakeholder’s passion for a better app portal was just so fantastically infectious, that we had to constantly be on our toes to not start on future features as we kept getting excited.

Going Forward:

Some components should be refactored into reusable components, specifically the sidebar navigation and forms. The dashboard pages that users land at after logging in need to be filled with calendar and communication components yet to be built. Our client envisions 5 more roles that will need to be implemented in the future. A full backend needs to be developed to match our requirements. I foresee future technical challenges centered around keeping the file structure sufficiently organized with multiple roles, integrating the client’s existing login with our frontend, and deciding on the best UX for headmasters to match mentees to mentors.

Personal Takeaways

Working with a stakeholder has been a fantastic opportunity. As previously mentioned, one of the most critical lessons I learned is that when a team agrees to deliver a product, they should firmly stick to that product and deliver it well. If they finish early, then by all means add features as a bonus, but only after prioritizing the mutually agreed-upon product. Beware scope creep. I’ll be a much better employee in the future understanding how to prioritize what is requested of us.

--

--