├── .gitattributes ├── .gitignore ├── LICENSE.md ├── README.md ├── netlify.toml ├── package-lock.json ├── package.json ├── public ├── assets │ ├── cartoon │ │ ├── happydev.png │ │ ├── happydev.svg │ │ ├── neutraldev.png │ │ ├── neutraldev.svg │ │ ├── saddev.png │ │ ├── saddev.svg │ │ ├── smileydev.png │ │ └── smileydev.svg │ ├── exercise.png │ ├── fire-symbol.ico │ ├── fire-symbol.png │ ├── sidebar │ │ ├── backlogo.png │ │ ├── backlogo2.png │ │ ├── earlogi.png │ │ ├── eyelogo.png │ │ ├── eyelogo2.png │ │ ├── home.png │ │ └── settings.png │ ├── traianglelogo2.png │ └── trianglelogo.png ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json ├── robots.txt └── sw.js └── src ├── App.css ├── App.js ├── components ├── charts │ ├── BarChart.js │ ├── LineChart.js │ └── PieChart.js ├── core │ ├── Modal.js │ ├── Nav.js │ ├── Score.js │ ├── Sidebar.js │ ├── TimerClock.js │ └── Toggles.js └── onboarding │ ├── Onboarding.js │ ├── OnboardingBackCare.js │ ├── OnboardingBackTrain.js │ ├── OnboardingHow.js │ ├── OnboardingName.js │ ├── OnboardingNotifs.js │ └── OnboardingSteps.js ├── constants └── about.constant.js ├── css ├── base │ ├── reset.css │ └── root.css ├── components │ ├── alert.css │ ├── modal.css │ ├── nav.css │ ├── sidebar.css │ ├── timerclock.css │ └── toggle.css ├── pages │ ├── care.css │ ├── dashboard.css │ ├── onboarding.css │ └── trainback.css ├── style.css └── utilities │ ├── colors.css │ ├── container.css │ ├── display.css │ ├── flex.css │ ├── grid.css │ ├── spacing.css │ └── typography.css ├── index.css ├── index.js ├── screens ├── Aboutus.js ├── BackCare.js ├── Dashboard.js ├── EarCare.js ├── Exercise.js ├── EyeCare.js ├── HowtoUse.js └── TrainBackCare.js └── services ├── datahandling.js └── notifications.js /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Siddhi Gate 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dev Care: One stop solution to take care of your health as developers 👩‍💻 2 | One-stop solution to take care of your health as developers. Dev Care will help you to take care of your back, eyes, and ears while working. The solutions we are working with are as follows: 3 | 4 | ## Features 5 | 6 | ### Back health: 7 | For back health, the app has the feature of posture detection. It detects your posture at regular intervals and reminds you to sit straight if you are not. 8 | 9 | 10 | ### Eyes health: 11 | 12 | To relax your eyes there is a 20-20-20 rule which states that: 13 | 14 | > For every 20 minutes spent looking at a screen, a person should look at something 20 feet away for 20 seconds 15 | 16 | This exercise relaxes our eyes and reduces eye strain. The app reminds you to follow this exercise at regular intervals. 17 | 18 | 19 | ### Ear health: 20 | 21 | For ear health, there is a 60-60 rule: 22 | 23 | > Only turn your device volume up to 60% and listen for no more than 60 minutes per day 24 | 25 | The app checks if you are using earphones for longer than 60 minutes and reminds you to control your earphone usage if it is becoming unhealthy. 26 | 27 | 28 | 29 | ## App walkthrough✨ 30 | 31 | ### Onboarding👋 32 | 33 | When a user visits the app for the first time, initial information and instructions are displayed to the user to make them familiar with the app. This app doesn’t require a login or signup. 34 | 35 | ![Onboarding](https://cdn.hashnode.com/res/hashnode/image/upload/v1646063229646/SR6dYn1l_.gif) 36 | 37 | 38 | ### Posture detection initial setup🧍‍♂️ 39 | 40 | The app first needs to understand what a good posture is and what a bad posture is. For that, you’ll need to click at least 10 pictures for both cases. The app will learn from those images what a correct and incorrect posture is. After which, at regular intervals, the app will be able to check your posture and further inform you if it is not correct. 41 | 42 | 43 | ![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1646064071016/N0IcAA4qn.png) 44 | 45 | ### Dashboard 🏠 46 | 47 | The dashboard displays the countdown timer. You can start and stop it whenever you want. You can opt to select only a few options to receive notifications for between back care, eye care, and ear care. Additionally, the app has a sweet cartoon creator whose mood will depend on how effectively you use this app. 48 | ![Dashboard](https://cdn.hashnode.com/res/hashnode/image/upload/v1646062814254/SPLzpsgMY.gif) 49 | 50 | 51 | ### Reminders 🔔 52 | 53 | You’ll receive reminders for doing the exercise, if your posture is incorrect and if you are using your earphones more than the limit at regular intervals. 54 | 55 | ![Notification reminders](https://cdn.hashnode.com/res/hashnode/image/upload/v1646067642936/hbbN63zRD.gif) 56 | 57 | ### Analysis📊 58 | 59 | Dev Care will perform an analysis of your habits and visualize them through different charts. For back care, a pie chart is used to visualize how many times you are found sitting in good and bad posture. For eye care, a line chart is used to depict how many times a day you follow the exercise. For ear care, a bar graph is used to depict the daily usage of earphones. 60 | 61 | 62 | ![ezgif.com-gif-maker (5).gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1646063976757/ElQyRLH3h.gif) 63 | 64 | 65 | ## Technologies and Tools used🛠 66 | - [React JS](https://reactjs.org/) 67 | - [TensorFlow JS](https://www.tensorflow.org/js) 68 | - [Chart JS](https://www.chartjs.org/) 69 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [[redirects]] 2 | from = "/*" 3 | to = "/index.html" 4 | status = 200 -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dev-care", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@tensorflow-models/knn-classifier": "^1.2.2", 7 | "@tensorflow-models/mobilenet": "^2.1.0", 8 | "@tensorflow/tfjs": "^3.13.0", 9 | "@testing-library/jest-dom": "^5.16.2", 10 | "@testing-library/react": "^12.1.2", 11 | "@testing-library/user-event": "^13.5.0", 12 | "react": "^17.0.2", 13 | "react-chartjs-2": "^4.0.1", 14 | "react-countdown": "^2.3.2", 15 | "react-dom": "^17.0.2", 16 | "react-router-dom": "^6.2.1", 17 | "react-scripts": "5.0.0", 18 | "react-webcam": "^6.0.1", 19 | "web-vitals": "^2.1.4" 20 | }, 21 | "scripts": { 22 | "start": "react-scripts start", 23 | "build": "CI= react-scripts build", 24 | "test": "react-scripts test", 25 | "eject": "react-scripts eject" 26 | }, 27 | "eslintConfig": { 28 | "extends": [ 29 | "react-app", 30 | "react-app/jest" 31 | ] 32 | }, 33 | "browserslist": { 34 | "production": [ 35 | ">0.2%", 36 | "not dead", 37 | "not op_mini all" 38 | ], 39 | "development": [ 40 | "last 1 chrome version", 41 | "last 1 firefox version", 42 | "last 1 safari version" 43 | ] 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /public/assets/cartoon/happydev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddhigate/dev-care-app/1231a8fb9d9d66af7660c97ba9fc893d75a790cd/public/assets/cartoon/happydev.png -------------------------------------------------------------------------------- /public/assets/cartoon/happydev.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/cartoon/neutraldev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddhigate/dev-care-app/1231a8fb9d9d66af7660c97ba9fc893d75a790cd/public/assets/cartoon/neutraldev.png -------------------------------------------------------------------------------- /public/assets/cartoon/neutraldev.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/cartoon/saddev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddhigate/dev-care-app/1231a8fb9d9d66af7660c97ba9fc893d75a790cd/public/assets/cartoon/saddev.png -------------------------------------------------------------------------------- /public/assets/cartoon/saddev.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/cartoon/smileydev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddhigate/dev-care-app/1231a8fb9d9d66af7660c97ba9fc893d75a790cd/public/assets/cartoon/smileydev.png -------------------------------------------------------------------------------- /public/assets/cartoon/smileydev.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/assets/exercise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddhigate/dev-care-app/1231a8fb9d9d66af7660c97ba9fc893d75a790cd/public/assets/exercise.png -------------------------------------------------------------------------------- /public/assets/fire-symbol.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddhigate/dev-care-app/1231a8fb9d9d66af7660c97ba9fc893d75a790cd/public/assets/fire-symbol.ico -------------------------------------------------------------------------------- /public/assets/fire-symbol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddhigate/dev-care-app/1231a8fb9d9d66af7660c97ba9fc893d75a790cd/public/assets/fire-symbol.png -------------------------------------------------------------------------------- /public/assets/sidebar/backlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddhigate/dev-care-app/1231a8fb9d9d66af7660c97ba9fc893d75a790cd/public/assets/sidebar/backlogo.png -------------------------------------------------------------------------------- /public/assets/sidebar/backlogo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddhigate/dev-care-app/1231a8fb9d9d66af7660c97ba9fc893d75a790cd/public/assets/sidebar/backlogo2.png -------------------------------------------------------------------------------- /public/assets/sidebar/earlogi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddhigate/dev-care-app/1231a8fb9d9d66af7660c97ba9fc893d75a790cd/public/assets/sidebar/earlogi.png -------------------------------------------------------------------------------- /public/assets/sidebar/eyelogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddhigate/dev-care-app/1231a8fb9d9d66af7660c97ba9fc893d75a790cd/public/assets/sidebar/eyelogo.png -------------------------------------------------------------------------------- /public/assets/sidebar/eyelogo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddhigate/dev-care-app/1231a8fb9d9d66af7660c97ba9fc893d75a790cd/public/assets/sidebar/eyelogo2.png -------------------------------------------------------------------------------- /public/assets/sidebar/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddhigate/dev-care-app/1231a8fb9d9d66af7660c97ba9fc893d75a790cd/public/assets/sidebar/home.png -------------------------------------------------------------------------------- /public/assets/sidebar/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddhigate/dev-care-app/1231a8fb9d9d66af7660c97ba9fc893d75a790cd/public/assets/sidebar/settings.png -------------------------------------------------------------------------------- /public/assets/traianglelogo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddhigate/dev-care-app/1231a8fb9d9d66af7660c97ba9fc893d75a790cd/public/assets/traianglelogo2.png -------------------------------------------------------------------------------- /public/assets/trianglelogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddhigate/dev-care-app/1231a8fb9d9d66af7660c97ba9fc893d75a790cd/public/assets/trianglelogo.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddhigate/dev-care-app/1231a8fb9d9d66af7660c97ba9fc893d75a790cd/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Dev Care 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddhigate/dev-care-app/1231a8fb9d9d66af7660c97ba9fc893d75a790cd/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siddhigate/dev-care-app/1231a8fb9d9d66af7660c97ba9fc893d75a790cd/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/sw.js: -------------------------------------------------------------------------------- 1 | self.addEventListener('notificationclick', function(event) { 2 | 3 | if (!event.action) { 4 | // Was a normal notification click 5 | console.log(event.notification.data) 6 | self.clients.openWindow(event.notification.data+'exercise') 7 | console.log('Notification Click.'); 8 | event.notification.close(); 9 | return; 10 | } 11 | 12 | switch (event.action) { 13 | case 'exercise-action': 14 | console.log('Do exercise'); 15 | self.clients.openWindow(event.notification.data+'exercise') 16 | break; 17 | case 'posture-action': 18 | console.log('Incorrect posture'); 19 | break; 20 | default: 21 | console.log(`Unknown action clicked: '${event.action}'`); 22 | break; 23 | } 24 | }); -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import "./css/style.css"; 2 | import { BrowserRouter, Routes, Route } from "react-router-dom"; 3 | import Dashboard from "./screens/Dashboard"; 4 | import BackCare from "./screens/BackCare"; 5 | import EyeCare from "./screens/EyeCare"; 6 | import EarCare from "./screens/EarCare"; 7 | import Exercise from "./screens/Exercise"; 8 | import TrainBackCare from "./screens/TrainBackCare"; 9 | import AboutUs from "./screens/Aboutus"; 10 | import HowtoUse from "./screens/HowtoUse"; 11 | 12 | function App() { 13 | return ( 14 | 15 | 16 | } /> 17 | } /> 18 | } /> 19 | } /> 20 | } /> 21 | } /> 22 | } /> 23 | } /> 24 | 25 | 26 | ); 27 | } 28 | 29 | export default App; 30 | -------------------------------------------------------------------------------- /src/components/charts/BarChart.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { 3 | Chart as ChartJS, 4 | CategoryScale, 5 | LinearScale, 6 | BarElement, 7 | Title, 8 | Tooltip, 9 | Legend, 10 | } from 'chart.js'; 11 | import { Bar } from 'react-chartjs-2'; 12 | 13 | ChartJS.register( 14 | CategoryScale, 15 | LinearScale, 16 | BarElement, 17 | Title, 18 | Tooltip, 19 | Legend 20 | ); 21 | 22 | export const options = { 23 | responsive: true, 24 | maintainAspectRatio: false, 25 | plugins: { 26 | title: { 27 | display: true, 28 | text: 'Ear Care Analysis', 29 | }, 30 | }, 31 | }; 32 | 33 | const labels = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; 34 | 35 | 36 | export function BarChart() { 37 | 38 | const [earData, setEarData] = useState([0,0,0,0,0,0,0]) 39 | 40 | 41 | useEffect(() => { 42 | let earData = JSON.parse(localStorage.getItem("eardata")); 43 | setEarData(earData); 44 | }, []) 45 | 46 | const data = { 47 | labels, 48 | datasets: [ 49 | { 50 | label: 'Earphone Usage Time', 51 | data: earData, 52 | backgroundColor: 'rgba(0, 153, 246, 0.5)', 53 | } 54 | ], 55 | }; 56 | 57 | return ; 58 | } 59 | -------------------------------------------------------------------------------- /src/components/charts/LineChart.js: -------------------------------------------------------------------------------- 1 | import { 2 | Chart as ChartJS, 3 | CategoryScale, 4 | LinearScale, 5 | PointElement, 6 | LineElement, 7 | Title, 8 | Tooltip, 9 | Legend, 10 | Filler, 11 | } from "chart.js"; 12 | import { useState, useEffect } from "react"; 13 | import { Line } from "react-chartjs-2"; 14 | 15 | ChartJS.register( 16 | CategoryScale, 17 | LinearScale, 18 | PointElement, 19 | LineElement, 20 | Title, 21 | Tooltip, 22 | Legend, 23 | Filler 24 | ); 25 | 26 | 27 | export function LineChart() { 28 | const [eyeData, setEyeData] = useState([0, 0, 0, 0, 0, 0, 0]); 29 | 30 | useEffect(() => { 31 | let eyedata = JSON.parse(localStorage.getItem("eyedata")); 32 | setEyeData(eyedata); 33 | }, []); 34 | 35 | const labels = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; 36 | const data = { 37 | labels: labels, 38 | datasets: [ 39 | { 40 | label: "Eye Care Exercises Analysis", 41 | fill: true, 42 | data: eyeData, 43 | borderColor: "rgba(0, 153, 246, 0.5)", 44 | backgroundColor: "rgba(0, 153, 246, 0.2)", 45 | responsive: true, 46 | }, 47 | ], 48 | }; 49 | 50 | 51 | return ( 52 | 59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /src/components/charts/PieChart.js: -------------------------------------------------------------------------------- 1 | import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js'; 2 | import { useState, useEffect } from 'react'; 3 | import { Pie } from 'react-chartjs-2'; 4 | 5 | ChartJS.register(ArcElement, Tooltip, Legend); 6 | 7 | const PieChart = () => { 8 | 9 | const [badcount, setBadCount] = useState(0); 10 | const [goodcount, setGoodCount] = useState(0); 11 | 12 | useEffect(() => { 13 | let badpostureCount = Number(localStorage.getItem("badposturecount")); 14 | let goodpostureCount = Number(localStorage.getItem("goodposturecount")); 15 | 16 | if(badpostureCount) { 17 | setBadCount(badpostureCount); 18 | } 19 | if(goodpostureCount) { 20 | setGoodCount(goodpostureCount) 21 | } 22 | }, []) 23 | 24 | 25 | const data = { 26 | labels: ['Bad posture', 'Good posture'], 27 | datasets: [ 28 | { 29 | label: '# count', 30 | data: [badcount, goodcount], 31 | backgroundColor: [ 32 | 'rgba(220, 38, 38, 0.7)', 33 | 'rgba(0, 153, 246, 0.7)', 34 | 35 | ], 36 | borderColor: [ 37 | 'rgba(220, 38, 38, 1)', 38 | 'rgba(0, 153, 246, 1)', 39 | 40 | ], 41 | borderWidth: 1, 42 | 43 | 44 | }, 45 | ], 46 | }; 47 | 48 | if(badcount === 0 & goodcount === 0){ 49 | return
50 | cartoon saying hi 51 |

