Image courtesy of Am I responsive?
PLEASE NOTE: If you would like to create your own account and sign, please ensure that you have 'Prevent Cross Site Tracking' turned off on your browser to allow it to work.
Syncora is a web application designed to help improve your productivity and organisation in day to day life. It allows you to view all your events, tasks and any notes you write down all in one place, making everything easier to find. Why not sign up today and start being the organised person you've always wanted to be?
This repository is for the front-end of the application, made using the React framework. The back-end of the project utilises the Django REST Framework. The repository for the back end can be found here.
Syncora is a web application that is the virtual version of a personal planner or diary. The primary goals of the app are:
User stories: #38, #50, #22, #23
User stories: #24, #28, #64, #66
User stories: #22, #23, #24, #26, #27, #28
Planning of the features for Syncora was done with user stories. While brainstorming user stories, epics were created which were then further identified into themes.
Three main themes were identified in the planning of this web app:
User Account - Anything to do with a user registering for the app, signing in and out and account deletion.
User Interface - This involves the design and layout of the web app, helping to provide an intuitive user experience while also providing an impactful design.
App Features - This involves the main features of the app that will define Syncora as a productivity app.
The above themes were then split down further into the following epics:
User Account
#4 Account Registration
#5 User Sign In/Out
#6 Account Deletion
#9 User Profile
User Interface
#7 Dashboard
#8 Navigation
App Features
#10 Tasks
#11 Events
#12 Notes
Each epic above is directly linked to the issue created on Github as part of the Agile methodology used to create the app.
Each user story was then created based of the above epics. Each user story is linked to an epic and labelled using the MoSCoW approach: must have, should have, could have and won't have. All user stories can be viewed on the Syncora Project Board.
User stories were created using Github's issues feature. Then using Github's projects feature, a board was created to keep track of all user stories, epics and themes. The board was split into five main columns: one column for themes, one for epics, then three to keep track of each user story. These three were the default To Do, In Progress and Completed columns provided by Github when creating a project. Each user story was then labelled using the MoSCoW approach. Any feature that falls under CRUD functionality was labelled as a must have feature. Story point labels were then created using a linear scale of: 1, 2, 4, 6 and 8. I decided not to use a label for 16 story points because that would be considered an epic, so this felt like a redundant label.
For each iteration, a new milestone was created. Each milestone was then given a due by date to help keep on track throughout the creation of this project. An initial milestone was created to act like the Product Back Log, where all user stories were first moved to.
Wireframes were created to help with the design and layout of Syncora, using the 'must-have' user stories to ensure important features were included. Wireframes for both desktop and mobile were created. Below are the wireframes for the mobile layout.
These can be viewed in PDF format here.
From the start I already had a layout in mind for the dashboard for desktop, which the rest of the wireframes have been based around.
All remaining desktop wireframes can be found here. The to-do list picture used as part of the hero image on the register/sign in pages is from geralt on pixabay.
Models for the database were planned out before starting creation of the project. These models can be found in detail on the back-end repository here.
I envinsioned a bright pink as the main colour for Syncora, using this base to then pick the rest of my colour palette. First, I searched different palettes based on pink to choose the shade of pink I wanted. Using this colour palette on colorhunt.co, I initially chose the colour #F05A7E
. Entering this colour onto coolors.co allowed me to then choose a slightly different, more muted shade of pink - #FB6083
. I then checked the contrast of this colour against a black to ensure it met AAA accessibility guidelines. Using this colour generator on Learn UI Design, I chose the shade #000010
, which will be used as text on the pink shade. Locking these two colours in on my coolors.co colour palette, I then generated another colour to use as a secondary colour. The colour I chose was this wheat colour #F5DFBB
. Finally, I picked a shade of white/cream as a fourth colour, again checking it's contrast on Learn UI Design against the above black shade. The cream/white shade I settled on to meet AAA guidelines was #FFF5F5
. Below is the final colour palette for Syncora.
Below is the contrast grid created on Contrast Grid by Eightshapes to check for passing contrast with the colour palette.
Google fonts was used to provide fonts for this project. For the Syncora logo and for section headings, I decided to choose the font Comfortaa. It is a simple but rounded font which gives simplicity and modernity at the same time, perfect for the branding of Syncora. Comfortaa was found on fontpair.co. For all other text, I chose the font Nunito. Using fontjoy.com, I generated fonts that would match Comfortaa but with a bit more contrast. Nunito was suggested, so I settled on this for all other text on the app.
Rather than a traditional top navigation bar, I wanted to create a side bar menu instead for Syncora. To create this, I installed the Contrast Design Bootstrap React (CDBReact) package from devwares.com, following the documentation to create and customise my sidebar menu. Although in the wireframes I had designed for the sidebar component to not be visible at all when collapsed, the CDBReact sidebar did not allow for this. I found this however to make the design more interesting and colourful. To compromise and ensure that it didn't take a lot of space when collapsed on mobile screens, I changed the minimum size from 80px to 75px. The sidebar component was then imported into the root route so that it would appear on all pages.
When a user visits and is logged out, only the 'Sign In' and 'Register' links are visible in the sidebar menu.
Once a user is logged in, the links change to access different features of the app including events, tasks, notes and the user's profile. The 'Dashboard' link acts as the homepage for the user, where they are able to see a brief overview of everything on one page. The user can also click to sign out from the side bar menu.
Alongside the sidebar menu, I created a simple and non-obstructive top navigation bar, building of the React Bootstrap Navbar component. In this top nav bar, all that was included were three quick links for a user to create a new event, task or note, rather than clicking on to their respective pages first and then click to create a new entry. Also included in the top navigation bar is a simple welcome message for the user, used as a simple indication that they are logged in. This top navigation bar only appears on screens larger than 992px so as to not take up screen space on smaller screens. These three quick links are available in the sidebar menu instead when a user is on a smaller device.
Using the Spinner component from React Boostrap, I created a reusable loading spinner component. This component is used to visually indicate to users when something is loading, so was imported into other pages as needed.
In case of any errors, I have created a page which will display any errors and allow users to navigate back to the website.
During development, I realised I would be using a modal throughout the website to ask the user to confirm if they wanted to delete their account, task, event or note. So I decided to create a reusable modal component which could then be used on different pages. I created props which when passed down, could be used to change the modal title, the content, the handleDelete
function, and it's show and closed states. This helped to save time manually re-writing a new modal on each page where needed.
I downloaded react-toastify to quickly create a toast notification to use rather than the standard bootstrap alerts, which take more space on the screen. The react-toastify library also allows the notifications to appear on screens which are navigated to after an action has occurred. For example, when a user signs up and are then redirected to the sign in page, a toast notification will appear telling them that registration was successful. This library made it easier to alert user's of events throughout the app seamlessly.
When the user first visits Syncora, they are greeted with a landing page which gives a brief description of what the purpose of the website is. From here, they are able to sign up with a call to action button, or sign in. The sign in and sign up links are also available in the sidebar.
User story: #13
The sign up form allows users to access the full functionality of the website. The user is asked to enter a username, an e-mail, and a password which is confirmed again. If any fields are blank, or if a username has already been taken, this is alerted to the user. E-mail is an optional field as set by the back-end library dj-rest-auth, so an alert does not show up here. Underneath the form is the option to sign in if a user already has an account. This redirects the user to the sign in form instead.
The sign in form is laid out similarly to the registration form, but instead only asks for a username and password to be entered. There is also a link below for users who don't have an account yet to allow them to register.
For both the sign up and sign in forms, a hero image appears to the right when viewed on a medium sized screen or larger.
User Story: #17
When a user clicks the sign out link in the sidebar, they will be redirected to a page asking them to confirm if they would like to sign out. They also have the option to go back to the previous page if they change their mind. Upon signing out, the user then gets redirected back to the sign in page.
Once the user logs in, they are taken to their dashboard page. On the dashboard is a brief overview of all their to do tasks, notes and any upcoming events.
User Stories: #33, #34, #35, #36, #37
A user is able to view their own profile. Their profile picture, name, username and e-mail will be displayed. On initial registration, a defaul avatar will be provided. This can be changed on the profile page by uploading a new one. Images over 2MB won't be allowed. The user is also able to delete their own account. A modal will pop up asking the user to confirm their action.
For future features where users can be linked on tasks and events together, I have included functionality which prevents users from changing other profile's pictures or deleting their accounts. When viewing another user's profile page, the upload and delete account buttons will not be visible.
When the user clicks the 'Events' icon in the sidebar, they are redirected to the Events page where they are shown all their events in a list. The event title is displayed in a preview, and more basic information is displayed when opening the drop down. Each event also gives the option to view the event in more detail or delete the event. Users are also able to filter their events by category, or do a simple text search for an event. Past events are muted to allow more visual differentiation between each event. If an event is happening on the same day as the user has the app open, this will also be indicated. Underneath the list is the option to create a new event.
When viewing the event detail, the user can see the date, start time, end time, location, category and any notes that they have added. Underneath is the option to either edit the event or delete the event. Editing the event will bring up the event form, allowing them to easily update any information.
User Stories: #50,#53, #55, #57
When either creating or editing an event, a form will be displayed to the user. They will be able to enter or update the name, date, start time, end time, location, category and notes.
User stories: #30, #40, #48, #49
When the user clicks the tasks icon in the sidebar, they are redirected to the tasks list page. A list of tasks are displayed which are split into two panes: To Do and Completed. Each task shows the title, priority level and the due date. The option to complete the task is also present, saving the need for the user to enter the edit form to complete them. Once completed, the task will move into the 'Completed' tab. If checked by accident, then the user is able to uncheck the task which will automatically move it back to the 'To Do' tab. Each task also allows the user to view the task in detail. Tasks can also be categorised using the filter button by priority or category, and will display all tasks whether they have been completed or not. Tasks can also be searched for by a simple text search. Underneath the list is the option to create a new task.
When 'View task' is clicked, the user is able to view the task in detail. This includes the due date, priority, category, any description included and whether the task has been completed or not. From here, the user is able to either edit or delete the task.
User Stories: #38, #41, #43, #44, #46
When either creating or editing a task, a form will be displayed to the user. They will be able to enter or update the title, due date, priority, category and description. Only when editing will the user also be able to click the 'Completed?' checkbox. This checkbox is not visible in the creation form.
When navigating to the notes page, all of the user's notes are displayed with the most recently updated at the top. The title of the note is displayed (if there is one) and a brief overview of the content can be seen. Each note has a 'See more' button to view the note in detail. Undearneath the list is a link to create a new note.
When clicking the 'See more' button on the notes list, the user is then able to see the note in detail. The user can see the title is available, the full content and the date it was updated. Underneath the note are some 'Edit' and 'Delete' buttons for the note and then the option to return to the previous screen.
When creating a new note, a simple form is displayed to the user with Title and Content inputs. The title is an optional feature for the note. The user can also navigate back to the previous page. If editing, the note detail is inserted into the form. The user is able to save the changes or cancel the edit.
The first feature I would like to implement in the future is allowing the customisation of the dashboard layout. Currently the layout is set so that tasks are followed by notes with events at the bottom. Implementing a drag and drop feature for each component would be a feature I could include, for example using the Swapy framework.
Another feature I would like to include is allowing users to create their own categories for tasks and events. Currently it could feel restrictive or that the categories don't correctly represent the task/event at hand. One way this could be implemented is creating a separate API end point for the categories for tasks and events, and allowing it to be a read and write endpoint.
Implementing reminders for upcoming events would further improve Syncora. This could be done currently using the react-toastify library that is downloaded. Creating e-mail reminders could further improve this functionality, allowing the user to not have to be logged in/ have the web page open constantly.
A final feature I would possibly like to implement is allowing users of the app to follow each other to allow a more collaborative environment. For example, is work colleagues could follow each other and then create events together and be tagged in those events.
When testing for form errors during creation of the sign up form, I came across an error stating the one of the field names was not defined. In my sign up form, I had included a name field so that when a user registered, it would become a part of their profile automatically, set up by the signals I created in the backend. I tried changing the name and value of the field to fname
, first_name
and just name
, however none of these worked. Looking at the dj-rest-auth documentation, only four fields were accepted in the POST data: username, password1, password2 and email. So to overcome this bug, I removed the name field from the registration form, no longer producing the error.
Following on from the previous error, I then encountered another bug when testing the form errors for the registration form. Below is a screenshot of the console error I recieved:
I double checked that all my settings were correct in both my front-end (under api/axiosDefaults.js) and the back-end (_under syncoraapi/settings.py). I had also ensured I followed both the instructions from Code Institute's React walkthrough project and the axios documentation to ensure I had set up my API correctly. After contacting some tutors for help, it appeared that it was due to package discrepencies in the backend. Changing the version of Django used to an older but still supported version fixed the error, allowing requests to be made to the API. More information about this error can be found in the back end README.
When testing registration of users to Syncora, I noticed that the page wasn't refreshing or being redirected to the index like I had coded. Looking at the console showed a 500 error after registration. However, when I clicked the sign up button again with the same information, a form error would display stating that a user with that username already exists. I double checked that registration was successful by logging into the back end and finding their profile in the API. So it was unclear why a 500 error was being shown. After searching, I found a similar issue on GitHub under the dj-rest-auth repository. It seemed that the issue was with the e-mail set up in the back end. After adding the new email settings, I tested the registration feature again and was successfully being redirected to the index page as planned. More information about this bug can be found in the back end documentation.
I was able to get signing in to the application up and running quickly and smoothly, however I then came across a bug when trying to implement custom context hooks. I wanted to be able to tell when a user was logged in so that I could conditionally render links in the sidebar based on that property. Following along with Code Institute's Moments walkthrough to create a Current User hook, I tried to import this hook into my sign up form. However, when testing the sign in functionality again, I came across this error:
TypeError: setCurrentUser is not a function
Even after checking all my imports, my setCurrentUser
function was not being recognised. Logging it to the console gave the result undefined
. Initially, when creating the CustomUserProvider
, I had it wrapped around the routes in App.jsx. When moving the provider to main.jsx and having it wrap around the <RouterProvider>
element, console logging the function no longer showed undefined. Testing the log in function again, it worked successfully and the links in the sidebar menu rendered as expected.
While testing out the registration and sign in feature on the deployed site to fix any bugs, a discrepancy occurred between a user's primary key and the corresponding profile primary key. A user was able to sign in properly, but clicking on the profile link would display a profile with an id that had yet to be created. Checking the network requests in the front end showed that the profile pk did not match the user pk. For example, a user which had the pk of 24 had a profile pk of 47. This discrepancy was confirmed by print statements in the back end of this project. I was not fully able to understand where the issue had come from, but potentially as there was an issue with Safari not logging users in after signing up, perhaps more user accounts were being created without a profile. However I do not think this was very likely. To overcome this bug, I created a new database and took the existing database information, removed the users from where the discrepancy began and reloaded that data into the new database. This fixed the issue and there were no longer discrepancies between the user and profile pk's.
An issue arose where the sidebar component would not toggle open and closed on smaller screensizes. Initially I thought it was a potential conflict with the screen size prop I had in my sidebar component which would conditionally render some sidebar links. However after adjusting the code this didn't work. Some console logs during development showed that the click handler was working, but not when actually clicking on the menu icon.
I created some utility functions to refresh the access token timestamp, thinking that there was a conflict between the currentUser not being gotten properly which was affecting the toggle functionality. This also did not fix the issue. After some tutor help, it turned out that the Welcome user message I was displaying on smaller screensizes had a higher z-index than the sidebar, as a Navbar component was used to create this message. Creating a custom css class to lower the z-index of this navbar to below the z-index of the sidebar solved the bug.
npm create vite@latest -- --template react
.Manual testing took place throughout the development of the app, for both the front-end and back-end. This section will focus on the front end React testing.
Testing tables were created using Google Sheets. Each row containted the feature being tested, the expected outcome, whether the test passed or failed and an extra notes column for further explanation. The tables have been converted to markdown format and can be found here.
The HTML for Syncora was validated using the W3 online validator. Validation passed, results can be seen here.
Each module.css file was validated using the W3C CSS Validator by direct input. Below are the validation results:
CSS File | Notes |
---|---|
App.module.css | Passes validation. |
Accordion.module.css | Passes validation. |
CreateLink.module.css | Passes validation. |
Dashboard.module.css | Passes validation. |
DetailPageButtons.module.css | Passes validation. |
Notes.module.css | Passes validation. |
ProfilePage.module.css | Passes validation. |
SideBar.module.css | An error was thrown for line 8 where overflow is set to scroll initial. Validator says that initial is not a value of overflow, but looking up this initial property found that it can be used for any css property. The problem seemed to stem from having both scroll and initial set as seen in the dev tools. I changed the value to be initial only to allow it to inherit. Validation then passed. |
SignInForm.module.css | Passes validation. |
Spinner.module.css | Passes validation. |
TaskList.module.css | Passes validation. |
TopNavbar.module.css | Passes validation. |
For validation for Javascript and JSX files, I downloaded the ESLint extension as a development dependency. Files were validated using the command npx eslint .
. The only errors that are displayed are the React missing props validation errors. Reading about it further, the new React documentation lists it as a Legacy API, and also recommend the use of TypeScript instead for checking prop types. For that reason, I have chosen to ignore those errors. There are no other errors and all validation passes otherwise. Below is the table for the validation for each .js/.jsx file:
Folder | Component | Notes |
---|---|---|
api | axiosDefaults.js | Passed validation |
components | DeleteModal.jsx | Warnings for missing props validation. Looking at the current React docs, this is now a legacy API and is being removed from React 19. For this reason, I have ignored these errors. Passes validation otherwise. |
ErrorPage.jsx | Unused 'React' variable removed. Passes validation. | |
LoadingSpinner.jsx | Passes validation | |
SideBar.jsx | Ignored missing props validation warning. Passes validation otherwise. | |
TopNavbar.jsx | Passes validation | |
contexts | CurrentUserContext.jsx | Ignored props validation error. Warning for fast refresh only working with export components. Files have been separated. |
functions | toasts.js | Passes validation |
dateFormat.js | Passes validation | |
mocks | handlers.js | Unrecognised HttpResponse import. Import added from msw, passes validation. |
pages/auth | SignUpForm.jsx | Unused React var and an unescaped entity. These have been fixed, validation passes. |
SignInForm.jsx | Unused React var, no unescaped entities error fixed. Passes validation. | |
SignOutPage.jsx | Removed unused variables. Passes validation | |
pages/events | EventDetail.jsx | Removed unused (e) in handleDelete. Removed unused React var. Changed fontawesome class to className. Solved missing handleMount dependency in useEffect. Passes validation. |
EventEdit.jsx | Ignored props validation error. Removed unused React var, changed class to className. Passes validation otherwise. | |
EventForm.jsx | Removed unused destructured props. Removed unused React var, changed class to className. Passes validation. | |
EventsList.jsx | Removed unused React var. Changed class to classname. Ignored missing props validation. Removed unused searchList var from state destructuring. Passes validation otherwise. | |
pages/notes | NoteDetail.jsx | Removed unused React var. Passes validation. |
NoteForm.jsx | Removed unused React var, changed icons to use className, ignored props validation errors. Passes validation. | |
NotesList.jsx | Replaced class with className, removed unused destructured noteId and setNoteId states. Ignored missing props validation error. Passes validation. | |
pages/others | Dashboard.jsx | Removed unused React var. Passes validation. |
LandingPage.jsx | Removed unused React var. Escaped comma. Replaced class with className for fa icon. Passes validation. | |
pages/profile | ProfilePage.jsx | Removed unused variables. Passes validation |
pages/tasks | TaskDetail.jsx | Removed unused variables from destructuring data. Changed fa icon class to className. Passes validation. |
TaskForm.jsx | Ignored missing props validation warning. Removed unused React var. Changed fa icons class to className. Passes validation otherwise. | |
TasksList.jsx | Remove unused react var. Change fa icons class to className. Removed searchList var as unused. Passes validation otherwise. | |
routes | roots.jsx | Removed unused variables. Renamed component to use PascalCase. Validation passes. |
utils | utils.js | Passes validation. |
src | App.jsx | Passes validation. |
main.jsx | Moved WrappedRoot.jsx to separate file for Fast Refresh warning. Removed unused CurrentUserProvider var. Passes validation. | |
setUpTests.js | Passes validation. |
Due to CORS errors when using wave.webaim.org, causing the site not to load, I was unable to use it for accessibility testing. As an alternative, I used the accessibility testing provided by Google Chrome's Lighthouse testing, going through each page. An average accessibility score of 85 was given. The issues listed that were not fixable were mainly from the Sidebar component, built using CDBReact. These were the following warnings given:
These all stemmed from the sidebar. I added aria labels to the navlinks for each icon in the sidebar to further improve accessibility for accessible names. However, when the sidebar is expanded, more labelling is available for screen readers, so I have chosen to dismiss this first warning.
The lists error is again due to the sidebar, where it is rendered as a nav which contains an unordered list. Inside each unordered list is a link which then contains the list item. As this is due to the combined use of React Router's NavLink component with the CDBReact Sidebar Menu Item, I was unable to fix this warning also.
Performance for mobile and desktop loading was done using Google Chrome's Lighthouse testing in the development tools.
The largest issue affecting the performance when first loading Syncora is due to the hero image. Although the image was converted from .jpeg to .webp to help improve loading time, it was still an issue. Another factor affecting the loading time was the FontAwesome CDN. However this couldn't be removed as FontAwesome icons are used throughout the app. Therefore, no further improvements could be made to the mobile performance, especially due to time constraints.
Better performance was shown for the desktop Lighthouse test, with a higher performance compared to mobile.
As this React app was built using Vite, some extra steps had to be taken in order to allow for a successful deployment. Extra steps were followed from this blog post by Ndagi Stanley.
npm install serve
package.json
include the following start script:
"start": "serve -s dist"
To work on this repository independently without affecting the original source code, the repo can be forked.
This repository can be cloned onto your own machine locally which will be synced with the repository on GitHub. This can be done to contribute directly.
A big thank you to my family for allowing me to pursue this Full Stack Development diploma to change my career path. Also for their patience and understanding in the last year. Also a special thank you to my friends and partner for being supportive throughout.