Skip to content

A React + Flask web app for managing projects and tasks

Notifications You must be signed in to change notification settings

jamesdoh0109/jdtodo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

64 Commits
 
 
 
 
 
 

Repository files navigation

JDTodo

A simple tool for managing projects and tasks

GitHub Repo stars   Stack Overflow Badge

Table of Contents

Project Overview

JDTodo is a web application designed for managing projects and tasks. It uses a React for the frontend and Python Flask for the backend to provide a user-friendly and efficient project/task management system.

Tech stack

Frontend

React Javascript Redux React Query React Hook Form Tailwind CSS

Backend

Python Flask

Database

MySQL

Features

JDTodo is more than just a simple todo list application. With JDTodo, you can:

  • Create a project (e.g. "Final exam prep")
  • Update a project with a new name (e.g. "Final exam prep" -> "CIS 121 exam prep")
  • Delete a project
  • Create tasks for a given project (e.g. "Review ch.9 and do 20 exercises on pg. 257") with a deadline, status (Not started, In progress, Finished), and description
  • Update tasks (e.g. marking it as "Finished", changing the deadline, etc.)
  • Delete tasks
  • See which tasks are overdue

Installation and Setup

Fork this repository and clone it locally. Once completed, follow these steps:

Environment variables

Create a .env file at the root of the repository, then define the following 6 environment variables:

  • SQLALCHEMY_DATABASE_URI
    • URI used to connect to the database (e.g. sqlite:////tmp/test.db, mysql://username:password@server/db). You will first need to create your own.
  • JWT_SECRET_KEY
    • Secret key used for signing and verifying JWTs. A good rule of thumb is to use a secret key with a length of at least 256 bits (32 bytes) or longer and also a random, unique string generated cryptographically (e.g. secrets.token_hex(32)).
  • MAIL_USERNAME
    • Username for your Gmail account
  • MAIL_PASSWORD
    • Password for your Gmail account
  • BASE_FRONTEND_URL
  • REACT_APP_BASE_BACKEND_URL

Backend

Once these have been set, then follow these steps to run the backend development server:

  1. cd backend
  2. pipenv shell
  3. pipenv install
  4. flask --app app run

Frontend

Follow these steps to run the frontend development server:

  1. cd frontend
  2. npm install
  3. npm start

Pages

Below are some of the essential pages that underpin the core functionality and represent the backbone of the application. Presented here are detailed descriptions and accompanying screenshots of the Dashboard, Project Details, and Manage Account pages.

Dashboard

On the dasboard page, you can view and create/edit/delete projects, and also navigate to the project detail page by clicking on a project card:

Desktop

Mobile

Project Details

Once you click on one of your project cards, you will be on the project detail page, where you can view and create/edit/delete tasks. You can also click on each task to view its detail:

Desktop

Mobile

Manage Account

If you need to make changes to your profile, visit the account page, where you can modify your name, email, and password, and also delete your account:

Desktop

Mobile

Implementation Details/Technical Notes

API Documentation

NOTE: this represents the application's internal API and is not intended for use as an open API. It's important to note that documenting an internal API can pose certain security risks. However, in the context of showcasing my portfolio project, the purpose is solely to provide supplementary information.

Here are the Flask API endpoints for managing users, projects, and tasks (these can be found in modules in backend/routes):

User endpoints

POST /api/login (authenticates the user given his/her log-in credentials)
Parameters
name type data type description
email required String the email of the user attempting to log in
password required String the password of the user attempting to log in
Response
http code response
200 { 'user': { 'id': user.id, 'firstname': user.firstname, 'lastname': user.lastname, 'email': user.email }, 'message': 'Login success' }
400 { 'error': 'Missing request body' } OR { 'error': 'Please fill in all the required field(s)' } OR { 'error': 'Email is not valid' }
401 { 'error': 'Invalid credentials' }
500 { 'error': 'Server error: please try again' }
POST /api/logout (terminates the user's active session)
Parameters

None

Response
http code response
200 { 'message': 'Logout success' }
POST /api/signup (creates the user given his/her registration information)
Parameters
name type data type description
firstname required String the first name of the user attempting to sign up
lastname required String the last name of the user attempting to sign up
email required String the email of the user attempting to sign up
password required String the password of the user attempting to sign up
passwordConfirm required String the confirmed password of the user attempting to sign up
Response
http code response
201 { 'message': 'User successfully created' }
400 { 'error': 'Missing request body' } OR { 'error': 'Please fill in all the required field(s)' } OR { 'error': 'Email is not valid' } OR { 'error': 'Password must match' } OR { 'error': 'Password must contain at least 8 characters, 1 uppercase letter, and 1 number' }
409 { 'error': 'Email already exists' }
500 { 'error': 'Server error: please try again' }
GET /api/user (retrieves information about a logged-in user using his/her JSON Web Token(JWT))
Parameters

None

Response
http code response
200 { 'user': { 'id': user.id, 'firstname': user.firstname, 'lastname': user.lastname, 'email': user.email }, 'message': 'User successfully fetched' }
401 { 'error': 'Missing cookie 'access_token_cookie'' }
500 { 'error': 'Server error: please try again' }
PATCH /api/user/<user_id> (modifies the user's firstname, lastname, and/or email)
Parameters
name type data type description
firstname required String the new first name of the user
lastname required String the new last name of the user
email required String the new email of the user
Response
http code response
200 { 'user': { 'id': user.id, 'firstname': user.firstname, 'lastname': user.lastname, 'email': user.email }, 'message': 'User successfully modified' }
400 { 'error': 'Missing request body' } OR { 'error': 'Please fill in all the required field(s)' } OR { 'error': 'Email is not valid' }
401 { 'error': 'Missing cookie 'access_token_cookie'' }
403 { 'error': 'Access denied' }
404 { 'error': 'User not found' }
409 { 'error': 'Email already exists' }
500 { 'error': 'Server error: please try again' }
DELETE /api/user/<user_id> (deletes the user's account)
Parameters

None

Response
http code response
200 { 'message': 'User successfully deleted' }
401 { 'error': 'Missing cookie 'access_token_cookie'' }
403 { 'error': 'Access denied' }
404 { 'error': 'User not found' }
500 { 'error': 'Server error: please try again' }
PATCH /api/change_password/<user_id> (modifies the user's password)
Parameters
name type data type description
passwordCurrent required String the current password of the user
passwordNew required String the new password of the user
passwordConfirm required String the confirmed new password of the user
Response
http code response
200 { 'message': 'Password successfully updated' }
400 { 'error': 'Missing request body' } OR { 'error': 'Please fill in all the required field(s)' } OR { 'error': 'Email is not valid' } OR { 'error': 'Passwords must match' } OR { 'error': 'Password must contain at least 8 characters, 1 uppercase letter, and 1 number'} OR { 'error': 'New password must be different from your current one' }
401 { 'error': 'Missing cookie 'access_token_cookie'' }
403 { 'error': 'Access denied' } OR { 'error': 'Incorrect password' }
404 { 'error': 'User not found' }
500 { 'error': 'Server error: please try again' }
POST /api/forgot_password (sends to the user an email with a link to a page for resetting passwords)
Parameters
name type data type description
email required String the email of the user with forgotten password
Response
http code response
200 { 'message': 'Email successfully sent' }
400 { 'error': 'Missing request body' } OR { 'error': 'Please fill in all the required field(s)' } OR { 'error': 'Email is not valid' }
404 { 'error': 'User not found' }
500 { 'error': 'Server error: please try again' }
PATCH /api/reset_password/<token> (resets the password for the user identified by the token for resetting passwords)
Parameters
name type data type description
password required String the new password of the user
passwordConfirm required String the confirmed new password of the user
Response
http code response
200 { 'message': 'Password successfully updated' }
400 { 'error': 'Missing request body' } OR { 'error': 'Please fill in all the required field(s)' } OR { 'error': 'Passwords must match' } OR { 'error': 'Password must contain at least 8 characters, 1 uppercase letter, and 1 number'} OR { 'error': 'New password must be different from your original one' }
404 { 'error': 'User not found' }
500 { 'error': 'Server error: please try again' }
GET /api/verify_reset_password_token/<token> (verifies if the token in the request URI is valid or not)
Parameters

None

Response
http code response
200 { 'message': 'Token found for {user.id}' }
403 { 'error': 'Token expired' }
404 { 'error': 'Token not found' }
500 { 'error': 'Server error: please try again' }
GET /api/protected (returns whether user has access to protected pages (i.e. whether user is logged-in or not))
Parameters

None

Response
http code response
200 { 'message': 'User authorized' }
401 { 'error': 'Missing cookie 'access_token_cookie'' }

Project endpoints

GET /api/projects/<project_id> (retrieves the project with project_id)
Parameters

None

Response
http code response
200 { 'project': { 'proj_id': project.id, 'proj_name': project.name, 'date_updated': project.updated_at }, 'message': 'Project successfully fetched' }
401 { 'error': 'Missing cookie 'access_token_cookie'' }
403 { 'error': 'Access denied' }
404 { 'error': 'Project not found' }
500 { 'error': 'Server error: please try again' }
PATCH /api/projects/<project_id> (modifies the name of the project with project_id)
Parameters
name type data type description
name required String the name of the project being modified
Response
http code response
200 { 'project': { 'proj_id': project.id, 'proj_name': project.name, 'date_updated': project.updated_at }, 'message': 'Project successfully modified' }
400 { 'error': 'Missing request body' } OR { 'error': 'Please fill in all the required field(s)' } OR { 'error': 'Project name must be less than 25 characters' }
401 { 'error': 'Missing cookie 'access_token_cookie'' }
403 { 'error': 'Access denied' }
404 { 'error': 'Project not found' }
409 { 'error': 'Project with the same name already exists' }
500 { 'error': 'Server error: please try again' }
DELETE /api/projects/<project_id> (deletes the project with project_id)
Parameters

None

Response
http code response
200 { 'message': 'Project successfully deleted' }
401 { 'error': 'Missing cookie 'access_token_cookie'' }
403 { 'error': 'Access denied' }
404 { 'error': 'Project not found' }
500 { 'error': 'Server error: please try again' }
GET /api/projects (retrieves a list of all projects for user identified by his/her JSON Web Token(JWT))
Parameters

None

Response
http code response
200 { 'project': [{ 'proj_id': project.id, 'proj_name': project.name, 'date_updated': project.updated_at }, ...], 'message': 'Projects successfully fetched' }
401 { 'error': 'Missing cookie 'access_token_cookie'' }
500 { 'error': 'Server error: please try again' }
POST /api/projects (creates a project with given the project name)
Parameters
name type data type description
name required String the name of the project being created
Response
http code response
201 { 'project': { 'proj_id': project.id, 'proj_name': project.name, 'date_updated': project.updated_at }, 'message': 'Project successfully created' }
400 { 'error': 'Missing request body' } OR { 'error': 'Please fill in all the required field(s)' } OR { 'error': 'Project name must be less than 25 characters' }
401 { 'error': 'Missing cookie 'access_token_cookie'' }
409 { 'error': 'Project with the same name already exists' }
500 { 'error': 'Server error: please try again' }

Task endpoints

GET /api/<project_id>/<tasks> (retrieves a list of tasks for project with project_id)
Parameters

None

Response
http code response
200 { 'tasks': [{ 'task_id': task.id, 'task_name': task.name, 'task_description': task.description, 'task_deadline': task.deadline, 'task_status': task.status }, ...], 'message': 'Tasks successfully fetched' }
403 { 'error': 'Access denied' }
404 { 'error': 'Project not found' }
500 { 'error': 'Server error: please try again' }
POST /api/<project_id>/<tasks> (creates a task with given task info for project with project_id)
Parameters
name type data type description
name required String the name of the task being created
deadline required DateTime the deadline of the task being created
status required String the status of the task being created
description optional String the description of the task being created
Response
http code response
201 { 'task': { 'task_id': task.id, 'task_name': task.name, 'task_description': task.description, 'task_deadline': task.deadline, 'task_status': task.status }, 'message': 'Task successfully created' }
400 { 'error': 'Missing request body' } OR { 'error': 'Please fill in all the required field(s)' } OR { 'error': 'Task name must be less than 60 characters' } OR { 'error': 'Task deadline cannot be in the past' }
401 { 'error': 'Missing cookie 'access_token_cookie'' }
403 { 'error': 'Access denied' }
404 { 'error': 'Project not found' }
409 { 'error': 'Task with the same name already exists' }
500 { 'error': 'Server error: please try again' }
PATCH /api/tasks/<task_id> (modifies the task info for task with task_id)
Parameters
name type data type description
name required String the name of the task being modified
deadline required DateTime the deadline of the task being modified
status required String the status of the task being modified
description optional String the description of the task being modified
Response
http code response
200 { 'task': { 'task_id': task.id, 'task_name': task.name, 'task_description': task.description, 'task_deadline': task.deadline,'task_status': task.status }, 'message': 'Task successfully modified' }
400 { 'error': 'Missing request body' } OR { 'error': 'Please fill in all the required field(s)' } OR { 'error': 'Task name must be less than 60 characters' } OR { 'error': 'Task deadline cannot be in the past' } OR { 'error': 'Task description must be less than 300 characters' }
401 { 'error': 'Missing cookie 'access_token_cookie'' }
403 { 'error': 'Access denied' }
404 { 'error': 'Task not found' }
409 { 'error': 'Task with the same name already exists' }
500 { 'error': 'Server error: please try again' }
DELETE /api/tasks/<task_id> (deletes the task with task_id)
Parameters

None

Response
http code response
200 { 'message': 'Task successfully deleted' }
401 { 'error': 'Missing cookie 'access_token_cookie'' }
403 { 'error': 'Access denied' }
404 { 'error': 'Task not found' }
500 { 'error': 'Server error: please try again' }

Data Models

NOTE: much like the preceding discussion on API routes, the presentation of this database structure serves the purpose of showcasing a pivotal facet of my portfolio project. However, it is crucial to acknowledge that the exposure of such sensitive information introduces potential security vulnerabilities.

In backend/models, you will find 3 modules, user.py, project.py, task.py, each of which represents data for the 3 entities in this project: user, project, and task. Here is the Entry Relationship Diagram (ERD) that shows each field for each model, and how the 3 models work together:

Note on State Management

In this fullstack application, we need to manage both the client and server states. I have implemented Redux and React Redux for managing client state, and React-Query for managing server states. Each usage has been evaluated to ensure I am not duplicating state management between multiple tools whenever possible.

Get in Touch

If you have any questions or want to contribute, feel free to reach out to jdtodo.help@gmail.com!

About

A React + Flask web app for managing projects and tasks

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published