Project Brief
Purpose
Support workers often make use of typed or handwritten notes along with more generalised calendar and chat software to coordinate their work, and can lack software that is specifically designed to cater to their needs on the job.
CareSync is designed to streamline scheduling, record-keeping, and handover for shift workers in the personal care/support space, allowing care coordinators and workers to easily keep shift schedules and documentation in sync.
![The CareSync home page with a navigation menu,
heading, login form, and demo signup button.](img/thumbs/caresync-home.jpg)
Requirements
The brief for this project was to build a full-stack application using the “MERN” stack, consisting of a MongoDB document database, a backend API server running the Express web framework, and a frontend user interface (UI) built using the React JavaScript library, both running on the Node JavaScript runtime.
![A basic diagram of the CareSync application
showing the flow of communication between client devices, the frontend server, the
backend server, and external services](img/thumbs/caresync-app-diagram.jpg)
The key technical project requirements were a well-designed codebase with clear separation of concerns between program modules, a demonstration of “Don’t Repeat Yourself” (DRY) coding, well-judged use of third-party libraries, good code flow control, and the application of Object-Oriented Programming (OOP) principles using appropriate data structures.
The interface of the application needed to be responsive, providing a functional user experience at all screen sizes, from small mobile devices, to mid-sized tablets and large wide-screen desktop monitors.
Working in development teams of two, we were also required to demonstrate teamwork and the adoption of a recognised project management methodology to delegate tasks, design, build, and deliver the project, within a five week timeframe (two weeks for planning and three weeks for development).
We used Trello to list and track work items on a kanban board, categorising tasks using labels such as "frontend", "backend", "documenation", and "user story", and moving them through a workflow of "To Do", "In Progress", "Testing", and "Done" to ensure delivery of the project by the deadline.
![An example of a Trello work item used to track tasks while the project was in-progress](img/thumbs/trello-work-item.jpg)
Technologies
After a collaborative planning process, my role was to take on the front-end development of the app, building the user interface and integrating it with the API being developed by my project partner.
In addition to building the app front-end in React, I made use of:
- The React Router library for client-side routing
- The Material UI (MUI) component library to build the user interface (UI) components, such as menus, buttons, lists, forms, tabs, and dialogs
- Sass (SCSS syntax) to further customise the UI component styling
- The FullCalendar library to build the interactive calendar grid
- Git and GitHub to manage version control and merge in new features
- Railway for deployment of the database, backend server, and frontend server
![A cloud of logos representing the CareSync
technology stack featuring MongoDB, Express, React, Node, React Router, Material UI, Sass, Git, GitHub,
Railway, and FullCalendar.](img/thumbs/caresync-v2-stack-cloud.jpg)
Planning & Design
Data Flow
When designing the data flow of the app, we took the client being cared for as the central entity that everything else is connected to. Within the object database, the key collections are users, clients, and shifts, with most data stored in a shift object, and a client and carer ID associated with each shift.
The calendar of the selected client then acts as the primary view/workspace, from which a user can access functions to create and edit shifts, view historical shifts, manage a client's care team, and drill down into a shift's details and documentation, such as notes and incident reports.
![A data flow diagram (DFD) showing the data
flow between entities, processes, and data stores in the application](img/thumbs/data-flow-diagram.jpg)
The controller functions of the app's API endpoints have typically been written to receive an object ID corresponding to a user, client, or shift, in the URL parameters of the request.
This, along with authorisation and error-handling middleware, and JSON data in the request body, are used to appropriately check authentication and authorisation, create/read/update/delete the corresponding document in the database, and return a response.
Wireframing
The visual interface of the app revolves around the calendar as the central component, and I took a mobile-first approach to the design. The goal was to keep things clean, simple, and centrally accessible, allowing the user to complete a task without having to navigate through too many different views.
In planning, we identified two key groups of users—carers and coordinators—as well as a few different user personas that belonged to each, and some accompanying user stories. We then thought through design considerations that would apply to each group.
![Three wireframes showing the calendar
view of the application at mobile, tablet, and desktop sizes.](img/thumbs/wire-main-all-sizes.jpg)
Carers may want to be able to quickly enter notes on their phone during a shift, while coordinators may organise the shift calendar and review shift notes and incident reports on a larger screen such as a tablet or desktop monitor.
These considerations informed the presence, placement and grouping of elements at different screen sizes, ensuring a responsive interface that enabled the user to perform their required tasks on a variety of devices.
User Flows
As part of the wireframing process, I designed wireframes for the key user flows that the app would require, such as logging in, selecting a client, and adding and editing shifts and documentation.
![Mobile wireframes showing the user login flow](img/thumbs/wire-user-login-flow.jpg)
![Mobile wireframes showing the coordinator shift management flow](img/thumbs/wire-shift-flow-employer.jpg)
![Mobile wireframes showing the carer select and view shift details flow](img/thumbs/wire-shift-flow-carer-mobile.jpg)
![Mobile wireframes showing the incident report creation flow](img/thumbs/wire-incident-flow.jpg)
When a user logs in, they can:
- Create care clients
- Select a client and assign carers to their care team
- View a client's calendar of historical and upcoming shifts
- Add new shifts and edit pending shifts (if they are the client's coordinator)
- Enter shift notes, incident reports, and handover for the shift (if they are the assigned carer for the shift)
![The client calendar in a list view
on a mobile device, with upcoming and recent shift cards underneath.](img/thumbs/mobile-calendar.jpg)
![The Shift Details Drawer on a
mobile device showing a shift overview. It includes information about the assigned
carer and shift times, and shows snippets of the shift notes, incident reports, and
handover.](img/thumbs/mobile-shift-details.jpg)
Key components
Client Cards
The Client List view is displayed after login. Client cards are generated for each client that the logged-in user is the coordinator or carer for, and are populated with the client's name and details of their next next upcoming shift.
From this view, users can add clients, remove clients, and select a client to navigate to their shift calendar.
![The client selection view showing client cards in a list](img/thumbs/select-client.jpg)
The Calendar
The core functionality of the app centres on the Calendar view and calendar day grid component, which makes viewing, creating, and editing shifts and their associated documentation centrally accessible once the user selects a client.
From here, the user can see all shifts for a client during the selected month, including the date/time range and the assigned carer. The calendar can also be toggled between a grid and a list view, which can provide a better user experience at smaller screen sizes.
![The CareSync Calendar view after selecting a client.
It features a calendar day grid of the current month and a sidebar allowing the
user to switch clients, manage the client's care team, and view in-progress, recent,
and upcoming shifts.](img/thumbs/calendar.jpg)
Shift Cards
Shift cards display a summary of shift information that's designed to give the most helpful details about the shift at a glance. The next upcoming shift and recent previous shifts can be accessed via quick-access cards beside or below the calendar, depending on the screen size.
![A shift card showing an overview of shift details,
including the date, start and end times, and carer. Icon buttons linking to shift notes,
incident reports, and handover are displayed at the bottom of the card.](img/thumbs/shift-card.jpg)
Shift cards link to the shift details panel, accessible by selecting the entire card or one of its document icon buttons. This allows users to quickly navigate to the shift overview, or directly to its shift notes, incident reports, or handover.
The shift icon buttons are responsively displayed below or beside the rest of the card, depending on the size of the screen. If the type of note the icon represents is present, it is displayed in colour rather than greyed out.
![A demonstration of hovering over the shift
card icon buttons that link to shift notes and reports. Descriptive tooltips appear when
hovering over each icon button.](img/thumbs/shift-card-icons.gif)
Dynamic Modal & Drawer
The modal component is used to display informational dialog boxes and confirmations shown before taking destructive actions like deleting documents from the database.
I built the modal and confirmations to be as flexible as possible, taking data such as
their title, body text, alert messages, and action buttons, as well as
onClose
and callback
handlers and child components, as props.
This made the components highly versatile and re-usable in many different contexts, reducing the amount of boilerplate code used to display dialogs and confirmations throughout the app.
![The new shift dialog floating over the Calendar view,
with a form allowing the input of a shift start time, end time, carer, and coordinator notes](img/thumbs/dialog-new-shift.jpg)
The Shifts Details Drawer component is a modal drawer that opens on the right-hand side of the viewport (or full-screen, on mobile) when the user selects a shift. Here, users can view shift details; make edits to its start and end time, carer, and coordinator notes; and view and add shift documentation.
I used React Router's browserRouter
and Outlet
components, as well as conditional rendering, to display the sub-sections of the
Shift Details Drawer depending on the URL, the properties of the selected shift, and
the application state. The drawer displays an overview of the shift, and users can drill
down into views of specific shift details and notes by selecting cards in the overview.
![The Shift Details Drawer open over the Calendar view](img/thumbs/shift-details-drawer.jpg)
Learning & Challenges
State and Session Management
The app makes use of React's context API to store key state data for the logged-in user, making it globally accessible and avoiding excessive prop drilling where data needs to be passed to deeply-nested components.
In the first iteration of the app, we used localStorage
to persist the
application state between page loads, but this was a rather insecure solution,
especially with the state containing potentially sensitive information such as internal
resource IDs, user data, and client shift data.
![The code for application's the read session
function. It asynchronously fetches the session data, parses JSON response into an object,
and returns it if successfull. If the reponse code is not 200, it returns null.](img/thumbs/code-readsession.jpg)
I later reworked the app's session and state management to be handled server-side using
the express-session
and connect-mongodb-session
libraries on
the backend, and the Web Crypto API on the frontend, to encrypt session data before
sending it to the backend server, associating it with the user, and inserting
it into the database.
When the user performs an action that modifies the global state store, the store object is encrypted and uploaded to the server. If the user reloads the page, or closes and reopens the window, and their authentication and session cookies are still present in the browser, the previously stored session is fetched and loaded, allowing the previous application state to be restored.
Time-Based Conditional Logic
Creating, viewing, and editing shifts depends on the current user’s relationship to the client (coordinator/carer), as well as the current time relative to the shift's date and time. This required the etablishment of some business rules, based on the processes and conventions used to coordinate and document shifts, and the corresponding logic.
To add a shift, the user must be a coordinator for the client. To add shift notes and incident reports, the user must be the assigned carer for the shift and the shift must be “in progress” (that is, the current time is after the shift start time and before the shift end time), OR within an eight-hour edit window after the shift. If there is a subsequent shift for the same client, notes must be added within the first two hours of that shift.
![The Shift Details Drawer Overview panel for a
pending shift. It has a blue notice at the top indicating that the shift is pending, and the body
text on the cards is greyed out to indicate that notes cannot be added yet.](img/thumbs/shift-overview-in-progress.jpg)
These kinds of considerations demanded careful thought when building the UI, displaying placeholder messages and help text, and determining the conditions under which a given user could view and/or edit certain pieces of information.
For example, the Shift Notes area in the Shift Details component will alternatively display existing shift notes, “You’ll be able to add shift notes here once the shift starts”, or “Enter your shift notes” (and a submission form), depending on whether the shift is in the past, in the future, or currently active.
![The Shift Details Drawer Overview panel
for an in-progress shift. It has a green notice at the top indicating the shift is in progress,
and the body text on the cards is coloured to indicate that notes can be added.](img/thumbs/shift-overview-pending.jpg)
API Resource Representation
Another challenge I encountered was inconsistency with API response values, with variable fields of the same resource being returned by different routes, and actual data requirements not always matching the initial API design.
In some cases, the data initially returned ended up being insufficient for the action being taken, or the result that needed to be displayed on the front-end, requiring additional API calls for more data from the resource.
For example, fetching the selected client returned the ObjectId
, but not the
name of, the coordinator, so in contexts where the name of the coordinator needed to be
displayed immediately upon fetching the client, this required another database query.
![A user object as represented by a MongoDB document.
It has the fields _id, firstName, lastName, email, password, isConfirmed, __v, createdAt,
and updatedAt.](img/thumbs/mongodb-user.jpg)
In some instances, these issues could be resolved by modifying the controller function's return values as needed, or simply double-checking the code to see which parts of a resource would, or would not, be returned by a given route.
As development progressed, it became clear that having good API documentation, establishing clear response formatting conventions during the design and early build phase, and returning entire (rather than partial) resources where the data was relatively small in size would have made for cleaner, less brittle code and a better developer experience when working with the API.
Deployment
A key project requirement was the deployment of the app to a live hosting service, and we opted for Railway for its simplicity and ease of use. This gave me exposure to the basics of a setting up an automated deployment pipeline.
The app consists of three separate services: a MongoDB database, a backend server running the Express API, and a frontend server for the React UI.
Deployments on GitHub trigger automatic rebuilds and redeployments of the relevant services, and a custom start command in the frontend deployment settings ensures that the app starts in production mode, bypassing startup of the React development server.
![The Railway dashboard for the app
showing the MongoDB, frontend, and backend services and recent deployment activity.](img/thumbs/railway-dashboard.jpg)
Environment variables supplied to each service securely provide information such as the URLs and credentials needed to communicate with other services like cloud file storage (Cloudinary) and email (Mailtrap), and the secrets, passphrases, and salts used to encrypt and decrypt session data and sign JSON Web Tokens (JWT).
Conclusion
As the capstone project for my Diploma of IT in Web Development, tackling this project gave insight into, and hands-on experience with, the many complexities of collaboratively building and deploying a full-stack application.
I gained experience in determining end-user requirements; designing an application architecture and data flow; web API and database design; methods of updating, storing, and retrieving application state; authenticating users and controlling access; and wireframing and building a responsive, user-friendly interface with the help of component libraries.