├── src ├── global │ ├── styles │ │ ├── utilities.css │ │ └── variables.css │ ├── reducer │ │ └── index.js │ ├── graphql │ │ └── index.js │ └── store │ │ └── index.js ├── Main │ ├── actions │ │ ├── index.js │ │ └── creators │ │ │ └── index.js │ ├── reducer │ │ └── index.js │ ├── index.js │ ├── Main.js │ ├── General │ │ ├── sections │ │ │ ├── DailyCycling.js │ │ │ ├── DailySteps.js │ │ │ ├── DailyWeight.js │ │ │ ├── DailySleep.js │ │ │ ├── WeeklySteps.js │ │ │ ├── WeeklyWeight.js │ │ │ ├── WeeklyHeartRate.js │ │ │ ├── WeeklySleep.js │ │ │ ├── styles.css │ │ │ └── DailyHeartRate.js │ │ ├── styles.css │ │ └── index.js │ └── config │ │ └── index.js ├── components │ ├── Tooltip │ │ ├── styles.css │ │ └── index.js │ ├── BarShape │ │ └── index.js │ └── Health2LinesGraph │ │ └── index.js └── index.js ├── .gitignore ├── static.json ├── .gitmodules ├── docker-compose.yml ├── public ├── manifest.json └── index.html ├── README-REACT-CREATE-APP.md ├── config ├── jest │ ├── fileTransform.js │ └── cssTransform.js ├── polyfills.js ├── paths.js ├── env.js ├── webpackDevServer.config.js ├── webpack.config.dev.js └── webpack.config.prod.js ├── scripts ├── test.js ├── start.js └── build.js ├── README.md ├── Dockerfile └── package.json /src/global/styles/utilities.css: -------------------------------------------------------------------------------- 1 | @import "./variables.css"; -------------------------------------------------------------------------------- /src/Main/actions/index.js: -------------------------------------------------------------------------------- 1 | export const SAMPLE_ACTION = 'SAMPLE_ACTION'; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist 4 | go/.cache 5 | server 6 | .vscode -------------------------------------------------------------------------------- /src/Main/actions/creators/index.js: -------------------------------------------------------------------------------- 1 | import { createAction } from 'redux-actions'; 2 | 3 | -------------------------------------------------------------------------------- /static.json: -------------------------------------------------------------------------------- 1 | { 2 | "proxies": { 3 | "/api/": { 4 | "origin": "${API_URL}" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "go/src/server/vendor/gopkg.in/mgo.v2"] 2 | path = go/src/server/vendor/gopkg.in/mgo.v2 3 | url = http://gopkg.in/mgo.v2 -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | healthdashboard: 5 | image: maximeheckel/healthdashboard:latest 6 | links: 7 | - "mongodb" 8 | ports: 9 | - "8000:8000" 10 | mongodb: 11 | image: mongo:3.0 -------------------------------------------------------------------------------- /src/global/reducer/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import main from '../../Main/reducer'; 3 | import { client } from '../graphql'; 4 | 5 | const rootReducer = combineReducers({ 6 | main, 7 | apollo: client.reducer(), 8 | }); 9 | 10 | export default rootReducer; 11 | -------------------------------------------------------------------------------- /src/global/graphql/index.js: -------------------------------------------------------------------------------- 1 | import ApolloClient, { createNetworkInterface } from 'apollo-client'; 2 | 3 | const host = process.env.HOSTNAME || ''; 4 | 5 | export const client = new ApolloClient({ 6 | networkInterface: 7 | createNetworkInterface({ uri: `${host}/api/v1/health/graphql` }), 8 | shouldBatch: true, 9 | }); 10 | -------------------------------------------------------------------------------- /src/components/Tooltip/styles.css: -------------------------------------------------------------------------------- 1 | @import "global/styles/utilities.css"; 2 | 3 | .wrapper { 4 | color: $color-white; 5 | background-color: $color-black; 6 | font-size: $font-size-text; 7 | padding: $space-xs $space-sm; 8 | border-radius: $border-radius-default; 9 | } 10 | 11 | .tooltipData { 12 | margin-top: $space-sm; 13 | } -------------------------------------------------------------------------------- /src/Main/reducer/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | SAMPLE_ACTION, 3 | } from '../actions'; 4 | 5 | const INITIAL = {}; 6 | 7 | export default function landing(state = INITIAL, action) { 8 | switch (action.type) { 9 | case SAMPLE_ACTION: 10 | console.log('Sample action'); 11 | return state; 12 | default: 13 | return state; 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /README-REACT-CREATE-APP.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app). 2 | 3 | Below you will find some information on how to perform common tasks.
4 | You can find the most recent version of this guide [here](https://github.com/facebookincubator/create-react-app/blob/master/packages/react-scripts/template/README.md). 5 | 6 | -------------------------------------------------------------------------------- /config/jest/fileTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | 5 | // This is a custom Jest transformer turning file imports into filenames. 6 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 7 | 8 | module.exports = { 9 | process(src, filename) { 10 | return `module.exports = ${JSON.stringify(path.basename(filename))};`; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /config/jest/cssTransform.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // This is a custom Jest transformer turning style imports into empty objects. 4 | // http://facebook.github.io/jest/docs/tutorial-webpack.html 5 | 6 | module.exports = { 7 | process() { 8 | return 'module.exports = {};'; 9 | }, 10 | getCacheKey() { 11 | // The output is always the same. 12 | return 'cssTransform'; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /src/Main/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | Switch, 4 | Route, 5 | Redirect, 6 | } from 'react-router-dom'; 7 | import General from './General'; 8 | 9 | const Main = () => { 10 | return ( 11 | 12 | 13 | 14 | 15 | ); 16 | } 17 | 18 | export default Main; 19 | -------------------------------------------------------------------------------- /src/Main/Main.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | 3 | class Main extends Component { 4 | 5 | static propTypes = { 6 | children: PropTypes.element, 7 | } 8 | 9 | render() { 10 | // eslint-disable-next-line no-use-before-define 11 | const { children, ...attributes } = this.props; 12 | return ( 13 |
14 | HELLO 15 |
16 | ); 17 | } 18 | } 19 | 20 | export default Main; 21 | -------------------------------------------------------------------------------- /src/components/BarShape/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const BarShape = (props) => { 5 | const { fill, x, y, width, height, coeffHeight } = props; 6 | return ( 7 | 15 | ); 16 | }; 17 | 18 | BarShape.propTypes = { 19 | fill: PropTypes.string, 20 | x: PropTypes.number, 21 | y: PropTypes.number, 22 | width: PropTypes.number, 23 | height: PropTypes.number, 24 | coeffHeight: PropTypes.number.isRequired, 25 | }; 26 | 27 | 28 | export default BarShape; 29 | -------------------------------------------------------------------------------- /config/polyfills.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (typeof Promise === 'undefined') { 4 | // Rejection tracking prevents a common issue where React gets into an 5 | // inconsistent state due to an error, but it gets swallowed by a Promise, 6 | // and the user has no idea what causes React's erratic future behavior. 7 | require('promise/lib/rejection-tracking').enable(); 8 | window.Promise = require('promise/lib/es6-extensions.js'); 9 | } 10 | 11 | // fetch() polyfill for making API calls. 12 | require('whatwg-fetch'); 13 | 14 | // Object.assign() is commonly used with React. 15 | // It will use the native implementation if it's present and isn't buggy. 16 | Object.assign = require('object-assign'); 17 | -------------------------------------------------------------------------------- /src/Main/General/sections/DailyCycling.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import css from './styles.css'; 4 | 5 | const DailyCycling = ({ dataCycling = {} }) => { 6 | const { value: valueCycling } = dataCycling; 7 | return ( 8 |
9 |
10 |
11 | { valueCycling / 1000 || 0 } km 12 |
13 |
Cycled
14 |
15 |
16 | ); 17 | }; 18 | 19 | DailyCycling.propTypes = { 20 | dataCycling: PropTypes.object, 21 | }; 22 | 23 | export default DailyCycling; 24 | -------------------------------------------------------------------------------- /src/global/store/index.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux'; 2 | import promiseMiddleware from 'redux-promise-middleware'; 3 | import { createLogger } from 'redux-logger'; 4 | import rootReducer from '../reducer'; 5 | 6 | const loggerMiddleware = createLogger(); 7 | 8 | let middlewares = applyMiddleware( 9 | promiseMiddleware() 10 | ); 11 | 12 | if (process.env.NODE_ENV === 'development') { 13 | middlewares = applyMiddleware( 14 | promiseMiddleware(), 15 | loggerMiddleware 16 | ); 17 | } 18 | 19 | export default function configureStore(initialState) { 20 | const store = createStore( 21 | rootReducer, 22 | initialState, 23 | middlewares, 24 | ); 25 | 26 | return store; 27 | } 28 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { ApolloProvider } from 'react-apollo'; 4 | import { 5 | BrowserRouter as Router, 6 | Route, 7 | } from 'react-router-dom'; 8 | import configureStore from './global/store'; 9 | import { client } from './global/graphql'; 10 | import Main from './Main'; 11 | 12 | const store = configureStore(); 13 | 14 | ReactDOM.render( 15 | 16 | 17 |
18 | 23 | 24 |
25 |
26 |
, 27 | document.getElementById('root') 28 | ); 29 | -------------------------------------------------------------------------------- /scripts/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Do this as the first thing so that any code reading it knows the right env. 4 | process.env.BABEL_ENV = 'test'; 5 | process.env.NODE_ENV = 'test'; 6 | process.env.PUBLIC_URL = ''; 7 | 8 | // Makes the script crash on unhandled rejections instead of silently 9 | // ignoring them. In the future, promise rejections that are not handled will 10 | // terminate the Node.js process with a non-zero exit code. 11 | process.on('unhandledRejection', err => { 12 | throw err; 13 | }); 14 | 15 | // Ensure environment variables are read. 16 | require('../config/env'); 17 | 18 | const jest = require('jest'); 19 | const argv = process.argv.slice(2); 20 | 21 | // Watch unless on CI or in coverage mode 22 | if (!process.env.CI && argv.indexOf('--coverage') < 0) { 23 | argv.push('--watch'); 24 | } 25 | 26 | 27 | jest.run(argv); 28 | -------------------------------------------------------------------------------- /src/components/Tooltip/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import moment from 'moment'; 4 | import css from './styles.css'; 5 | 6 | const CustomTooltip = ({ payload }) => { 7 | return ( 8 |
9 |
10 | {payload[0] ? moment(payload[0].payload.startDate).format('LL') || null : null} 11 |
12 |
13 | {payload[0] ? payload[0].value : ''} {payload[0] ? payload[0].unit : ''} 14 |
15 |
16 | {`${payload[1] ? payload[1].value : ''} 17 | ${payload[1] ? payload[1].unit : ''}`} 18 |
19 |
20 |
21 |
22 | ); 23 | }; 24 | 25 | CustomTooltip.propTypes = { 26 | payload: PropTypes.array, 27 | }; 28 | 29 | export default CustomTooltip; 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apple Health Web Dashboard 2 | 3 | NOTE: Apple Health Web Dashboard is a clone of https://github.com/MaximeHeckel/health-dashboard 4 | 5 | ## Description: 6 | 7 | Apple Health Web Dashboard provides a daily view of your Apple Health data, like steps, sleep, etc. 8 | 9 | Apple Health Web Dashboard is a react web app (bootstrapped with React Create App). It pulls data from a backend server and displays it. See: https://github.com/markwk/apple-health-sync-backend 10 | 11 | ## Dependencies 12 | * Should have installed [create-react-app](https://github.com/facebookincubator/create-react-app) globally 13 | npm install -g create-react-app 14 | * To make a build version, should have installed yarn globally 15 | 16 | ## Installation and Setup 17 | 18 | * Install dependencies by running $ npm install 19 | * Set your backend url by editing config/webpackDevServer.config.js to proxy target. It's currently set to target: 'http://localhost:8000', 20 | * To test, run $ npm start 21 | -------------------------------------------------------------------------------- /src/Main/General/sections/DailySteps.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import css from './styles.css'; 4 | 5 | const DailySteps = ({ dataSteps = {}, dataDistance = {} }) => { 6 | const { value: valueSteps } = dataSteps; 7 | const { value: valueDistance } = dataDistance; 8 | return ( 9 |
10 |
11 |
12 | { valueSteps || 0 } steps 13 |
14 |
15 | — or — 16 |
17 |
18 | { valueDistance / 1000 || 0 } km 19 |
20 |
Walked / Ran
21 |
22 |
23 | ); 24 | }; 25 | 26 | DailySteps.propTypes = { 27 | dataSteps: PropTypes.object, 28 | dataDistance: PropTypes.object, 29 | }; 30 | 31 | export default DailySteps; 32 | -------------------------------------------------------------------------------- /src/Main/General/sections/DailyWeight.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import css from './styles.css'; 4 | 5 | const DailyWeight = ({ dataWeight = {}, dataBmi = {}, dataBF = {} }) => { 6 | const { value: valueWeight } = dataWeight; 7 | const { value: valueBmi } = dataBmi; 8 | const { value: valueBF } = dataBF; 9 | return ( 10 |
11 |
12 |
13 | { valueWeight / 1000 || 0 } kg 14 |
15 |
Mass
16 |
17 | { valueBmi || 0 } 18 |
19 |
BMI
20 |
21 | { valueBF || 0 } % 22 |
23 |
Bodyfat
24 |
25 |
26 | ); 27 | }; 28 | 29 | DailyWeight.propTypes = { 30 | dataWeight: PropTypes.object, 31 | dataBmi: PropTypes.object, 32 | dataBF: PropTypes.object, 33 | }; 34 | 35 | export default DailyWeight; 36 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Mark Koester - Health Dashboard 12 | 13 | 14 | 17 |
18 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/Main/General/sections/DailySleep.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import moment from 'moment'; 4 | import forEach from 'lodash/forEach'; 5 | import css from './styles.css'; 6 | 7 | class DailySleep extends Component { 8 | static propTypes = { 9 | dataSleep: PropTypes.array, 10 | } 11 | 12 | normalizeData(entries) { 13 | let normalizedOutput = 0; 14 | 15 | forEach(entries, (entry) => { 16 | if (entry.value === 'INBED') { 17 | normalizedOutput += moment(entry.endDate) - moment(entry.startDate); 18 | } 19 | }); 20 | 21 | return normalizedOutput; 22 | } 23 | 24 | render() { 25 | const { 26 | dataSleep, 27 | } = this.props; 28 | const sleepTime = this.normalizeData(dataSleep); 29 | 30 | return ( 31 |
32 |
33 |
34 | {(sleepTime / 3600 / 1000).toFixed(2)} h 35 |
36 |
Slept
37 |
38 |
39 | ); 40 | } 41 | } 42 | 43 | export default DailySleep; 44 | -------------------------------------------------------------------------------- /src/Main/General/sections/WeeklySteps.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import forEach from 'lodash/forEach'; 4 | import Health2LinesGraph from '../../../components/Health2LinesGraph'; 5 | import css from './styles.css'; 6 | 7 | const WeeklySteps = ({ dataSteps = [], dataCycling = [] }) => { 8 | const weeklyData = []; 9 | forEach(dataSteps, (entry, i) => { 10 | weeklyData.push({ valueWalking: dataSteps[i].value, valueCycling: dataCycling[i].value / 1000 }); 11 | }); 12 | 13 | return ( 14 |
15 |
16 | 27 |
28 |
29 |
30 | Weekly walking and cycling distance 31 |
32 |
33 |
34 |
35 | ); 36 | }; 37 | 38 | WeeklySteps.propTypes = { 39 | dataSteps: PropTypes.array, 40 | dataCycling: PropTypes.array, 41 | }; 42 | 43 | export default WeeklySteps; 44 | -------------------------------------------------------------------------------- /src/Main/General/sections/WeeklyWeight.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import forEach from 'lodash/forEach'; 4 | import Health2LinesGraph from '../../../components/Health2LinesGraph'; 5 | import css from './styles.css'; 6 | 7 | const WeeklyWeight = ({ dataWeight = [], dataBF = [] }) => { 8 | const weeklyData = []; 9 | forEach(dataWeight, (entry, i) => { 10 | weeklyData.push({ valueWeight: dataWeight[i].value / 1000, valueBF: dataBF[i].value }); 11 | }); 12 | 13 | return ( 14 |
15 |
16 | 29 |
30 |
31 |
32 | Weekly weight and bodyfat percentage 33 |
34 |
35 |
36 |
37 | ); 38 | }; 39 | 40 | WeeklyWeight.propTypes = { 41 | dataWeight: PropTypes.array, 42 | dataBF: PropTypes.array, 43 | }; 44 | 45 | export default WeeklyWeight; 46 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:latest as serverbuilder 2 | 3 | ADD ./go /go/src/github.com/MaximeHeckel/healthDashboard/go/ 4 | WORKDIR /go/src/github.com/MaximeHeckel/healthDashboard/go/src/server 5 | RUN CGO_ENABLED=0 GOOS=linux go build -ldflags '-s' -a -installsuffix cgo -v -o server 6 | 7 | ################################### 8 | 9 | FROM node:7.4-alpine as appbuilder 10 | 11 | RUN apk --update add git bash curl tar \ 12 | && rm -rf /var/cache/apk/* \ 13 | && touch ~/.bashrc \ 14 | && curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 0.22.0 15 | ENV NPM_CONFIG_LOGLEVEL=warn \ 16 | PATH=/root/.yarn/bin:${PATH} 17 | 18 | ADD . ./app 19 | WORKDIR /app 20 | RUN yarn install --ignore-engines 21 | ENV NODE_ENV=production 22 | RUN yarn build 23 | 24 | ################################## 25 | 26 | FROM alpine:latest 27 | 28 | COPY --from=serverbuilder /go/src/github.com/MaximeHeckel/healthDashboard/go/src/server/server ./app/ 29 | COPY --from=appbuilder /app/build/index.html ./app/ 30 | COPY --from=appbuilder /app/build/manifest.json ./app/ 31 | COPY --from=appbuilder /app/build/asset-manifest.json ./app/ 32 | COPY --from=appbuilder /app/build/static/js/* ./app/static/js/ 33 | COPY --from=appbuilder /app/build/static/css/* ./app/static/css/ 34 | WORKDIR /app 35 | RUN apk --update add ca-certificates && chmod +x server 36 | 37 | EXPOSE 8000 38 | ENTRYPOINT ["./server", "-static=./"] -------------------------------------------------------------------------------- /src/Main/General/sections/WeeklyHeartRate.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { 4 | BarChart, 5 | Bar, 6 | YAxis, 7 | Tooltip, 8 | } from 'recharts'; 9 | import CustomTooltip from '../../../components/Tooltip'; 10 | import BarShape from '../../../components/BarShape'; 11 | import css from './styles.css'; 12 | 13 | const WeeklyHeartRate = ({ dataHR = [] }) => { 14 | return ( 15 |
16 |
17 | 18 | 23 | } 30 | /> 31 | 39 | } /> 40 | 41 |
42 |
43 |
44 | Weekly heart rate range 45 |
46 |
47 |
48 |
49 | ); 50 | }; 51 | 52 | WeeklyHeartRate.propTypes = { 53 | dataHR: PropTypes.array, 54 | }; 55 | 56 | export default WeeklyHeartRate; 57 | -------------------------------------------------------------------------------- /src/components/Health2LinesGraph/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { 4 | LineChart, 5 | Line, 6 | Tooltip, 7 | YAxis, 8 | } from 'recharts'; 9 | import CustomTooltip from '../../components/Tooltip'; 10 | 11 | const Health2LinesGraph = ({ 12 | data, 13 | domain1, 14 | domain2, 15 | value1, 16 | value2, 17 | color1, 18 | color2, 19 | unit1, 20 | unit2, 21 | height, 22 | width, 23 | }) => { 24 | return ( 25 | 26 | 34 | 43 | 52 | 61 | } /> 62 | 63 | ); 64 | }; 65 | 66 | Health2LinesGraph.propTypes = { 67 | data: PropTypes.array, 68 | value1: PropTypes.string, 69 | value2: PropTypes.string, 70 | color1: PropTypes.string, 71 | color2: PropTypes.string, 72 | unit1: PropTypes.string, 73 | unit2: PropTypes.string, 74 | height: PropTypes.number, 75 | width: PropTypes.number, 76 | xDataKey: PropTypes.string, 77 | domain1: PropTypes.array, 78 | domain2: PropTypes.array, 79 | }; 80 | 81 | export default Health2LinesGraph; 82 | -------------------------------------------------------------------------------- /src/global/styles/variables.css: -------------------------------------------------------------------------------- 1 | /* COLORS */ 2 | $color-white: #FFFFFF; 3 | $color-black: #212222; 4 | $color-tuna: #4A4B4D; 5 | $color-metal-blue-clear: #4362CB; 6 | $color-metal-blue-dark: #16275E; 7 | $color-data-green: #30E2DD; 8 | $color-data-blue: #24B9F0; 9 | $color-data-violet: #AEBDFC; 10 | $color-data-red: #F52A64; 11 | $color-data-orange: #FF9D13; 12 | $color-data-yellow: #FDC62E; 13 | 14 | $color-grey: #F6F5FA; 15 | 16 | /* SCREEN SIZES */ 17 | 18 | /* media queries */ 19 | $screen-xs: 480px; 20 | 21 | @custom-media --xs (width <= $screen-xs); 22 | @custom-media --gt-xs (width > $screen-xs); 23 | 24 | $screen-sm: 600px; 25 | 26 | @custom-media --sm ( $screen-xs < width <= $screen-sm); 27 | @custom-media --lt-sm (width <= $screen-sm); 28 | @custom-media --gt-sm (width > $screen-sm); 29 | 30 | $screen-md: 700px; 31 | 32 | @custom-media --md ( $screen-sm < width <= $screen-md); 33 | @custom-media --lt-md (width <= $screen-md); 34 | @custom-media --gt-md (width > $screen-md); 35 | 36 | $screen-lg: 1300px; 37 | 38 | @custom-media --lg ( $screen-md < width <= $screen-lg); 39 | @custom-media --lt-lg (width <= $screen-lg); 40 | @custom-media --gt-lg (width > $screen-lg); 41 | 42 | $screen-xl: 1500px; 43 | 44 | @custom-media --lt-xl (width <= $screen-xl); 45 | @custom-media --xl (width > $screen-xl); 46 | 47 | /* SPACING */ 48 | 49 | $space-xxs: 4px; 50 | $space-xs: 8px; 51 | $space-sm: 12px; 52 | $space-md: 16px; 53 | $space-lg: 20px; 54 | $space-xl: 24px; 55 | $space-xxl: 32px; 56 | $space-xxxl: 40px; 57 | $space-xxxxl: 48px; 58 | $space-xxxxxl: 56px; 59 | 60 | /* RADIUS */ 61 | 62 | $border-radius-default: 3px; 63 | 64 | /* FONTS */ 65 | 66 | $font-size-text: 12px; 67 | $line-height-text: 17px; 68 | 69 | $font-size-button: 12px; 70 | 71 | $font-size-subtitle-small: 16px; 72 | $line-height-subtitle-small: 22px; 73 | 74 | $font-size-subtitle: 22px; 75 | $line-height-subtitle: 31px; 76 | 77 | $font-size-title-small: 30px; 78 | $line-height-headline: 42px; 79 | 80 | $font-size-title: 34px; 81 | $line-height-title: 48px; 82 | 83 | -------------------------------------------------------------------------------- /src/Main/General/sections/WeeklySleep.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import moment from 'moment'; 4 | import { 5 | BarChart, 6 | Bar, 7 | YAxis, 8 | Tooltip, 9 | } from 'recharts'; 10 | import forEach from 'lodash/forEach'; 11 | import CustomTooltip from '../../../components/Tooltip'; 12 | import BarShape from '../../../components/BarShape'; 13 | import css from './styles.css'; 14 | 15 | function normalizeData(entries) { 16 | let entryDate = null; 17 | if (entries.length > 0) { 18 | entryDate = entries[entries.length - 1].startDate; 19 | } 20 | let normalizedOutput = 0; 21 | 22 | forEach(entries, (entry) => { 23 | if (entry.value === 'INBED') { 24 | normalizedOutput += moment(entry.endDate) - moment(entry.startDate); 25 | } 26 | }); 27 | 28 | normalizedOutput = (normalizedOutput / 3600 / 1000).toFixed(2); 29 | return { 30 | value: parseInt(normalizedOutput, 10), 31 | startDate: entryDate, 32 | }; 33 | } 34 | 35 | const WeeklySleep = ({ dataSleep = [] }) => { 36 | const weeklySleep = []; 37 | forEach(dataSleep, (entries) => { 38 | weeklySleep.push(normalizeData(entries)); 39 | }); 40 | 41 | return ( 42 |
43 |
44 | 45 | 50 | } 56 | /> 57 | } /> 58 | 59 |
60 |
61 |
62 | Weekly sleep hours 63 |
64 |
65 |
66 |
67 | ); 68 | }; 69 | 70 | WeeklySleep.propTypes = { 71 | dataSleep: PropTypes.array, 72 | }; 73 | 74 | export default WeeklySleep; 75 | -------------------------------------------------------------------------------- /config/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const url = require('url'); 6 | 7 | // Make sure any symlinks in the project folder are resolved: 8 | // https://github.com/facebookincubator/create-react-app/issues/637 9 | const appDirectory = fs.realpathSync(process.cwd()); 10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath); 11 | 12 | const envPublicUrl = process.env.PUBLIC_URL; 13 | 14 | function ensureSlash(path, needsSlash) { 15 | const hasSlash = path.endsWith('/'); 16 | if (hasSlash && !needsSlash) { 17 | return path.substr(path, path.length - 1); 18 | } else if (!hasSlash && needsSlash) { 19 | return `${path}/`; 20 | } else { 21 | return path; 22 | } 23 | } 24 | 25 | const getPublicUrl = appPackageJson => 26 | envPublicUrl || require(appPackageJson).homepage; 27 | 28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer 29 | // "public path" at which the app is served. 30 | // Webpack needs to know it to put the right