Start using this feature to view the graph analysis here!

52 |
53 | } 54 | 55 | return ( 56 | 57 | ); 58 | }; 59 | 60 | export default PieChart; -------------------------------------------------------------------------------- /src/components/core/Modal.js: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom'; 2 | 3 | export default function Modal({ children, handleClose }) { 4 | return ReactDOM.createPortal(( 5 |
6 |
7 | {children} 8 |
9 |
10 | ), document.body) 11 | } -------------------------------------------------------------------------------- /src/components/core/Nav.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | 4 | const Nav = () => { 5 | return ( 6 |
7 |
8 | 9 |
10 | 11 | devcare 12 |
13 | 14 |
15 |
16 | ); 17 | }; 18 | 19 | export default Nav; 20 | -------------------------------------------------------------------------------- /src/components/core/Score.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Score = () => { 4 | return ( 5 |
6 | 7 |

8 | You are taking good care of your health! 9 |

10 |
11 | ); 12 | }; 13 | 14 | export default Score; 15 | -------------------------------------------------------------------------------- /src/components/core/Sidebar.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | 4 | const Sidebar = () => { 5 | return ( 6 | 40 | ); 41 | }; 42 | 43 | export default Sidebar; 44 | -------------------------------------------------------------------------------- /src/components/core/TimerClock.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const TimerClock = ({ hours, minutes, seconds }) => { 4 | return ( 5 |
6 | {minutes} : {seconds} 7 |
8 | ); 9 | }; 10 | 11 | export default TimerClock; 12 | -------------------------------------------------------------------------------- /src/components/core/Toggles.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | 3 | const Toggles = ({ 4 | backOption, 5 | setBackOption, 6 | eyeOption, 7 | setEyeOption, 8 | soundOption, 9 | setSoundOption, 10 | }) => { 11 | return ( 12 |
13 |
14 |
Back Care:
15 |
16 | 26 |
27 |
28 |
29 |
Eye Care:
30 |
31 | 41 |
42 |
43 |
44 |
Ear Care:
45 |
46 | 56 |
57 |
58 |
59 | ); 60 | }; 61 | 62 | export default Toggles; 63 | -------------------------------------------------------------------------------- /src/components/onboarding/Onboarding.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import Modal from "../core/Modal"; 3 | import OnboardingBackCare from "./OnboardingBackCare"; 4 | import OnboardingBackTrain from "./OnboardingBackTrain"; 5 | import OnboardingHow from "./OnboardingHow"; 6 | import OnboardingName from "./OnboardingName"; 7 | import OnboardingNotifs from "./OnboardingNotifs"; 8 | import OnboardingSteps from "./OnboardingSteps"; 9 | 10 | const Onboarding = () => { 11 | const [card, setCard] = useState("name"); 12 | 13 | return ( 14 | 15 | {card === "name" && } 16 | 17 | {card === "features" && } 18 | 19 | {card === "how" && } 20 | 21 | {card === "notifs" && } 22 | 23 | {card === "backcare" && } 24 | 25 | {card === "backtrain" && } 26 | 27 | ); 28 | }; 29 | 30 | export default Onboarding; 31 | -------------------------------------------------------------------------------- /src/components/onboarding/OnboardingBackCare.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const OnboardingBackCare = ({ setCard }) => { 4 | return ( 5 |
6 |
7 |

8 | Back Care 9 |

10 | 14 |
15 | 16 |
17 |
18 |

19 | Take pictures of good and bad 20 | posture{" "} 21 |

22 |

23 | App will learn the difference 24 | between them 25 |

26 |

27 | Notify you if you are in bad posture 28 |

29 |
30 |
31 |
32 | 38 |
39 |
40 | ); 41 | }; 42 | 43 | export default OnboardingBackCare; 44 | -------------------------------------------------------------------------------- /src/components/onboarding/OnboardingBackTrain.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | 4 | const OnboardingBackTrain = () => { 5 | return ( 6 |
7 |
8 |

9 | Back Care 10 |

11 | 15 |
16 | 17 |
18 |
19 |

20 | Lets teach our app what a 21 | good posture and a bad posture is{" "} 22 |

23 |
24 |
25 |
26 | 27 | 30 | 31 |
32 |
33 | ); 34 | }; 35 | 36 | export default OnboardingBackTrain; 37 | -------------------------------------------------------------------------------- /src/components/onboarding/OnboardingHow.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const OnboardingHow = ({ setCard }) => { 4 | return ( 5 |
6 |
7 |

8 | How? 9 |

10 | 14 |
15 | 16 |
17 |
18 |

19 | Detect your posture and 20 | notify 21 |

22 |

23 | Reminder for an exercise 24 |

25 |

26 | Analysis of your habits 27 |

28 |
29 |
30 |
31 | 37 |
38 |
39 | ); 40 | }; 41 | 42 | export default OnboardingHow; 43 | -------------------------------------------------------------------------------- /src/components/onboarding/OnboardingName.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | 3 | const OnboardingName = ({ setCard }) => { 4 | const [name, setName] = useState(""); 5 | const [error, setError] = useState(false); 6 | 7 | const inputHandler = (e) => { 8 | localStorage.setItem("name", e.target.value); 9 | setName(e.target.value); 10 | setError(false); 11 | }; 12 | 13 | const clickHandler = (e) => { 14 | e.preventDefault(); 15 | if (name.length > 1) { 16 | setCard("features"); 17 | } else { 18 | setError(true); 19 | } 20 | }; 21 | 22 | return ( 23 |
24 |
25 |

26 | Hello! 27 |

28 | 32 |
33 |
34 |
35 |

What can I call you?

36 | 37 | 44 | {error &&

Please enter your name

} 45 |
46 | 47 |
48 | 55 |
56 |
57 |
58 | ); 59 | }; 60 | 61 | export default OnboardingName; 62 | -------------------------------------------------------------------------------- /src/components/onboarding/OnboardingNotifs.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | 3 | const OnboardingNotifs = ({ setCard }) => { 4 | useEffect(() => { 5 | Notification.requestPermission(); 6 | }, []); 7 | 8 | return ( 9 |
10 |
11 |

12 | Reminder 13 |

14 | 18 |
19 | 20 |
21 |
22 |

23 | Allow Notifications to remind you 24 |

25 |
26 |
27 |
28 | 34 |
35 |
36 | ); 37 | }; 38 | 39 | export default OnboardingNotifs; 40 | -------------------------------------------------------------------------------- /src/components/onboarding/OnboardingSteps.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const OnboardingSteps = ({ setCard }) => { 4 | return ( 5 |
6 |
7 |

8 | Features 9 |

10 | 14 |
15 | 16 |
17 |

18 | We help you to take care of your:{" "} 19 |

20 |
21 |

22 | Back 23 |

24 |

25 | Eye 26 |

27 |

28 | Ear 29 |

30 |
31 |
32 |
33 | 39 |
40 |
41 | ); 42 | }; 43 | 44 | export default OnboardingSteps; 45 | -------------------------------------------------------------------------------- /src/constants/about.constant.js: -------------------------------------------------------------------------------- 1 | export default class AboutText { 2 | static PageHeadingText = "Dev care will help you to take care of your health as developers." 3 | static BackHealthText = "For back health, the app has the feature of posture detection. It detects your posture at regular intervals and reminds you to sit straight if you are not."; 4 | static EyeHealthText = "To relax your eyes there is a 20-20-20 rule which states that: For every 20 minutes spent looking at a screen, a person should look at something 20 feet away for 20 seconds This exercise relaxes our eyes and reduces eye strain. The app reminds you to follow this exercise at regular intervals." 5 | static EarHealthText = "For ear health, there is a 60-60 rule: Only turn your device volume up to 60% and listen for no more than 60 minutes per day The app checks if you are using earphones for longer than 60 minutes and reminds you to control your earphone usage if it is becoming unhealthy." 6 | } -------------------------------------------------------------------------------- /src/css/base/reset.css: -------------------------------------------------------------------------------- 1 | *, 2 | *::before, 3 | *::after { 4 | box-sizing: border-box; 5 | } 6 | 7 | body, 8 | h1, 9 | h2, 10 | h3, 11 | h4, 12 | h5, 13 | h5, 14 | p 15 | { 16 | margin: 0; 17 | } 18 | 19 | html { 20 | scroll-behavior: smooth; 21 | font-size: 100%; 22 | } 23 | 24 | body { 25 | min-height: 100vh; 26 | font-family: var(--ff-primary) 27 | } 28 | 29 | img, 30 | video, 31 | iframe { 32 | max-inline-size: 100%; 33 | block-size: auto; 34 | } 35 | 36 | input, 37 | button, 38 | textarea, 39 | select { 40 | font: inherit; 41 | } 42 | 43 | a { 44 | text-decoration: none; 45 | color: inherit 46 | } 47 | 48 | /* Remove all animations, for users who have turned them off */ 49 | @media (prefers-reduced-motion: reduce) { 50 | html:focus-within { 51 | scroll-behavior: auto; 52 | } 53 | 54 | *, 55 | *::before, 56 | *::after { 57 | animation-duration: 0.01ms !important; 58 | animation-iteration-count: 1 !important; 59 | transition-duration: 0.01ms !important; 60 | scroll-behavior: auto !important; 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /src/css/base/root.css: -------------------------------------------------------------------------------- 1 | :root { 2 | 3 | /* colors */ 4 | --clr-primary: #0099f6; 5 | --clr-primary-light: #68e1fd; 6 | --clr-primary-50: #eff6ff; 7 | --clr-primary-100: #dbeafe; 8 | --clr-primary-200: #bfdbfe; 9 | --clr-primary-300: #93c5fd; 10 | --clr-primary-400: #60a5fa; 11 | --clr-primary-500: #3b82f6; 12 | --clr-primary-600: #2563eb; 13 | --clr-primary-700: #1d4ed8; 14 | --clr-primary-800: #1e40af; 15 | --clr-primary-900: #1e3a8a; 16 | 17 | 18 | --clr-purple: #6d28d9; 19 | --clr-purple-dark: #5b21b6; 20 | --clr-purple-light: #ddd6fe; 21 | 22 | --clr-secondary: #6b7280; 23 | --clr-secondary-dark: #374151; 24 | --clr-secondary-light: #e5e7eb; 25 | 26 | --clr-green: #16a34a; 27 | --clr-green-dark:#15803d; 28 | --clr-green-light: #90ee8a; 29 | 30 | --clr-yellow: #eab308; 31 | --clr-yellow-dark: #a16207; 32 | --clr-yellow-light: #fef08a; 33 | 34 | --clr-red: #dc2626; 35 | --clr-red-dark: #b91c1c; 36 | --clr-red-light: #ffcdce; 37 | 38 | --clr-blue: #60a5fa; 39 | --clr-blue-dark: #1d4ed8; 40 | --clr-blue-light: #bfdbfe; 41 | 42 | --clr-gray-light:#f3f4f6; 43 | --clr-gray: gray; 44 | --clr-dark-gray: #374151; 45 | --clr-gray-opacity: rgb(243, 244, 246, 0.5); 46 | 47 | --clr-white: #ffffff; 48 | 49 | /* box shadow */ 50 | --bs-gray: 0px 3px 18px -5px #d1d5db; 51 | --bs-gray-dark: rgba(0, 0, 0, 0.1) 0px 10px 50px; 52 | 53 | /* font-sizes */ 54 | --fs-xxl: 3rem; 55 | --fs-xl: 2rem; 56 | --fs-lg: 1.5rem; 57 | --fs-md: 1.125rem; 58 | --fs-sm: 1rem; 59 | --fs-xs: 0.5rem; 60 | 61 | /* font-family */ 62 | --ff-primary: 'Poppins', sans-serif; 63 | --ff-serif: "Lora", serif; 64 | --ff-cursive: 'Great Vibes', cursive;; 65 | 66 | /* font weights */ 67 | --fw-normal: 400; 68 | --fw-semibold: 500; 69 | --fw-bold: 600; 70 | --fw-bolder: 700; 71 | 72 | /* line heights */ 73 | --lh-xxl: 4rem; 74 | --lh-xl: 2.5rem; 75 | --lh-lg: 2rem; 76 | --lh-md: 1.5rem; 77 | --lh-sm: 1rem; 78 | 79 | /* sizing */ 80 | --sz-xxl: 4rem; 81 | --sz-xl: 2.5rem; 82 | --sz-lg: 2rem; 83 | --sz-md: 1.5rem; 84 | --sz-sm: 1rem; 85 | --sz-xs: 0.5rem; 86 | 87 | } -------------------------------------------------------------------------------- /src/css/components/alert.css: -------------------------------------------------------------------------------- 1 | .alert-yellow { 2 | position: absolute; 3 | top: 3rem; 4 | right: 1rem; 5 | background-color: var(--clr-primary-300); 6 | z-index: 9999; 7 | padding: 0.5rem 0.5rem; 8 | max-width: 50ch; 9 | border-radius: 5px; 10 | } 11 | 12 | .alert-yellow i { 13 | margin: 0 1rem; 14 | cursor: pointer; 15 | font-size: 1.25rem; 16 | } -------------------------------------------------------------------------------- /src/css/components/modal.css: -------------------------------------------------------------------------------- 1 | .modal-backdrop { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | width: 100%; 6 | height: 100%; 7 | background: rgba(121, 127, 136, 0.4); 8 | z-index: 9999; 9 | } 10 | 11 | .modal { 12 | padding: 30px; 13 | max-width: 480px; 14 | margin: 200px auto; 15 | background: #fff; 16 | border-radius: 5px; 17 | box-shadow: var(--bs-gray-dark); 18 | } 19 | 20 | .modal-content .btn-primary { 21 | background-color: var(--clr-primary); 22 | border: none; 23 | outline: none; 24 | color: white; 25 | font-size: 1.1rem; 26 | padding: 0.75rem 5rem; 27 | border-radius: 5px; 28 | margin-top: 1rem; 29 | cursor: pointer; 30 | } 31 | 32 | .modal-content .desc { 33 | font-size: 1.25rem; 34 | margin: 1rem 0; 35 | } 36 | 37 | .modal-content .h1 { 38 | margin: 1rem 0; 39 | } -------------------------------------------------------------------------------- /src/css/components/nav.css: -------------------------------------------------------------------------------- 1 | .header { 2 | background-color: white; 3 | padding: 0.25rem 1rem; 4 | box-shadow: rgba(149, 157, 165, 0.2) 0px 1px 10px; 5 | position: sticky; 6 | top: 0; 7 | left: 0; 8 | right: 0; 9 | z-index: 9; 10 | } 11 | 12 | .header .header-info-wrapper { 13 | max-width: 1300px; 14 | margin: auto; 15 | display: flex; 16 | justify-content: space-between; 17 | align-items: center; 18 | } 19 | 20 | .header .logo { 21 | font-size: 2rem; 22 | display: flex; 23 | align-items: flex-end; 24 | margin: 0.75rem 0; 25 | } 26 | 27 | .header .logo-img { 28 | width: 1.75rem; 29 | height: 1.75rem; 30 | margin-inline: 1rem; 31 | } 32 | 33 | .header .logo-title { 34 | display: block; 35 | font-family: var(--ff-primary); 36 | /* font-style: italic; */ 37 | font-size: 1.5rem; 38 | line-height: 1; 39 | 40 | } 41 | 42 | .navigation { 43 | display: flex; 44 | justify-content: center; 45 | align-items: center; 46 | gap: 2rem; 47 | } 48 | 49 | .navigation li { 50 | padding: 0.5rem 0.2rem; 51 | cursor: pointer; 52 | } 53 | 54 | .navigation a { 55 | text-decoration: none; 56 | font-size: 1.5rem; 57 | color: var(--clr-secondary-dark); 58 | } 59 | 60 | .navigation .nav-link:hover, 61 | .navigation .nav-link:focus { 62 | color: var(--clr-primary); 63 | } 64 | -------------------------------------------------------------------------------- /src/css/components/sidebar.css: -------------------------------------------------------------------------------- 1 | .sidebar { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: flex-end; 5 | } 6 | 7 | .sidebar-links { 8 | list-style: none; 9 | margin: 2rem auto; 10 | } 11 | 12 | 13 | .sidebar-links li { 14 | margin: 1.5rem 0; 15 | padding: 0.75rem 2rem; 16 | border-radius: 2rem; 17 | font-size: 1.2rem; 18 | display: flex; 19 | align-items: center; 20 | gap: 1.5rem; 21 | cursor: pointer; 22 | } 23 | 24 | .sidebar-links i { 25 | display: block; 26 | font-size: 1.25rem; 27 | color: var(--clr-primary-500) 28 | } 29 | 30 | .sidebar-links span { 31 | display: block; 32 | } 33 | 34 | .sidebar-links li:hover { 35 | background-color: var(--clr-primary-50); 36 | font-weight: 500; 37 | } -------------------------------------------------------------------------------- /src/css/components/timerclock.css: -------------------------------------------------------------------------------- 1 | .timer-clock { 2 | font-size: 3rem; 3 | width: 30%; 4 | } 5 | -------------------------------------------------------------------------------- /src/css/components/toggle.css: -------------------------------------------------------------------------------- 1 | /* The switch - the box around the slider */ 2 | .switch { 3 | position: relative; 4 | display: inline-block; 5 | width: 60px; 6 | height: 34px; 7 | } 8 | 9 | /* Hide default HTML checkbox */ 10 | .switch input { 11 | opacity: 0; 12 | width: 0; 13 | height: 0; 14 | } 15 | 16 | /* The slider */ 17 | .slider { 18 | position: absolute; 19 | cursor: pointer; 20 | top: 0; 21 | left: 0; 22 | right: 0; 23 | bottom: 0; 24 | background-color: #ccc; 25 | -webkit-transition: 0.4s; 26 | transition: 0.4s; 27 | } 28 | 29 | .slider:before { 30 | position: absolute; 31 | content: ""; 32 | height: 26px; 33 | width: 26px; 34 | left: 4px; 35 | bottom: 4px; 36 | background-color: white; 37 | -webkit-transition: 0.4s; 38 | transition: 0.4s; 39 | } 40 | 41 | input[type="checkbox"]:checked + .slider { 42 | background-color: #2196f3; 43 | } 44 | 45 | input[type="checkbox"]:focus + .slider { 46 | box-shadow: 0 0 1px #2196f3; 47 | } 48 | 49 | input[type="checkbox"]:checked + .slider:before { 50 | -webkit-transform: translateX(26px); 51 | -ms-transform: translateX(26px); 52 | transform: translateX(26px); 53 | } 54 | 55 | /* Rounded sliders */ 56 | .slider.round { 57 | border-radius: 34px; 58 | } 59 | 60 | .slider.round:before { 61 | border-radius: 50%; 62 | } 63 | 64 | .options { 65 | display: flex; 66 | flex-direction: column; 67 | gap: 1rem; 68 | font-size: 1.25rem; 69 | } 70 | 71 | .option { 72 | display: flex; 73 | justify-content: space-evenly; 74 | } -------------------------------------------------------------------------------- /src/css/pages/care.css: -------------------------------------------------------------------------------- 1 | .main-graph-wrapper { 2 | flex-grow: 1; 3 | display: flex; 4 | height: 100vh; 5 | justify-content: space-between; 6 | padding: 1rem 1.5rem; 7 | } 8 | 9 | .main-graph-wrapper .main { 10 | flex-grow: 1; 11 | } 12 | 13 | .main-graph-wrapper .main h1 { 14 | margin-bottom: 2rem; 15 | } 16 | 17 | .main-graph-wrapper .main .graph { 18 | max-width: 500px; 19 | margin: auto; 20 | } 21 | 22 | .main-graph-wrapper .main .line-graph { 23 | max-width: 100%; 24 | margin: auto; 25 | min-height: 60vh; 26 | } 27 | 28 | .main-graph-wrapper .cards-container { 29 | width: 35%; 30 | } 31 | 32 | .cards-container .card { 33 | padding: 3rem 2rem; 34 | box-shadow: var(--bs-gray); 35 | display: flex; 36 | flex-direction: column; 37 | gap: 1rem; 38 | margin: 1rem; 39 | border: 1px solid var(--clr-secondary-light) 40 | } 41 | 42 | .card p { 43 | color: var(--clr-secondary-dark); 44 | line-height: 1.5; 45 | } 46 | 47 | .cards-container .card .btn-primary { 48 | border: none; 49 | color: white; 50 | background-color: var(--clr-primary-400); 51 | padding: 1rem 4rem; 52 | border-radius: 5px; 53 | cursor: pointer; 54 | } 55 | 56 | @media screen and (max-width: 1300px) { 57 | .main-graph-wrapper { 58 | flex-direction: column; 59 | } 60 | 61 | .main-graph-wrapper .main { 62 | width: 100%; 63 | } 64 | 65 | .main-graph-wrapper .cards-container { 66 | width: 100%; 67 | display: flex; 68 | } 69 | 70 | .cards-container .card { 71 | width: 45%; 72 | } 73 | 74 | } 75 | 76 | @media screen and (max-width: 800px) { 77 | .main-graph-wrapper .main .graph { 78 | max-width: 450px; 79 | margin: auto; 80 | } 81 | .main-graph-wrapper .cards-container { 82 | margin-top: 2rem; 83 | flex-direction: column; 84 | 85 | justify-content: center; 86 | align-items: center; 87 | } 88 | .cards-container .card { 89 | width: 70%; 90 | } 91 | 92 | .main h1 { 93 | display: block; 94 | font-weight: 500; 95 | } 96 | } 97 | 98 | @media screen and (max-width: 600px) { 99 | .main-graph-wrapper .main .graph { 100 | max-width: 300px; 101 | margin: auto; 102 | } 103 | 104 | .cards-container .card { 105 | width: 90%; 106 | } 107 | } 108 | 109 | @media screen and (max-width: 400px) { 110 | 111 | .main-graph-wrapper { 112 | padding: 0.25rem; 113 | } 114 | 115 | .main-graph-wrapper .main .graph { 116 | max-width: 70vw; 117 | margin: 0; 118 | } 119 | 120 | 121 | .main-graph-wrapper .main .line-graph { 122 | max-width: 100%; 123 | margin: auto; 124 | min-height: 50vh; 125 | } 126 | 127 | 128 | } -------------------------------------------------------------------------------- /src/css/pages/dashboard.css: -------------------------------------------------------------------------------- 1 | .main-wrapper { 2 | display: flex; 3 | min-height: 85vh; 4 | margin-top: 2rem; 5 | } 6 | 7 | .sidebar { 8 | max-width: 20%; 9 | border-right: 1px solid var(--clr-secondary-light); 10 | margin-top: 2rem; 11 | } 12 | 13 | .main { 14 | width: 60%; 15 | } 16 | 17 | .cartoon { 18 | flex-grow: 1; 19 | border-left: 1px solid var(--clr-secondary-light); 20 | } 21 | 22 | .main { 23 | padding: 1rem 3rem; 24 | } 25 | 26 | .main-content { 27 | display: flex; 28 | flex-direction: column; 29 | justify-content: center; 30 | align-items: center; 31 | height: 55vh; 32 | border: 1px solid #e5e7eb; 33 | width: 100%; 34 | margin-top: 3rem; 35 | gap: 2rem; 36 | } 37 | 38 | .timer { 39 | width: 100%; 40 | display: flex; 41 | justify-content: space-evenly; 42 | align-items: center; 43 | } 44 | 45 | .options { 46 | width: 30%; 47 | } 48 | 49 | .option { 50 | display: flex; 51 | justify-content: space-between; 52 | } 53 | 54 | .main-content .btn-primary { 55 | border: none; 56 | background-color: #2196f3; 57 | padding: 1rem 5rem; 58 | color: white; 59 | font-size: 1.2rem; 60 | border-radius: 5px; 61 | margin: 1rem 1rem 0 1rem; 62 | cursor: pointer; 63 | } 64 | 65 | .main-content .btn-secondary { 66 | border: none; 67 | background-color: #DBEAFE; 68 | padding: 1rem 5rem; 69 | color: black; 70 | font-size: 1.2rem; 71 | border-radius: 5px; 72 | margin: 1rem 1rem 0 1rem; 73 | cursor: pointer; 74 | } 75 | 76 | .main-content .btn-secondary:hover { 77 | background-color: var(--clr-primary-500); 78 | } 79 | 80 | .cta { 81 | margin: 1rem; 82 | width: 80%; 83 | display: flex; 84 | align-items: center; 85 | justify-content: center; 86 | } 87 | 88 | .btn-primary:hover { 89 | background-color: var(--clr-primary-600); 90 | } 91 | 92 | .btn-primary:disabled { 93 | cursor: no-drop; 94 | opacity: 0.5; 95 | } 96 | 97 | @media screen and (max-width: 1300px) { 98 | .sidebar-links i { 99 | font-size: 1.5rem; 100 | } 101 | .sidebar-links span { 102 | display: none; 103 | } 104 | 105 | .main { 106 | width: 75%; 107 | } 108 | } 109 | 110 | @media screen and (max-width: 1100px) { 111 | .cartoon { 112 | display: none; 113 | } 114 | 115 | .sidebar-links { 116 | margin: 1rem; 117 | } 118 | 119 | .sidebar-links li { 120 | padding: 1rem; 121 | } 122 | } 123 | 124 | @media screen and (max-width: 800px) { 125 | .main-wrapper, 126 | .sidebar { 127 | margin-top: 1rem; 128 | } 129 | 130 | .sidebar-links { 131 | padding: 0.5rem; 132 | } 133 | 134 | .sidebar-links li { 135 | padding: 1rem 0; 136 | } 137 | 138 | .sidebar-links i { 139 | font-size: 1.25rem; 140 | } 141 | 142 | h1 { 143 | font-size: 1.5rem; 144 | font-weight: 500; 145 | 146 | /* display: none; */ 147 | } 148 | 149 | .main { 150 | width: 100%; 151 | padding: 1rem; 152 | } 153 | 154 | .main-content { 155 | margin-top: 1.5rem; 156 | /* border: none; */ 157 | padding: 1rem; 158 | gap: 0.5rem; 159 | height: auto; 160 | } 161 | 162 | .timer { 163 | flex-direction: column; 164 | } 165 | 166 | .timer-clock 167 | { 168 | width: 100%; 169 | } 170 | 171 | .timer-clock { 172 | text-align: center; 173 | align-items: center; 174 | } 175 | 176 | .options { 177 | margin: 3rem; 178 | width: 80%; 179 | } 180 | 181 | .cta { 182 | margin: 0; 183 | } 184 | } 185 | 186 | @media screen and (max-width: 500px) { 187 | 188 | .main-content { 189 | margin-top: 1.5rem; 190 | border: none; 191 | } 192 | 193 | /* h1 { 194 | display: none; 195 | } */ 196 | 197 | .options { 198 | width: 100%; 199 | } 200 | 201 | } 202 | -------------------------------------------------------------------------------- /src/css/pages/onboarding.css: -------------------------------------------------------------------------------- 1 | .onboarding-name { 2 | margin: 0 1rem; 3 | } 4 | 5 | .onboarding-name input{ 6 | width: 100%; 7 | border-radius: 4px; 8 | padding: 0.5rem; 9 | outline: 0; 10 | border: 1px solid var(--clr-secondary-light); 11 | margin: 0.25rem 0; 12 | } 13 | 14 | .onboarding-name input:focus { 15 | border: 2px solid var(--clr-primary); 16 | } 17 | 18 | .onboarding-name button { 19 | border:none; 20 | outline: none; 21 | background-color: var(--clr-primary); 22 | margin-top: 1.5rem; 23 | padding: 0.5rem 3rem; 24 | border-radius: 3px; 25 | color: white; 26 | cursor: pointer; 27 | } 28 | 29 | .features p { 30 | margin: 1rem 2rem 0.5rem 0; 31 | font-size: 1.25rem; 32 | } 33 | 34 | .features i { 35 | margin-right: 0.5rem; 36 | color: var(--clr-primary); 37 | } 38 | 39 | .steps i { 40 | margin-right: 0.75rem; 41 | color: var(--clr-primary-500); 42 | font-size: 1.25rem; 43 | } 44 | 45 | .steps p { 46 | margin: 0.5rem 0; 47 | } 48 | 49 | .notifs i { 50 | margin-right: 1rem; 51 | color: var(--clr-primary-500); 52 | font-size: 1.5rem; 53 | } 54 | 55 | .notifs p { 56 | margin: 0.5rem 0; 57 | font-size: 1.1rem; 58 | } 59 | -------------------------------------------------------------------------------- /src/css/pages/trainback.css: -------------------------------------------------------------------------------- 1 | .train-back-btns { 2 | flex-grow: 1; 3 | } 4 | 5 | .train-back-btns button { 6 | /* background-color: var(--clr-primary); */ 7 | border: none; 8 | outline: none; 9 | margin: 1rem; 10 | border-radius: 3px; 11 | color: white; 12 | padding: 0.75rem; 13 | cursor: pointer; 14 | position: relative; 15 | width: 100%; 16 | max-width: 450px; 17 | } 18 | 19 | .train-back-btns button:disabled { 20 | cursor: no-drop; 21 | opacity: 0.3; 22 | } 23 | 24 | .train-back-btns button span { 25 | position: absolute; 26 | right: 1rem; 27 | } 28 | 29 | 30 | 31 | @media screen and (max-width: 1000px) { 32 | .train-wrapper { 33 | flex-direction: column; 34 | } 35 | 36 | .train-back-btns { 37 | width: 70%; 38 | margin: auto; 39 | 40 | } 41 | 42 | .train-back-btns button { 43 | margin: auto; 44 | width: 80%; 45 | margin: 0.5rem; 46 | } 47 | } -------------------------------------------------------------------------------- /src/css/style.css: -------------------------------------------------------------------------------- 1 | @import 'https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'; 2 | @import 'https://fonts.googleapis.com/css2?family=Great+Vibes&family=Vidaloka&display=swap'; 3 | @import 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css'; 4 | 5 | 6 | @import './base/root.css'; 7 | @import './base/reset.css'; 8 | 9 | @import './utilities/spacing.css'; 10 | @import './utilities/flex.css'; 11 | @import './utilities/typography.css'; 12 | @import './utilities/container.css'; 13 | @import './utilities/colors.css'; 14 | @import './utilities/spacing.css'; 15 | 16 | @import './components/nav.css'; 17 | @import './components/toggle.css'; 18 | @import './components/sidebar.css'; 19 | @import './components/timerclock.css'; 20 | @import './components/modal.css'; 21 | @import './components/alert.css'; 22 | 23 | @import './pages/dashboard.css'; 24 | @import './pages/care.css'; 25 | @import './pages/trainback.css'; 26 | @import './pages/onboarding.css'; -------------------------------------------------------------------------------- /src/css/utilities/colors.css: -------------------------------------------------------------------------------- 1 | .txt-primary { 2 | color: var(--clr-primary) 3 | } 4 | 5 | .txt-secondary { 6 | color: var(--clr-secondary) 7 | } 8 | 9 | .txt-green { 10 | color: var(--clr-green) 11 | } 12 | 13 | .txt-yellow { 14 | color: var(--clr-yellow) 15 | } 16 | 17 | .txt-red { 18 | color: var(--clr-red) 19 | } 20 | 21 | .txt-blue { 22 | color: var(--clr-blue) 23 | } 24 | 25 | .txt-gray { 26 | color: var(--clr-gray); 27 | } 28 | 29 | .txt-dark-gray { 30 | color: var(--dark-gray); 31 | } 32 | 33 | .bg-primary { 34 | background-color: var(--clr-primary); 35 | } 36 | 37 | .bg-secondary { 38 | background-color: var(--clr-secondary); 39 | } 40 | 41 | .bg-green { 42 | background-color: var(--clr-green); 43 | } 44 | 45 | .bg-yellow { 46 | background-color: var(--clr-yellow); 47 | } 48 | 49 | .bg-red { 50 | background-color: var(--clr-red); 51 | } 52 | 53 | .bg-blue { 54 | background-color: var(--clr-blue); 55 | } 56 | 57 | .bs-dark-gray { 58 | box-shadow: var(--bs-gray-dark); 59 | } -------------------------------------------------------------------------------- /src/css/utilities/container.css: -------------------------------------------------------------------------------- 1 | .container-center-xl { 2 | max-width: 1200px; 3 | margin: auto; 4 | } 5 | 6 | .container-center-lg { 7 | max-width: 1000px; 8 | margin: auto; 9 | } 10 | 11 | .container-center-md { 12 | max-width: 800px; 13 | margin: auto; 14 | } 15 | 16 | .container-center-xs { 17 | max-width: 650px; 18 | margin: auto; 19 | } 20 | 21 | .container-center-sm { 22 | max-width: 450px; 23 | margin: auto; 24 | } -------------------------------------------------------------------------------- /src/css/utilities/display.css: -------------------------------------------------------------------------------- 1 | .d-none { 2 | display: none; 3 | } 4 | 5 | .d-inline { 6 | display: inline; 7 | } 8 | 9 | .d-block { 10 | display: block; 11 | } 12 | 13 | .d-inline-block { 14 | display: inline-block; 15 | } -------------------------------------------------------------------------------- /src/css/utilities/flex.css: -------------------------------------------------------------------------------- 1 | .flex { 2 | display: flex; 3 | } 4 | 5 | .flex-col { 6 | flex-direction: column; 7 | } 8 | 9 | .flex-row { 10 | flex-direction: row; 11 | } 12 | 13 | .flex-justify-between { 14 | justify-content: space-between; 15 | } 16 | 17 | .flex-justify-center { 18 | justify-content: center; 19 | } 20 | 21 | .flex-items-center { 22 | align-items: center; 23 | } 24 | 25 | .flex-items-end { 26 | align-items: flex-end; 27 | } 28 | 29 | .flex-items-start { 30 | align-items: start; 31 | } 32 | 33 | .flex-wrap { 34 | flex-wrap: wrap; 35 | } 36 | 37 | .flex-gap-xl { 38 | gap: 3rem; 39 | } 40 | 41 | .flex-gap-lg { 42 | gap: 2rem; 43 | } 44 | 45 | .flex-gap-md { 46 | gap: 1rem; 47 | } 48 | 49 | .flex-gap-sm { 50 | gap: 0.5rem; 51 | } 52 | 53 | .flex-gap-xs { 54 | gap: 0.25rem; 55 | } -------------------------------------------------------------------------------- /src/css/utilities/grid.css: -------------------------------------------------------------------------------- 1 | .grid { 2 | display: grid; 3 | gap: 1rem; 4 | } 5 | 6 | .grid-col-2 { 7 | grid-template-columns: 1fr 1fr; 8 | } 9 | 10 | .grid-col-3 { 11 | grid-template-columns: 1fr 1fr 1fr; 12 | } 13 | 14 | .grid-col-4 { 15 | grid-template-columns: 1fr 1fr 1fr 1fr; 16 | } -------------------------------------------------------------------------------- /src/css/utilities/spacing.css: -------------------------------------------------------------------------------- 1 | .m-xl { 2 | margin: 2rem; 3 | } 4 | 5 | .m-lg { 6 | margin: 1.5rem; 7 | } 8 | 9 | .m-md { 10 | margin: 1rem; 11 | } 12 | 13 | .m-sm { 14 | margin: 0.25rem; 15 | } 16 | 17 | .mx-xl { 18 | margin-inline: 2rem; 19 | } 20 | 21 | .mx-lg { 22 | margin-inline: 1.5rem; 23 | } 24 | 25 | .mx-md { 26 | margin-inline: 1rem; 27 | } 28 | 29 | .mx-sm { 30 | margin-inline: 0.25rem; 31 | } 32 | 33 | .mt-xl { 34 | margin-top: 2rem; 35 | } 36 | 37 | .mt-lg { 38 | margin-top: 1.5rem; 39 | } 40 | 41 | .mt-md { 42 | margin-top: 1rem; 43 | } 44 | 45 | .mt-sm { 46 | margin-top: 0.25rem; 47 | } 48 | 49 | .mb-xl { 50 | margin-bottom: 2rem; 51 | } 52 | 53 | .mb-lg { 54 | margin-bottom: 1.5rem; 55 | } 56 | 57 | .mb-md { 58 | margin-bottom: 1rem; 59 | } 60 | 61 | .mb-sm { 62 | margin-bottom: 0.25rem; 63 | } 64 | -------------------------------------------------------------------------------- /src/css/utilities/typography.css: -------------------------------------------------------------------------------- 1 | /* 2 | Font Sizes 3 | */ 4 | 5 | .fs-xxl { 6 | font-size: var(--fs-xxl); 7 | } 8 | 9 | .fs-xl { 10 | font-size: var(--fs-xl); 11 | } 12 | 13 | .fs-lg { 14 | font-size: var(--fs-lg); 15 | } 16 | 17 | .fs-md { 18 | font-size: var(--fs-md); 19 | } 20 | 21 | .fs-sm { 22 | font-size: var(--fs-sm); 23 | } 24 | 25 | .fs-xs { 26 | font-size: var(--fs-xs); 27 | } 28 | 29 | /* 30 | Font weights 31 | */ 32 | 33 | .fw-normal { 34 | font-weight: var(--fw-normal); 35 | } 36 | 37 | .fw-semibold { 38 | font-weight: var(--fw-semibold); 39 | } 40 | 41 | .fw-bold { 42 | font-weight: var(--fw-bold); 43 | } 44 | 45 | .fw-bolder { 46 | font-weight: var(--fw-bolder); 47 | } 48 | 49 | /* 50 | Text decoration 51 | */ 52 | 53 | .txt-underline { 54 | text-decoration: underline; 55 | } 56 | 57 | .txt-strike-through { 58 | text-decoration: line-through; 59 | } 60 | 61 | .txt-decoration-none { 62 | text-decoration: none; 63 | } 64 | 65 | /* 66 | Text Transform 67 | */ 68 | 69 | .txt-uppercase { 70 | text-transform: uppercase; 71 | } 72 | 73 | .txt-lowercase { 74 | text-transform: lowercase; 75 | } 76 | 77 | .txt-capitalize { 78 | text-transform: capitalize; 79 | } 80 | 81 | /* 82 | Text Alignment 83 | */ 84 | 85 | .txt-start { 86 | text-align: left; 87 | } 88 | 89 | .txt-center { 90 | text-align: center; 91 | } 92 | 93 | .txt-end { 94 | text-align: right; 95 | } 96 | 97 | /* 98 | Headings 99 | */ 100 | 101 | .heading-h1 { 102 | font-size: calc(var(--fs-xxl) + 0.5vw); 103 | line-height: var(--lh-xxl); 104 | } 105 | 106 | .heading-h2 { 107 | font-size: calc(var(--fs-xl) + 0.5vw); 108 | line-height: var(--lh-xl); 109 | } 110 | 111 | .heading-h3 { 112 | font-size: calc(var(--fs-lg) + 0.5vw); 113 | line-height: var(--lh-lg); 114 | } 115 | 116 | .heading-h4 { 117 | font-size: calc(var(--fs-md) + 0.5vw); 118 | line-height: var(--lh-md); 119 | } 120 | 121 | /* 122 | Line heights 123 | */ 124 | 125 | .lh-md { 126 | line-height: var(--lh-md); 127 | } -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | /* body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } */ 14 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ); 12 | -------------------------------------------------------------------------------- /src/screens/Aboutus.js: -------------------------------------------------------------------------------- 1 | import Nav from "../components/core/Nav"; 2 | import Sidebar from "../components/core/Sidebar"; 3 | import "../css/style.css"; 4 | import AboutText from "../constants/about.constant"; 5 | 6 | function AboutUs() { 7 | return ( 8 | <> 9 | 10 |
11 | 12 |
13 |
14 |

About Us

15 |
16 |

17 | {AboutText.PageHeadingText} 18 |

19 | 20 |

Back health

21 |

22 | {AboutText.BackHealthText} 23 |

24 | 25 |

Eye health

26 |

27 | {AboutText.EyeHealthText} 28 |

29 | 30 |

Ear health

31 |

32 | {AboutText.EarHealthText} 33 |

34 |
35 |
36 |
37 |
38 | 39 | ); 40 | } 41 | 42 | export default AboutUs; 43 | -------------------------------------------------------------------------------- /src/screens/BackCare.js: -------------------------------------------------------------------------------- 1 | import Nav from "../components/core/Nav"; 2 | import Sidebar from "../components/core/Sidebar"; 3 | import PieChart from "../components/charts/PieChart"; 4 | import "../css/style.css"; 5 | import { Link } from "react-router-dom"; 6 | import { useEffect, useState } from "react"; 7 | import Modal from "../components/core/Modal"; 8 | 9 | function BackCare() { 10 | const [showModal, setShowModal] = useState(); 11 | 12 | return ( 13 | <> 14 | 15 |
16 | 17 |
18 |
19 |

Back Care

20 |
21 | 22 |
23 |
24 |
25 |
26 |

How it works?

27 |

Find out how this feature works

28 | 34 |
35 |
36 |

Take new pics

37 |

38 | Will help the app to determine whether you are sitting in 39 | correct posture or not 40 |

41 | 42 | {" "} 45 | 46 |
47 |
48 |
49 |
50 | {showModal && ( 51 | 52 |
53 |
57 | setShowModal(false)} 67 | > 68 |

69 | Back Care 70 |

71 | 75 |
76 | 77 |
78 |
79 |

80 | Take pictures of good and 81 | bad posture{" "} 82 |

83 |

84 | App will learn the 85 | difference between them 86 |

87 |

88 | Notify you if you are in bad 89 | posture 90 |

91 |
92 |
93 |
94 | 100 |
101 |
102 |
103 | )} 104 | 105 | ); 106 | } 107 | 108 | export default BackCare; 109 | -------------------------------------------------------------------------------- /src/screens/Dashboard.js: -------------------------------------------------------------------------------- 1 | import Nav from "../components/core/Nav"; 2 | import Sidebar from "../components/core/Sidebar"; 3 | import "../css/style.css"; 4 | import Score from "../components/core/Score"; 5 | import TimerClock from "../components/core/TimerClock"; 6 | import Toggles from "../components/core/Toggles"; 7 | import Countdown, { zeroPad } from "react-countdown"; 8 | import { useEffect, useRef, useState } from "react"; 9 | import { 10 | notify, 11 | notifyEar, 12 | notifySitStraight, 13 | } from "../services/notifications"; 14 | import { setEarData } from "../services/datahandling"; 15 | import Webcam from "react-webcam"; 16 | import * as mobilenetModule from "@tensorflow-models/mobilenet"; 17 | import * as knnClassifier from "@tensorflow-models/knn-classifier"; 18 | import * as tf from "@tensorflow/tfjs"; 19 | import { model } from "@tensorflow/tfjs"; 20 | import Onboarding from "../components/onboarding/Onboarding"; 21 | 22 | const renderer = ({ hours, minutes, seconds, completed }) => { 23 | if (completed) { 24 | return ( 25 | 29 | ); 30 | } else { 31 | return ( 32 | 36 | ); 37 | } 38 | }; 39 | 40 | const fromDatasetObject = (datasetObject) => { 41 | return Object.entries(datasetObject).reduce( 42 | (result, [indexString, { data, shape }]) => { 43 | const tensor = tf.tensor2d(data, shape); 44 | const index = Number(indexString); 45 | 46 | result[index] = tensor; 47 | 48 | return result; 49 | }, 50 | {} 51 | ); 52 | }; 53 | 54 | let i = 0; 55 | let timeCounter = 1; 56 | 57 | const classifier = knnClassifier.create(); 58 | 59 | function Dashboard() { 60 | let visitedHome = localStorage.getItem("visited"); 61 | let name = localStorage.getItem("name"); 62 | 63 | const [time, setTime] = useState(20000); 64 | const [isCamOn, setIsCamOn] = useState(false); 65 | 66 | const [isStarted, setIsStarted] = useState(false); 67 | 68 | const [backOption, setBackOption] = useState(true); 69 | const [eyeOption, setEyeOption] = useState(true); 70 | const [soundOption, setSoundOption] = useState(false); 71 | const [isPluggedOut, setIsPluggedOut] = useState(); 72 | const [earDeviceId, setEarDeviceId] = useState(""); 73 | const [isDisabled, setIsDisabled] = useState(false); 74 | 75 | const webcamRef = useRef(null); 76 | const timerRef = useRef(null); 77 | 78 | const startHandler = () => { 79 | let timerAPI = timerRef.current.getApi(); 80 | timerAPI.start(); 81 | setIsStarted(true); 82 | }; 83 | 84 | const stopHandler = () => { 85 | let timerAPI = timerRef.current.getApi(); 86 | timerAPI.stop(); 87 | setIsStarted(false); 88 | }; 89 | 90 | const onComplete = () => { 91 | if (backOption) { 92 | setIsCamOn(true); 93 | } 94 | if (eyeOption) { 95 | notify(window.location.href); 96 | } 97 | if (soundOption && timeCounter % 2 === 0 && !isPluggedOut) { 98 | notifyEar(window.location.href); 99 | setEarData(); 100 | } 101 | timeCounter++; 102 | restart(); 103 | }; 104 | 105 | useEffect(() => { 106 | if (isCamOn === true) classifyPic(); 107 | }, [isCamOn]); 108 | 109 | const restart = () => { 110 | if (i === 0) { 111 | setTime(time + 1); 112 | i = 1; 113 | } else { 114 | setTime(time - 1); 115 | i = 0; 116 | } 117 | startHandler(); 118 | }; 119 | 120 | useEffect(() => { 121 | stopHandler(); 122 | let eyedata = localStorage.getItem("eyedata"); 123 | if (!eyedata) { 124 | localStorage.setItem("eyedata", JSON.stringify([0, 0, 0, 0, 0, 0, 0])); 125 | } 126 | let eardata = localStorage.getItem("eardata"); 127 | if (!eardata) { 128 | localStorage.setItem("eardata", JSON.stringify([0, 0, 0, 0, 0, 0, 0])); 129 | } 130 | navigator.serviceWorker.register("sw.js"); 131 | let str = localStorage.getItem("myData"); 132 | if (str) { 133 | classifier.setClassifierDataset( 134 | fromDatasetObject(JSON.parse(localStorage.getItem("myData"))) 135 | ); 136 | } 137 | }, []); 138 | 139 | useEffect(() => { 140 | console.log(eyeOption, backOption, soundOption) 141 | if(eyeOption || backOption || soundOption) { 142 | setIsDisabled(false) 143 | } 144 | else { 145 | setIsDisabled(true); 146 | } 147 | }, [eyeOption, backOption, soundOption]); 148 | 149 | const classifyPic = async () => { 150 | if (backOption) { 151 | let net = await mobilenetModule.load(); 152 | const img = webcamRef.current.video; 153 | const activation = net.infer(img, true); 154 | const result = await classifier.predictClass(activation); 155 | 156 | setIsCamOn(false); 157 | if (result.label === "1") { 158 | notifySitStraight(); 159 | let badposturecount = Number(localStorage.getItem("badposturecount")); 160 | if (!badposturecount) { 161 | badposturecount = 0; 162 | } 163 | localStorage.setItem("badposturecount", badposturecount + 1); 164 | } else { 165 | let goodposturecount = Number(localStorage.getItem("goodposturecount")); 166 | if (!goodposturecount) { 167 | goodposturecount = 0; 168 | } 169 | localStorage.setItem("goodposturecount", goodposturecount + 1); 170 | } 171 | } 172 | }; 173 | 174 | // Ear Care 175 | const getLocalStream = () => { 176 | navigator.mediaDevices 177 | .getUserMedia({ video: false, audio: true }) 178 | .then((stream) => { 179 | setEarDeviceId(stream.getAudioTracks()[0].getSettings().deviceId); 180 | 181 | if (earDeviceId == "default") { 182 | setEarDeviceId(stream.getAudioTracks()[0].getSettings().groupId); 183 | } 184 | 185 | navigator.mediaDevices.enumerateDevices().then(() => { 186 | let recorder = new MediaRecorder(stream); 187 | recorder.stream.getAudioTracks().forEach(function (track) { 188 | track.enabled = false; 189 | }); 190 | }); 191 | }) 192 | .catch((err) => { 193 | setSoundOption(false); 194 | }); 195 | }; 196 | 197 | useEffect(() => { 198 | if (soundOption) { 199 | setIsPluggedOut(false); 200 | getLocalStream(); 201 | } 202 | }, [soundOption]); 203 | 204 | navigator.mediaDevices.addEventListener("devicechange", () => { 205 | navigator.mediaDevices.enumerateDevices().then((devices) => { 206 | var pluggedout = true; 207 | for (let i = 0; i < devices.length; i++) { 208 | if ( 209 | ((devices[i].deviceId == earDeviceId) != devices[i].groupId) == 210 | earDeviceId 211 | ) { 212 | pluggedout = false; 213 | } 214 | } 215 | if (pluggedout) { 216 | setIsPluggedOut(true); 217 | } 218 | }); 219 | }); 220 | 221 | return ( 222 | <> 223 | 224 |
225 | 226 |
227 |

Hello {name && {name}}👋

228 | 229 |
230 |
231 | 238 | 246 |
247 |
248 |
249 | 252 | {isStarted && ( 253 | 256 | )} 257 |
258 |
259 |
260 |
261 | 262 | 263 |
264 | {isCamOn && ( 265 | 278 | )} 279 | {!visitedHome && } 280 | 281 | ); 282 | } 283 | 284 | export default Dashboard; 285 | -------------------------------------------------------------------------------- /src/screens/EarCare.js: -------------------------------------------------------------------------------- 1 | import Nav from "../components/core/Nav"; 2 | import Sidebar from "../components/core/Sidebar"; 3 | import "../css/style.css"; 4 | import { BarChart } from "../components/charts/BarChart"; 5 | import { useState } from "react"; 6 | import Modal from "../components/core/Modal"; 7 | 8 | function EarCare() { 9 | const [showHowModal, setshowHowModal] = useState(false); 10 | const [showexerciseModal, setExerciseModal] = useState(false); 11 | 12 | return ( 13 | <> 14 | 15 |
16 | 17 |
18 |
19 |

Ear Care

20 |
21 | 22 |
23 |
24 |
25 |
26 |

How it works?

27 |

Find out how this feature works

28 | 34 |
35 |
36 |

Ear Exercises

37 |

38 | Get to know how to take care of your ears. 39 |

40 | 46 |
47 |
48 |
49 |
50 | 51 | {showHowModal && ( 52 | 53 |
54 |
58 | setshowHowModal(false)} 68 | > 69 |

70 | Ear Care 71 |

72 | cartoon character 77 |
78 | 79 |
80 |
81 |

82 | {" "} 83 | It is recommended to use 84 | earphones max for 60 minutes per day.{" "} 85 |

86 |

87 | We will remind you when u 88 | reach the time limit. 89 |

90 |

91 | We will visualize your 92 | earphone usage time through chart 93 |

94 | {/*

This will help your eyes to relax.

*/} 95 |
96 |
97 |
98 | 104 |
105 |
106 |
107 | )} 108 | 109 | {showexerciseModal && ( 110 | 111 |
112 |
116 | setExerciseModal(false)} 126 | > 127 |

128 | Ear Care 129 |

130 | cartoon character 135 |
136 | 137 |
138 |
139 |

Follow 60-60 rule.

140 |

141 | Use earphones maximum for 60 142 | minutes per day. 143 |

144 |

145 | Keep the volume 60% 146 | maximum. 147 |

148 | {/*

This will help your eyes to relax.

*/} 149 |
150 |
151 |
152 | 158 |
159 |
160 |
161 | )} 162 | 163 | ); 164 | } 165 | 166 | export default EarCare; 167 | -------------------------------------------------------------------------------- /src/screens/Exercise.js: -------------------------------------------------------------------------------- 1 | import Modal from "../components/core/Modal"; 2 | import { useEffect, useState } from "react"; 3 | 4 | const ExerciseCard = ({ setIsDone }) => { 5 | return ( 6 |
7 |

Take care👋

8 |
9 | exercise 16 |
17 |
Look 20 feet away for 20 seconds.
18 |
19 | 25 |
26 |
27 | ); 28 | }; 29 | 30 | const DoneExerciseCard = () => { 31 | return ( 32 |
33 |

Hurray!

34 |
35 | exercise 42 |
43 |
44 | Yayy! You have done the exercise. You can close this tab now. 45 |
46 |
47 | 48 |
49 |
50 | ); 51 | }; 52 | 53 | const Exercise = () => { 54 | const [isDone, setIsDone] = useState(false); 55 | 56 | useEffect(() => { 57 | let eyedata = JSON.parse(localStorage.getItem("eyedata")); 58 | let todayDate = new Date(); 59 | let today = todayDate.getDay(); 60 | eyedata[today] = Number(eyedata[today]) + 1; 61 | localStorage.setItem("eyedata", JSON.stringify(eyedata)); 62 | }, []); 63 | 64 | return ( 65 |
66 | 67 | {isDone ? ( 68 | 69 | ) : ( 70 | 71 | )} 72 | 73 |
74 | ); 75 | }; 76 | 77 | export default Exercise; 78 | -------------------------------------------------------------------------------- /src/screens/EyeCare.js: -------------------------------------------------------------------------------- 1 | import Nav from "../components/core/Nav"; 2 | import Sidebar from "../components/core/Sidebar"; 3 | import "../css/style.css"; 4 | import { LineChart } from "../components/charts/LineChart"; 5 | import { useState } from "react"; 6 | import Modal from "../components/core/Modal"; 7 | 8 | function EyeCare() { 9 | const [showHowModal, setshowHowModal] = useState(false); 10 | const [showexerciseModal, setExerciseModal] = useState(false); 11 | 12 | return ( 13 | <> 14 | 15 |
16 | 17 |
18 |
19 |

Eye Care

20 |
21 | 22 |
23 |
24 |
25 |
26 |

How it works?

27 |

Find out how this feature works

28 | 34 |
35 |
36 |

Eye Exercises

37 |

38 | Get to know some eye exercises to relax your eyes. 39 |

40 | 46 |
47 |
48 |
49 |
50 | 51 | {showHowModal && ( 52 | 53 |
54 |
58 | setshowHowModal(false)} 68 | > 69 |

70 | Eye Care 71 |

72 | cartoon character 77 |
78 | 79 |
80 |
81 |

82 | {" "} 83 | We will help you to relax your 84 | eyes.{" "} 85 |

86 |

87 | We will remind you every 20 88 | minutes 89 |

90 |

91 | Click on the notification and 92 | do the given exercise 93 |

94 |
95 |
96 |
97 | 103 |
104 |
105 |
106 | )} 107 | 108 | {showexerciseModal && ( 109 | 110 |
111 |
115 | setExerciseModal(false)} 125 | > 126 |

127 | Eye Care 128 |

129 |
130 | eye exercise 135 |
136 |
137 |

138 | {" "} 139 | Every 20 minutes you spend looking at the screen look 20 feet 140 | away for 20 seconds. 141 |

142 |
143 |
144 |
145 | 151 |
152 |
153 |
154 | )} 155 | 156 | ); 157 | } 158 | 159 | export default EyeCare; 160 | -------------------------------------------------------------------------------- /src/screens/HowtoUse.js: -------------------------------------------------------------------------------- 1 | import Nav from "../components/core/Nav"; 2 | import Sidebar from "../components/core/Sidebar"; 3 | import { Link } from "react-router-dom"; 4 | import "../css/style.css"; 5 | 6 | function HowtoUse() { 7 | return ( 8 | <> 9 | 10 |
11 | 12 |
13 |
14 |

How to use?

15 |
16 |

17 | Dev care will help you to take care of your health as 18 | developers. 19 |

20 | 21 |

Back care

22 |
23 |
    24 |
  1. 25 | Teach the app what a good and bad posture is{" "} 26 | 35 | Here 36 | 37 |
  2. 38 |
  3. Click atleast 5 pictures in good and bad posture is
  4. 39 |
  5. 40 | Test the posture detection, click more pics for more 41 | accurate results 42 |
  6. 43 |
  7. Come back to home and start the timer
  8. 44 |
  9. 45 | Every 20 minutes the app will check your posture and will 46 | remind you if it is incorrect 47 |
  10. 48 |
49 |
50 | 51 |

Eye care

52 |

53 |

    54 |
  1. Start the timer
  2. 55 |
  3. 56 | Every 20 minutes the app will remind you to do an exercise 57 |
  4. 58 |
  5. Click on the notification
  6. 59 |
  7. 60 | The app will navigate you to an exercise page. Click on Done 61 | when u r done doing the exercise 62 |
  8. 63 |
  9. 64 | The exercise is to look 20 feet away for 20 seconds every 20 65 | minutes to relax our eyes. 66 |
  10. 67 |
68 |

69 | 70 |

Ear care

71 |

72 |

    73 |
  1. Click on the toggle button of Ear care on dashboard
  2. 74 |
  3. Give earphone permission
  4. 75 |
  5. 76 | Now after 40 minutes the app will remind you that you are 77 | reaching the daily limit of earphone usage time 78 |
  6. 79 |
  7. 80 | It is advisable to use earphones for maximum 60 minutes a 81 | day at a maximum of 60% volume 82 |
  8. 83 |
84 |

85 |
86 |
87 |
88 |
89 | 90 | ); 91 | } 92 | 93 | export default HowtoUse; 94 | -------------------------------------------------------------------------------- /src/screens/TrainBackCare.js: -------------------------------------------------------------------------------- 1 | import Nav from "../components/core/Nav"; 2 | import Modal from "../components/core/Modal"; 3 | import Webcam from "react-webcam"; 4 | import { useEffect, useRef, useState } from "react"; 5 | import { Link } from "react-router-dom"; 6 | import * as mobilenetModule from "@tensorflow-models/mobilenet"; 7 | import * as knnClassifier from "@tensorflow-models/knn-classifier"; 8 | import { model } from "@tensorflow/tfjs"; 9 | 10 | const toDatasetObject = async (dataset) => { 11 | const result = await Promise.all( 12 | Object.entries(dataset).map(async ([classId, value]) => { 13 | const data = await value.data(); 14 | 15 | return { 16 | label: classId, 17 | data: Array.from(data), 18 | shape: value.shape, 19 | }; 20 | }) 21 | ); 22 | 23 | return result; 24 | }; 25 | 26 | const classifier = knnClassifier.create(); 27 | 28 | const TrainBackCare = () => { 29 | const [isError, setIsError] = useState(false); 30 | const [showModal, setShowModal] = useState(true); 31 | const [showAlert, setShowAlert] = useState(true); 32 | const [showDoneModal, setShowDoneModal] = useState(false); 33 | const [goodPicsCount, setGoodPicsCount] = useState(0); 34 | const [badPicsCount, setBadPicsCount] = useState(0); 35 | const [isDisabled, setIsDisabled] = useState(true); 36 | const [saving, setSaving] = useState(false); 37 | const [isTrained, setIsTrained] = useState(false); 38 | const [result, setResult] = useState(null); 39 | 40 | const webcamRef = useRef(null); 41 | 42 | const trainModel = async (classId) => { 43 | setSaving(true); 44 | 45 | let img = webcamRef.current.video; 46 | 47 | let mobilenet = await mobilenetModule.load(); 48 | let activation = mobilenet.infer(img, true); 49 | classifier.addExample(activation, classId); 50 | 51 | setSaving(false); 52 | 53 | if (classId === "good") { 54 | setGoodPicsCount(goodPicsCount + 1); 55 | } 56 | 57 | if (classId === "bad") { 58 | setBadPicsCount(badPicsCount + 1); 59 | } 60 | }; 61 | 62 | const saveModel = async () => { 63 | let dataset = classifier.getClassifierDataset(); 64 | 65 | let datasetObj = await toDatasetObject(dataset); 66 | 67 | let jsonStr = JSON.stringify(datasetObj); 68 | localStorage.setItem("myData", jsonStr); 69 | 70 | setIsTrained(true); 71 | setShowDoneModal(true); 72 | }; 73 | 74 | const classifyPic = async () => { 75 | setResult("finding if your posture is correct"); 76 | let net = await mobilenetModule.load(); 77 | const img = webcamRef.current.video; 78 | // Get the activation from mobilenet from the webcam. 79 | const activation = net.infer(img, true); 80 | const result = await classifier.predictClass(activation); 81 | 82 | setResult(result.label); 83 | }; 84 | 85 | const showError = () => { 86 | setIsError(true); 87 | }; 88 | 89 | useEffect(() => { 90 | if (goodPicsCount >= 2 && badPicsCount >= 2) { 91 | setIsDisabled(false); 92 | } 93 | }, [goodPicsCount, badPicsCount]); 94 | 95 | useEffect(() => { 96 | localStorage.setItem("visited", true); 97 | }, []); 98 | 99 | return ( 100 |
101 | 102 | 103 |
104 |

Teach this app 👩‍🏫

105 |

106 | Teach this app the difference between good posture and bad posture 107 |

108 | 109 |
110 |
116 | {" "} 117 | 131 |
132 | 133 |
134 | {saving && ( 135 |
139 | Please wait. The app is learning about your posture. 140 |
141 | )} 142 | 145 | 148 | 155 | {isTrained && ( 156 | 159 | )} 160 | {isTrained && result &&
{result}
} 161 | {isTrained && ( 162 | 163 | {" "} 164 |

165 | {" "} 166 | 167 | Back to home 168 | 169 |

170 | 171 | )} 172 |
173 |
174 |
175 | 176 | {isError && ( 177 | 178 |
179 |

Error

180 |
181 | exercise 188 |
189 |
190 | Problem with camera. Can't use this feature :( 191 |
192 |
193 | 194 | 197 | 198 |
199 |
200 |
201 | )} 202 | 203 | {showModal && ( 204 | 205 |
206 |
207 |

208 | Back Care 209 |

210 | 214 |
215 | 216 |
217 |
218 |

219 | Take pictures of good 220 | and bad posture{" "} 221 |

222 |

223 | Click on Done to finish 224 |

225 |

226 | More the pics 227 | better it will be to detect{" "} 228 |

229 |
230 |
231 |
232 | 238 |
239 |
240 |
241 | )} 242 | 243 | {showAlert && ( 244 |
245 | We don't save your data. Everything resides only on your browser.{" "} 246 | setShowAlert(false)} 249 | > 250 |
251 | )} 252 | 253 | {showDoneModal && ( 254 | 255 |
256 | setShowDoneModal(false)} 265 | > 266 |
267 |

268 | Yayy! 269 |

270 | 274 |
275 | 276 |
277 |
278 |

279 | Now the app knows what a correct posture is. We are ready to 280 | use the app now! 281 |

282 |
283 |
284 |
285 | 286 | 295 | 296 | 297 | 306 |
307 |
308 |
309 | )} 310 |
311 | ); 312 | }; 313 | 314 | export default TrainBackCare; 315 | -------------------------------------------------------------------------------- /src/services/datahandling.js: -------------------------------------------------------------------------------- 1 | export const setEarData = () => { 2 | let eardata = JSON.parse(localStorage.getItem("eardata")); 3 | if (!eardata) { 4 | console.log("no ear data"); 5 | eardata = [0, 0, 0, 0, 0, 0, 0]; 6 | } 7 | let todayDate = new Date(); 8 | let today = todayDate.getDay(); 9 | eardata[today] = Number(eardata[today]) + 40; 10 | localStorage.setItem("eardata", JSON.stringify(eardata)); 11 | }; 12 | -------------------------------------------------------------------------------- /src/services/notifications.js: -------------------------------------------------------------------------------- 1 | export const notify = (url) => { 2 | console.log("hereeeeeeee"); 3 | Notification.requestPermission(function (result) { 4 | if (result === "granted") { 5 | navigator.serviceWorker.ready.then(function (registration) { 6 | registration.showNotification("Exercise Time", { 7 | body: "Look away from the screen", 8 | icon: "./assets/cartoon/smileydev.png", 9 | data: url, 10 | vibrate: [200, 100, 200, 100, 200, 100, 200], 11 | tag: "Exercise Reminder", 12 | requireInteraction: true, 13 | actions: [ 14 | { 15 | action: "exercise-action", 16 | title: "Do exercise", 17 | }, 18 | ], 19 | }); 20 | console.log(); 21 | }); 22 | } 23 | }); 24 | 25 | const audio = new Audio( 26 | "https://soundbible.com/mp3/service-bell_daniel_simion.mp3" 27 | ); 28 | audio.play(); 29 | }; 30 | 31 | export const notifySitStraight = (url) => { 32 | console.log("hereeeeeeee"); 33 | Notification.requestPermission(function (result) { 34 | if (result === "granted") { 35 | navigator.serviceWorker.ready.then(function (registration) { 36 | registration.showNotification("Sit Straight", { 37 | body: "Bad posture sit straight", 38 | icon: "./assets/cartoon/saddev.png", 39 | data: url, 40 | vibrate: [200, 100, 200, 100, 200, 100, 200], 41 | tag: "Sit straight", 42 | }); 43 | console.log(); 44 | }); 45 | } 46 | }); 47 | }; 48 | 49 | export const notifyEar = (url) => { 50 | console.log("hereeeeeeee"); 51 | Notification.requestPermission(function (result) { 52 | if (result === "granted") { 53 | navigator.serviceWorker.ready.then(function (registration) { 54 | registration.showNotification("Earphone remove time", { 55 | body: "Stop using earphones now!", 56 | icon: "./assets/cartoon/smileydev.png", 57 | data: url, 58 | vibrate: [200, 100, 200, 100, 200, 100, 200], 59 | tag: "Exercise Reminder", 60 | requireInteraction: true, 61 | actions: [ 62 | { 63 | action: "exercise-action", 64 | title: "Do exercise", 65 | }, 66 | ], 67 | }); 68 | console.log(); 69 | }); 70 | } 71 | }); 72 | 73 | const audio = new Audio( 74 | "https://soundbible.com/mp3/service-bell_daniel_simion.mp3" 75 | ); 76 | audio.play(); 77 | }; 78 | --------------------------------------------------------------------------------