├── 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 |
15 | You need to enable JavaScript to run this app.
16 |
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