{"id":22418992,"url":"https://github.com/paul-bokelman/over-engineered-todo-application","last_synced_at":"2026-05-06T22:10:24.682Z","repository":{"id":129968967,"uuid":"410381637","full_name":"paul-bokelman/over-engineered-todo-application","owner":"paul-bokelman","description":"An over-engineered todo application built for the P2-Anteaters team, featuring a beautiful UI with React, full authentication and todo system with Python (SQLAlchemy).","archived":false,"fork":false,"pushed_at":"2021-09-25T21:05:00.000Z","size":126,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-27T04:32:05.041Z","etag":null,"topics":["flask","react","sqlalchemy"],"latest_commit_sha":null,"homepage":"https://p2anteaters-todos.tk/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/paul-bokelman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-09-25T21:03:52.000Z","updated_at":"2021-09-25T21:08:42.000Z","dependencies_parsed_at":null,"dependency_job_id":"fdfce80b-7028-4c64-8803-92743b7ec7a0","html_url":"https://github.com/paul-bokelman/over-engineered-todo-application","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/paul-bokelman/over-engineered-todo-application","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paul-bokelman%2Fover-engineered-todo-application","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paul-bokelman%2Fover-engineered-todo-application/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paul-bokelman%2Fover-engineered-todo-application/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paul-bokelman%2Fover-engineered-todo-application/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/paul-bokelman","download_url":"https://codeload.github.com/paul-bokelman/over-engineered-todo-application/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paul-bokelman%2Fover-engineered-todo-application/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32713923,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-06T19:35:05.142Z","status":"ssl_error","status_checked_at":"2026-05-06T19:35:03.996Z","response_time":117,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["flask","react","sqlalchemy"],"created_at":"2024-12-05T16:13:44.671Z","updated_at":"2026-05-06T22:10:24.673Z","avatar_url":"https://github.com/paul-bokelman.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Todo application\n\nproject built for the P2-Anteaters team\n\n## How its made\n\nOur project is comprised of two standalone sections, the **server side** and the **client side**. We did this in order to use React on the frontend in order to make a beautiful \u0026 functional website in record time.\n\n#### Server side (Flask)\n\nThe first section of our project is our main server written in Python with the Flask lightweight framework. Our server was built to be a complete API so all actions are handled remotely through API requests from the client. This approach allows us to use a javascript framework like react for the frontend, without this approach it wouldn't be efficient.\n\nThe base of our server has two database models which are the `User` and `Todos` models that are bound through a one-to-many relationship which means that a `User` may have many `Todos` but a `Todos` may not have many `User`. Below are the model snippets.\n\n```py\nclass User(UserMixin, db.Model):\n    id = db.Column(db.Integer, primary_key=True)\n    username = db.Column(db.String(15), unique=True)\n    email = db.Column(db.String(50), unique=True)\n    password = db.Column(db.String(80))\n    todos = db.relationship('Todos', backref='author')\n\n\nclass Todos(db.Model):\n    id = db.Column(db.Integer, primary_key=True)\n    name = db.Column(db.String(120), nullable=False)\n    desc = db.Column(db.Text, nullable=False)\n    completed = db.Column(db.Boolean)\n    date_added = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)\n    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)\n```\n\nAlthough these models would be rendered useless without server functionality so we added some API routes so that the client side of the application could interact with relational database. Below are an explanation of just 2 of the 8 routes.\n\n**Adding Todo**\n\nA crucial part of our application was to allow the logged in user to add a \"todo\", otherwise we wouldn't have anything. Similar to all of the other routes in the `app.py` we accomplished this by creating an API route that can we requested through the client side.\n\n```py\n@app.route('/add_todo/\u003cusername\u003e', methods=['POST'])\ndef add_todo(username):\n    d = request.get_json()\n    user = User.query.filter_by(username=username).first()\n    todo = Todos(name=d['name'], desc=d['desc'], completed=d['completed'], author=user)\n    db.session.add(todo)\n    db.session.commit()\n    return 'Added Todo', 201\n```\n\nThis route in particular takes a `username` parameter as well as a JSON object from the incoming POST request. This incoming JSON is then deconstructed by the individual keys after the corresponding user object matching the `username` parameter is found, these deconstructed keys are then committed into the database through the current users corresponding Id.\n\n**Users Todos**\n\nIn order to display all of the users individual \"todos\" onto the screen we first need the data to be accessed by the client side. This is why we have a route that returns all of a users \"todos\".\n\n```py\n@app.route('/todos/\u003cusername\u003e', methods=['GET'])\ndef todos(username):\n    for user in User.query.filter_by(username=username):\n        todos = []\n        for todo in user.todos:\n            todos.append({'id':todo.id, 'name': todo.name, 'desc': todo.desc, 'completed': todo.completed, 'date_added': todo.date_added})\n        response = jsonify({'todos': todos})\n        return response\n    return 'Request failed, make sure user exists (case sensitive).', 404\n```\n\nThis route takes a single parameter which is the `username` parameter, similar to the `/add_todo` route. We then use the `username` string to find the users particular corresponding object in the database, this object contains all type of information about the user although through dot notation we can loop over all of the users todos. All of the todos are then appended to an array, converted to acceptable JSON then returned back to the client. In the case that the user doesn't exist there is a fallback return that simply lets the client know that the server couldn't find the entry.\n\n#### Client side (React)\n\nThe client side (frontend) of the application was built entirely in React, and without the server is completely functionless although servable for frontend functionality. The biggest advantage that React provides as opposed to traditional front-end development is that you can build functional components which are practically snippets of the ui that can all be combined in order to build a faster and cleaner application. Another major advantage of using React for frontend development is the access to the massive ecosystem of libraries to help aid you in the development process, this project is no exception and is utilizing lots of libraries.\n\n**Application Structure**\n\nThe client side is divided into 2 major sections, The \"server-optional\" and the \"server-dependant\" sections. As the names imply they both function as namely implied. For each section there will be a snippet and a more in depth look into the sub sections.\n\n###### _Server dependant_\n\nThe server optional section of the client is the section of the application where the integrity and functionality of the Flask server really shines, these areas in the client are where data is relayed back and fourth between the server and the client in order to accomplish a full stack application.\n\nThe server dependant section is comprised of the auth and dashboard sub-sections. These two sub-sections interact with the server differently although interact regardless.\n\n- [Auth section](https://github.com/PedroBMedeiros/P2-Anteaters/blob/main/client/src/Pages/Auth.jsx) - This section has all of the forms and on-demand request functions associated with the authentication process. This page in particular is a deeply nested tree of components which each serve their purpose. In the nested `Form.jsx` component there are clear server request functions, here are the signup and login functions. Each of these functions accept a deconstructed object with different required values, those values are then converted to JSON format and \"posted\" to the corresponding route for server validation.\n\n```jsx\nconst handleSignup = async ({ username, email, password }) =\u003e {\n  const data = {\n    username,\n    email,\n    password,\n  };\n  const response = await axios({\n    method: \"post\",\n    url: \"https://antsapi.nighthawkcodingsociety.com/signup\",\n    data,\n  }).catch((error) =\u003e error.response);\n  if (response.status === 201) {\n    setLoggingIn(true);\n  } else {\n    console.log(response.status);\n  }\n};\n\nconst handleLogin = async ({ username, password, remember }) =\u003e {\n  const data = {\n    username,\n    password,\n    remember: remember === undefined ? false : remember,\n  };\n  console.log(data);\n  setLoading(true);\n  const response = await axios({\n    method: \"post\",\n    url: \"https://antsapi.nighthawkcodingsociety.com/login\",\n    data,\n  }).catch((error) =\u003e error.response);\n  if (response.status === 200) {\n    setUsername(username);\n    logIn();\n    history.push({\n      pathname: \"/dashboard\",\n      state: { username: username, loggedIn: true },\n    });\n  } else {\n    console.log(response.status);\n  }\n  setLoading(false);\n};\n```\n\n- [Dashboard section](https://github.com/PedroBMedeiros/P2-Anteaters/blob/main/client/src/Pages/Dashboard.jsx) - The dashboard is the meat of the project. This page has by far the most functionality as well as interaction with the server. To oversimplify things, there are two main things that are happening within this page, the first is the **fetching of the users todos** this functionality is handled by using the username of the user logged in stored by the state to get the corresponding todos by concatenating the username in the `/todos` route. The second major part of this page is the **adding of todos for a particular user**. This function simply gets the input data from the forms in the \"adding\" card in the dashboard then uses that data to make a post request through axios and adds an entry to the database for the current user.\n\n```jsx\n// fetching of the users todos\nconst { isLoading, isError, data, error, refetch } = useQuery(\"todos\", () =\u003e\n  axios.get(\n    `https:/https://antsapi.nighthawkcodingsociety.com/todos/${state.username}`\n  )\n);\n```\n\n```jsx\n// adding of todos for a particular user\nconst addTodo = async ({ name, desc }) =\u003e {\n  const data = {\n    name,\n    desc,\n    completed: false,\n  };\n  console.log(data);\n  const response = await axios({\n    method: \"post\",\n    url: `https://https://antsapi.nighthawkcodingsociety.com/add_todo/${state.username}`,\n    data,\n  }).catch((error) =\u003e error.response);\n  if (response.status === 201) {\n    console.log(\"todo added\");\n    refetch();\n  } else {\n    console.log(response.status);\n  }\n};\n```\n\n###### _Server optional_\n\nThe server optional section of the client is arguably the less important section of the two considering that it doesn't interact with the server in any fashion. The major sub-sections of this section include the home page as well as the members sections.\n\n- [Home Page](https://github.com/PedroBMedeiros/P2-Anteaters/blob/main/client/src/Pages/Home.jsx) - By far the most style intensive, the home page has multiple intertwined components that are configured to display the different sections of the home page. Each \"feature\" section on the homepage is made up from a single component, this component takes multiple props to determine the variety in the particular section.\n\n```jsx\n// Home page jsx to pass props to Feature component\n\u003cFeature\n  icon=\"easy\"\n  tag={[\"Fundamentally easy\", 1, \"text-green-400\"]} //green\n  header=\"We prioritize simplicity.\"\n\u003e\n  Generally todo applications these days have very cluttered user interfaces and\n  advertisements in every corner, we aim to change that. Our ui was built\n  passion and the people in mind and makes everything from viewing your todos to\n  adding new ones a breeze.\n  \u003cdiv className=\"text-gray-300 text-left mt-8\"\u003e\n    \u003cLink to=\"/auth\" className=\"inline-block text-center\"\u003e\n      \u003cdiv className=\"h-36 w-52 group px-12 py-8 rounded-2xl border-2 bg-white hover:shadow-xl hover:text-green-500 hover:border-green-500 hover:border-4 duration-200\"\u003e\n        \u003cFaRegIdBadge className=\"text-5xl mx-auto\" /\u003e\n        \u003ch1 className=\"mt-2 font-semibold text-xl\"\u003eRegister\u003c/h1\u003e\n      \u003c/div\u003e\n    \u003c/Link\u003e\n    \u003cdiv className=\"inline-block group px-6 py-8 text-3xl bg-white\"\u003e\n      \u003cTiArrowRightThick /\u003e\n    \u003c/div\u003e\n    \u003cLink to=\"/auth\" className=\"inline-block text-center\"\u003e\n      \u003cdiv className=\"relative h-36 w-52 group px-12 py-8 rounded-2xl border-2 bg-white hover:shadow-xl hover:text-green-400 hover:border-green-400 hover:border-4 duration-200\"\u003e\n        \u003cHiOutlineUser className=\"text-5xl mx-auto\" /\u003e\n        \u003ch1 className=\"mt-2 font-semibold text-xl\"\u003eLogin\u003c/h1\u003e\n      \u003c/div\u003e\n    \u003c/Link\u003e\n    \u003cdiv className=\"inline-block group px-6 py-8 text-3xl bg-white\"\u003e\n      \u003cTiArrowRightThick /\u003e\n    \u003c/div\u003e\n    \u003cLink to=\"/dashboard\" className=\"inline-block text-center\"\u003e\n      \u003cdiv className=\"relative h-36 w-52 group px-12 py-8 rounded-2xl border-2 bg-white hover:shadow-xl hover:text-green-300 hover:border-green-300 hover:border-4 duration-200\"\u003e\n        \u003cBsCardChecklist className=\"text-5xl mx-auto\" /\u003e\n        \u003ch1 className=\"mt-2 font-semibold text-xl\"\u003eTodos\u003c/h1\u003e\n      \u003c/div\u003e\n    \u003c/Link\u003e\n  \u003c/div\u003e\n\u003c/Feature\u003e\n```\n\n```jsx\n// Feature component that takes deconstructed props from parent\nexport const Feature = ({ icon, tag, header, children }) =\u003e {\n  const getIcon = () =\u003e {\n    switch (icon) {\n      case \"easy\":\n        return \u003cBiAward /\u003e;\n      case \"speed\":\n        return \u003cBsLightningFill /\u003e;\n      case \"team\":\n        return \u003cRiTeamFill /\u003e;\n    }\n  };\n\n  const primary =\n    tag[1] === 1\n      ? \"from-green-400\"\n      : tag[1] === 2\n      ? \"from-yellow-400\" //blue\n      : \"from-blue-400\";\n  const secondary =\n    tag[1] === 1\n      ? \"to-green-600\"\n      : tag[1] === 2\n      ? \"to-yellow-600\" //blue\n      : \"to-blue-600\";\n\n  return (\n    \u003cdiv className=\"mt-36\"\u003e\n      \u003cdiv\n        className={`w-12 h-12 rounded-xl bg-gradient-to-br flex items-center justify-center ${primary} ${secondary} text-2xl text-white`}\n      \u003e\n        {getIcon()}\n      \u003c/div\u003e\n      \u003ch4 className={`mt-4 uppercase ${tag[2]} font-semibold text-lg`}\u003e\n        {tag[0]}\n      \u003c/h4\u003e\n      \u003ch3 className=\"text-6xl font-bold\"\u003e{header}\u003c/h3\u003e\n      \u003cp className=\"mt-2 max-w-4xl text-xl text-gray-600 font-medium leading-8 mb-6\"\u003e\n        {children[0]}\n      \u003c/p\u003e\n      \u003cdiv\u003e{children[1]}\u003c/div\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n- [Members sections](https://github.com/PedroBMedeiros/P2-Anteaters/blob/main/client/src/Pages/Members.jsx) - The members section is a very hard section to simply represent through 1 singular file, this is because the members section is comprised of many different components that conditionally render based on the name param. This works by having the initial button with the select options redirect the user to a route that checks the specific param, depending on the param a certain member-specific component will be rendered with their corresponding information.\n\n```jsx\n// Members select page\nexport const Members = () =\u003e {\n  const history = useHistory();\n  return (\n    \u003cdiv\u003e\n      \u003cFormik\n        initialValues={{ member: \"Mackenzie\" }}\n        onSubmit={(values) =\u003e {\n          history.push(`/member/${values.member}`);\n        }}\n      \u003e\n        {() =\u003e (\n          \u003cForm\u003e\n            \u003cFormWrapper\u003e\n              \u003cField\n                name=\"member\"\n                component=\"select\"\n                className=\"block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50\"\n              \u003e\n                \u003coption value=\"Mackenzie\"\u003eMackenzie\u003c/option\u003e\n                \u003coption value=\"Anthony\"\u003eAnthony\u003c/option\u003e\n                \u003coption value=\"Pedro\"\u003ePedro\u003c/option\u003e\n                \u003coption value=\"Naweid\"\u003eNaweid\u003c/option\u003e\n                \u003coption value=\"Cherry\"\u003eCherry\u003c/option\u003e\n              \u003c/Field\u003e\n              \u003cSubmitButton text=\"Go\" /\u003e\n            \u003c/FormWrapper\u003e\n          \u003c/Form\u003e\n        )}\n      \u003c/Formik\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n```jsx\n// Redirect page for specific component\nexport const MemberTemplate = () =\u003e {\n  let { member } = useParams();\n  return (\n    \u003cdiv\u003e\n      {member === \"Mackenzie\" ? (\n        \u003cKenzie /\u003e\n      ) : member === \"Anthony\" ? (\n        \u003cAnthony /\u003e\n      ) : member === \"Pedro\" ? (\n        \u003cPedro /\u003e\n      ) : member === \"Naweid\" ? (\n        \u003cNaweid /\u003e\n      ) : member === \"Cherry\" ? (\n        \u003cCherry /\u003e\n      ) : (\n        \u003ch1\u003emember not found\u003c/h1\u003e\n      )}\n      \u003cBubbleSort /\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n```jsx\n// Example of member component\nexport const Anthony = () =\u003e {\n  const [res, setRes] = useState(cube(2));\n  return (\n    \u003cdiv\u003e\n      \u003cHeading name=\"Anthony\" /\u003e\n      \u003cLabel\u003eCube Formula:\u003c/Label\u003e\n      \u003cFormik\n        initialValues={{ x: \"\" }}\n        onSubmit={(values) =\u003e {\n          const { x } = values;\n          setRes(cube(x));\n        }}\n      \u003e\n        {() =\u003e (\n          \u003cForm\u003e\n            \u003cFormWrapper\u003e\n              \u003cInputForm label=\"x\" name=\"x\" type=\"number\" /\u003e\n              \u003cSubmitButton text=\"Submit\" /\u003e\n            \u003c/FormWrapper\u003e\n          \u003c/Form\u003e\n        )}\n      \u003c/Formik\u003e\n      \u003cAnswer\u003e{res}\u003c/Answer\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n**Libraries**\n\nAs previously mentioned the client side uses lots of libraries, below is a list of the **_Major_** libraries and their purpose/contribution in the project.\n\n- [Axios](https://axios-http.com/docs/intro) - Used to make HTTP requests to the server side of the application with ease.\n\n- [React Query](https://react-query.tanstack.com/) - Responsible for managing state as well as caching data from server responses.\n\n- [Tailwind CSS](https://tailwindcss.com/) - Ease of styling for full application.\n\n**Deployment**\n\nOur client side deployment was done on [Netlify](https://www.netlify.com/), this means that we have two different deployments with no strings attached. The reason we chose to deploy the client side on Netlify instead of the raspberry pi is because on Netlify everything is git-flow oriented which means that all you need to do to deploy a client application built on node is simply login and choose the repo.\n\n## Technical Requirements\n\n- Python\n- SQLAlchemy\n- Flask\n- React\n- Raspberry Pi\n- HTML/CSS\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaul-bokelman%2Fover-engineered-todo-application","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpaul-bokelman%2Fover-engineered-todo-application","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaul-bokelman%2Fover-engineered-todo-application/lists"}