├── .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 | 
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 | 
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 | 
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 | 
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 | 
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 | You need to enable JavaScript to run this app.
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 |
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 |
7 |
8 |
9 |
10 |
11 | Dashboard
12 |
13 |
14 |
15 |
16 |
17 | Back care
18 |
19 |
20 |
21 |
22 |
23 | Eye Care
24 |
25 |
26 |
27 |
28 |
29 | Ear Care
30 |
31 |
32 |
33 |
34 |
35 | How to use
36 |
37 |
38 |
39 |
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 |
17 | {
21 | setBackOption(!backOption);
22 | }}
23 | >
24 |
25 |
26 |
27 |
28 |
29 |
Eye Care:
30 |
31 |
32 | {
36 | setEyeOption(!eyeOption);
37 | }}
38 | >
39 |
40 |
41 |
42 |
43 |
44 |
Ear Care:
45 |
46 |
47 | {
51 | setSoundOption(!soundOption);
52 | }}
53 | >
54 |
55 |
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 | setCard("backtrain")}
35 | >
36 | Next
37 |
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 |
28 | Next
29 |
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 | setCard("notifs")}
34 | >
35 | Next
36 |
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 |
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 | setCard("backcare")}
31 | >
32 | Next
33 |
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 | setCard("how")}
36 | >
37 | Next
38 |
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 |
23 |
24 |
25 |
26 |
How it works?
27 |
Find out how this feature works
28 |
setShowModal(true)}
31 | >
32 | Find out
33 |
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 |
43 | Take pics
44 | {" "}
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 | setShowModal(false)}
97 | >
98 | Done
99 |
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 |
250 | Start
251 |
252 | {isStarted && (
253 |
254 | Stop
255 |
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 |
setshowHowModal(true)}
31 | >
32 | Find out
33 |
34 |
35 |
36 |
Ear Exercises
37 |
38 | Get to know how to take care of your ears.
39 |
40 |
setExerciseModal(true)}
43 | >
44 | Find out
45 |
46 |
47 |
48 |
49 |
50 |
51 | {showHowModal && (
52 |
53 |
54 |
58 |
setshowHowModal(false)}
68 | >
69 |
70 | Ear Care
71 |
72 |
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 | setshowHowModal(false)}
101 | >
102 | done
103 |
104 |
105 |
106 |
107 | )}
108 |
109 | {showexerciseModal && (
110 |
111 |
112 |
116 |
setExerciseModal(false)}
126 | >
127 |
128 | Ear Care
129 |
130 |
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 | setExerciseModal(false)}
155 | >
156 | done
157 |
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 |
16 |
17 |
Look 20 feet away for 20 seconds.
18 |
19 | setIsDone(true)}
22 | >
23 | Done
24 |
25 |
26 |
27 | );
28 | };
29 |
30 | const DoneExerciseCard = () => {
31 | return (
32 |
33 |
Hurray!
34 |
35 |
42 |
43 |
44 | Yayy! You have done the exercise. You can close this tab now.
45 |
46 |
47 | Done
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 |
setshowHowModal(true)}
31 | >
32 | Find out
33 |
34 |
35 |
36 |
Eye Exercises
37 |
38 | Get to know some eye exercises to relax your eyes.
39 |
40 |
setExerciseModal(true)}
43 | >
44 | Find out
45 |
46 |
47 |
48 |
49 |
50 |
51 | {showHowModal && (
52 |
53 |
54 |
58 |
setshowHowModal(false)}
68 | >
69 |
70 | Eye Care
71 |
72 |
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 | setshowHowModal(false)}
100 | >
101 | done
102 |
103 |
104 |
105 |
106 | )}
107 |
108 | {showexerciseModal && (
109 |
110 |
111 |
115 | setExerciseModal(false)}
125 | >
126 |
127 | Eye Care
128 |
129 |
130 |
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 | setExerciseModal(false)}
148 | >
149 | done
150 |
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 |
25 | Teach the app what a good and bad posture is{" "}
26 |
35 | Here
36 |
37 |
38 | Click atleast 5 pictures in good and bad posture is
39 |
40 | Test the posture detection, click more pics for more
41 | accurate results
42 |
43 | Come back to home and start the timer
44 |
45 | Every 20 minutes the app will check your posture and will
46 | remind you if it is incorrect
47 |
48 |
49 |
50 |
51 |
Eye care
52 |
53 |
54 | Start the timer
55 |
56 | Every 20 minutes the app will remind you to do an exercise
57 |
58 | Click on the notification
59 |
60 | The app will navigate you to an exercise page. Click on Done
61 | when u r done doing the exercise
62 |
63 |
64 | The exercise is to look 20 feet away for 20 seconds every 20
65 | minutes to relax our eyes.
66 |
67 |
68 |
69 |
70 |
Ear care
71 |
72 |
73 | Click on the toggle button of Ear care on dashboard
74 | Give earphone permission
75 |
76 | Now after 40 minutes the app will remind you that you are
77 | reaching the daily limit of earphone usage time
78 |
79 |
80 | It is advisable to use earphones for maximum 60 minutes a
81 | day at a maximum of 60% volume
82 |
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 |
trainModel("good")}>
143 | Take Good Posture Pic {goodPicsCount}
144 |
145 |
trainModel("bad")}>
146 | Take Bad Posture Pic {badPicsCount}
147 |
148 |
saveModel()}
151 | disabled={isDisabled}
152 | >
153 | Done
154 |
155 | {isTrained && (
156 |
classifyPic()}>
157 | Test Check Posture
158 |
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 |
188 |
189 |
190 | Problem with camera. Can't use this feature :(
191 |
192 |
193 |
194 |
195 | Back to home
196 |
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 | setShowModal(false)}
235 | >
236 | Let's go
237 |
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 | setShowDoneModal(false)}
292 | >
293 | Home
294 |
295 |
296 |
297 | setShowDoneModal(false)}
303 | >
304 | Test
305 |
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 |
--------------------------------------------------------------------------------