From b395398b7be2db87f3ce09686daf96d331610d46 Mon Sep 17 00:00:00 2001
From: MayGo <maigo.erit@gmail.com>
Date: Sat, 15 Jun 2019 22:08:26 +0300
Subject: [PATCH 1/7] Add wake time and sleep time to calendar. Add en_GB
 locale for calendar. Add linechart.

---
 client/src/SummaryContext.tsx                 | 52 ++++++++++++
 .../src/components/LineCharts/LineChart.tsx   | 84 +++++++++++++++++++
 .../components/LineCharts/LineChart.util.ts   | 45 ++++++++++
 .../LineCharts/LineCharts.styles.ts           |  6 ++
 .../SummaryCalendar/SummaryCalendar.tsx       | 75 ++++++++++-------
 .../SummaryCalendar/SummaryCalendar.util.ts   | 42 +++++++++-
 client/src/constants.ts                       |  6 ++
 client/src/router.tsx                         | 28 ++++---
 client/src/routes/SummaryPage.tsx             |  7 +-
 electron/app/window-manager.ts                |  6 +-
 10 files changed, 304 insertions(+), 47 deletions(-)
 create mode 100644 client/src/SummaryContext.tsx
 create mode 100644 client/src/components/LineCharts/LineChart.tsx
 create mode 100644 client/src/components/LineCharts/LineChart.util.ts
 create mode 100644 client/src/components/LineCharts/LineCharts.styles.ts

diff --git a/client/src/SummaryContext.tsx b/client/src/SummaryContext.tsx
new file mode 100644
index 00000000..1410849b
--- /dev/null
+++ b/client/src/SummaryContext.tsx
@@ -0,0 +1,52 @@
+import moment from 'moment';
+import * as React from 'react';
+import { TrackItemService } from './services/TrackItemService';
+import {
+    summariseLog,
+    summariseOnline,
+    summariseTimeOnline,
+} from './components/SummaryCalendar/SummaryCalendar.util';
+
+export const SummaryContext = React.createContext<any>({});
+
+export const SummaryProvider = ({ children }) => {
+    const [isLoading, setIsLoading] = React.useState<any>(false);
+    const [selectedDate, setSelectedDate] = React.useState<any>(moment());
+    const [selectedMode, setSelectedMode] = React.useState<any>('month');
+
+    const [logSummary, setLogSummary] = React.useState<any>([]);
+    const [onlineSummary, setOnlineSummary] = React.useState<any>([]);
+    const [onlineTimesSummary, setOnlineTimesSummary] = React.useState<any>([]);
+
+    const loadData = React.useCallback(async () => {
+        setIsLoading(true);
+        const beginDate = moment(selectedDate).startOf(selectedMode);
+        const endDate = moment(selectedDate).endOf(selectedMode);
+
+        TrackItemService.findAllItems(beginDate, endDate).then(
+            ({ appItems, statusItems, logItems }) => {
+                setLogSummary(summariseLog(logItems, selectedMode));
+                setOnlineSummary(summariseOnline(statusItems, selectedMode));
+                setOnlineTimesSummary(summariseTimeOnline(statusItems, selectedMode));
+                setIsLoading(false);
+            },
+        );
+    }, [selectedDate, selectedMode]);
+
+    const defaultContext = {
+        selectedDate,
+        setSelectedDate,
+        selectedMode,
+        setSelectedMode,
+        logSummary,
+        onlineSummary,
+        onlineTimesSummary,
+        isLoading,
+    };
+
+    React.useEffect(() => {
+        loadData();
+    }, [selectedDate, selectedMode]); // eslint-disable-line react-hooks/exhaustive-deps
+
+    return <SummaryContext.Provider value={defaultContext}>{children}</SummaryContext.Provider>;
+};
diff --git a/client/src/components/LineCharts/LineChart.tsx b/client/src/components/LineCharts/LineChart.tsx
new file mode 100644
index 00000000..462c6fb4
--- /dev/null
+++ b/client/src/components/LineCharts/LineChart.tsx
@@ -0,0 +1,84 @@
+import * as React from 'react';
+import moment from 'moment';
+import {
+    VictoryBar,
+    VictoryChart,
+    VictoryAxis,
+    VictoryTooltip,
+    VictoryVoronoiContainer,
+} from 'victory';
+import { convertDate, DATE_TIME_FORMAT, TIME_FORMAT, COLORS } from '../../constants';
+import { chartTheme } from '../Timeline/ChartTheme';
+import useWindowSize from '@rehooks/window-size';
+import { SummaryContext } from '../../SummaryContext';
+import {
+    addToTimeDuration,
+    formatToTimeEveryOther,
+    dayTickValues,
+    formatToDay,
+    toTimeDuration,
+} from './LineChart.util';
+import { diffAndFormatShort } from '../../utils';
+
+const scale = { x: 'time', y: 'time' };
+const padding = { left: 50, top: 0, bottom: 20, right: 10 };
+const domainPadding = { y: 10, x: 10 };
+const labelComponent = () => (
+    <VictoryTooltip
+        style={chartTheme.tooltip.style}
+        cornerRadius={chartTheme.tooltip.cornerRadius}
+        pointerLength={chartTheme.tooltip.pointerLength}
+        flyoutStyle={chartTheme.tooltip.flyoutStyle}
+        renderInPortal
+        horizontal={false}
+    />
+);
+export const LineChart = () => {
+    const { innerWidth: chartWidth } = useWindowSize();
+    const { selectedDate, onlineTimesSummary } = React.useContext(SummaryContext);
+
+    console.error('onlineTimesSummary', onlineTimesSummary);
+
+    return (
+        <VictoryChart
+            theme={chartTheme}
+            scale={scale}
+            width={chartWidth}
+            height={500}
+            domainPadding={domainPadding}
+            padding={padding}
+            horizontal
+        >
+            <VictoryAxis
+                orientation="bottom"
+                tickCount={24}
+                tickFormat={formatToTimeEveryOther}
+                dependentAxis
+            />
+            <VictoryAxis orientation="left" name="time-axis" tickFormat={formatToDay} />
+            <VictoryBar
+                y={d => toTimeDuration(convertDate(d.beginDate), convertDate(d.beginDate))}
+                y0={d => toTimeDuration(convertDate(d.beginDate), convertDate(d.endDate))}
+                x={d => convertDate(d.beginDate).startOf('day')}
+                barWidth={10}
+                data={onlineTimesSummary}
+                labelComponent={labelComponent()}
+                labels={d =>
+                    `Start time: ${convertDate(d.beginDate).format(TIME_FORMAT)}
+                    End time: ${convertDate(d.endDate).format(TIME_FORMAT)}
+                    Duration: ${diffAndFormatShort(d.beginDate, d.endDate)}`
+                }
+            />
+            <VictoryBar
+                y={d => toTimeDuration(convertDate(d.beginDate), convertDate(d.beginDate))}
+                y0={d => addToTimeDuration(convertDate(d.beginDate), d.online)}
+                x={d => convertDate(d.beginDate).startOf('day')}
+                barWidth={10}
+                style={{ data: { fill: COLORS.green } }}
+                data={onlineTimesSummary}
+                labelComponent={labelComponent()}
+                labels={d => `Online: ${moment.duration(d.online).format()}`}
+            />
+        </VictoryChart>
+    );
+};
diff --git a/client/src/components/LineCharts/LineChart.util.ts b/client/src/components/LineCharts/LineChart.util.ts
new file mode 100644
index 00000000..46b74c43
--- /dev/null
+++ b/client/src/components/LineCharts/LineChart.util.ts
@@ -0,0 +1,45 @@
+import moment from 'moment';
+import { convertDate } from '../../constants';
+
+export const generateTickValues = (date, ticks, unit, startOf) => {
+    const day = convertDate(date).startOf(startOf);
+    const dates = [...Array(ticks)].map((__, i) => {
+        return day.clone().add(i, unit);
+    });
+
+    return dates;
+};
+
+export const toTimeDuration = (from, to) => {
+    const start = moment(from).startOf('day');
+
+    return moment(moment(to).diff(start));
+};
+export const addToTimeDuration = (from, duration) => {
+    const start = moment(from).startOf('day');
+
+    return moment(moment(from).diff(start) + duration);
+};
+
+export const isOddHour = date => moment(date).get('hour') % 2;
+
+export const formatToTime = t => moment.utc(t).format('HH:mm');
+
+export const formatToTimeEveryOther = t => {
+    const hour = moment(t).startOf('hour');
+    return formatToTime(hour);
+};
+
+export const formatToDay = t => moment(t).format('DD');
+
+export const timeTickValues = t => {
+    const ticks = 36;
+    const day = moment();
+    const dates = [...Array(ticks)].map((__, i) => {
+        return toTimeDuration(day, day.clone().add(i, 'hour'));
+    });
+
+    return dates;
+};
+
+export const dayTickValues = t => generateTickValues(t, 31, 'day', 'month');
diff --git a/client/src/components/LineCharts/LineCharts.styles.ts b/client/src/components/LineCharts/LineCharts.styles.ts
new file mode 100644
index 00000000..0b4f57d5
--- /dev/null
+++ b/client/src/components/LineCharts/LineCharts.styles.ts
@@ -0,0 +1,6 @@
+import styled from 'styled-components';
+
+export const Heading = styled.h3`
+    text-align: center;
+    color: rgba(0, 0, 0, 0.65);
+`;
diff --git a/client/src/components/SummaryCalendar/SummaryCalendar.tsx b/client/src/components/SummaryCalendar/SummaryCalendar.tsx
index e89e6a2f..fb7cb1bd 100644
--- a/client/src/components/SummaryCalendar/SummaryCalendar.tsx
+++ b/client/src/components/SummaryCalendar/SummaryCalendar.tsx
@@ -1,24 +1,42 @@
 import { Flex } from '@rebass/grid';
-import { Badge, Calendar, Spin } from 'antd';
+import { Badge, Calendar, Spin, Icon } from 'antd';
 import moment, { Moment } from 'moment';
 import * as React from 'react';
 import useReactRouter from 'use-react-router';
-import { TrackItemService } from '../../services/TrackItemService';
 import { TimelineContext } from '../../TimelineContext';
+import { SummaryContext } from '../../SummaryContext';
 import { Spinner } from '../Timeline/Timeline.styles';
 import { Item, TaskList } from './SummaryCalendar.styles';
-import { summariseLog, summariseOnline } from './SummaryCalendar.util';
+
 import { Logger } from '../../logger';
+import {
+    TIME_FORMAT,
+    convertDate,
+    TIME_FORMAT_SHORT,
+    DURATION_FORMAT,
+    DURATION_SETTINGS,
+} from '../../constants';
+import { formatDuration } from './SummaryCalendar.util';
 
 export const SummaryCalendar = () => {
     const { setTimerange } = React.useContext(TimelineContext);
+    const {
+        selectedDate,
+        setSelectedDate,
+        selectedMode,
+        setSelectedMode,
+        logSummary,
+        onlineSummary,
+        onlineTimesSummary,
+        isLoading,
+    } = React.useContext(SummaryContext);
 
     const { history } = useReactRouter();
 
-    const onDateSelect = (selectedDate: Moment | undefined) => {
+    const onDateSelect = (date: Moment | undefined) => {
         const pathname = '/app/timeline';
-        if (selectedDate) {
-            setTimerange([selectedDate.startOf('day'), selectedDate.endOf('day')]);
+        if (date) {
+            setTimerange([date.startOf('day'), date.endOf('day')]);
             history.push(pathname);
         } else {
             Logger.error('No date');
@@ -26,25 +44,6 @@ export const SummaryCalendar = () => {
 
         // setPath
     };
-    const [isLoading, setIsLoading] = React.useState<any>(false);
-    const [selectedDate, setSelectedDate] = React.useState<any>(moment());
-    const [selectedMode, setSelectedMode] = React.useState<any>('month');
-    const [logSummary, setLogSummary] = React.useState<any>([]);
-    const [onlineSummary, setOnlineSummary] = React.useState<any>([]);
-
-    React.useEffect(() => {
-        setIsLoading(true);
-        const beginDate = moment(selectedDate).startOf(selectedMode);
-        const endDate = moment(selectedDate).endOf(selectedMode);
-
-        TrackItemService.findAllItems(beginDate, endDate).then(
-            ({ appItems, statusItems, logItems }) => {
-                setLogSummary(summariseLog(logItems, selectedMode));
-                setOnlineSummary(summariseOnline(statusItems, selectedMode));
-                setIsLoading(false);
-            },
-        );
-    }, [selectedDate, selectedMode]);
 
     const changeSelectedDate = (date?: Moment, mode?: 'month' | 'year') => {
         setSelectedDate(date);
@@ -53,18 +52,28 @@ export const SummaryCalendar = () => {
 
     const getListData = day => {
         const listData: any[] = [];
-        const worked = logSummary[day];
-        if (worked) {
-            const formattedDuration = moment.duration(worked).format();
-            listData.push({ type: 'warning', content: `Worked: ${formattedDuration}` });
+
+        const times = onlineTimesSummary[day];
+        if (times) {
+            listData.push({
+                type: 'coffee',
+                content: `Wake time ${convertDate(times.beginDate).format(TIME_FORMAT_SHORT)}`,
+            });
+            listData.push({
+                type: 'eye-invisible',
+                content: `Sleep time ${convertDate(times.endDate).format(TIME_FORMAT_SHORT)}`,
+            });
         }
 
         const online = onlineSummary[day];
         if (online) {
-            const formattedDuration = moment.duration(online).format();
-            listData.push({ type: 'success', content: `Online: ${formattedDuration}` });
+            listData.push({ type: 'laptop', content: `Worked  ${formatDuration(online)}` });
         }
 
+        const worked = logSummary[day];
+        if (worked) {
+            listData.push({ type: 'tool', content: `Tasks  ${formatDuration(worked)}` });
+        }
         return listData || [];
     };
 
@@ -75,7 +84,9 @@ export const SummaryCalendar = () => {
                 <TaskList>
                     {listData.map(item => (
                         <Item key={item.content}>
-                            <Badge status={item.type} text={item.content} />
+                            <Icon type={item.type} theme="outlined" />
+                            {'  '}
+                            {item.content}
                         </Item>
                     ))}
                 </TaskList>
diff --git a/client/src/components/SummaryCalendar/SummaryCalendar.util.ts b/client/src/components/SummaryCalendar/SummaryCalendar.util.ts
index f13a4936..26f9c165 100644
--- a/client/src/components/SummaryCalendar/SummaryCalendar.util.ts
+++ b/client/src/components/SummaryCalendar/SummaryCalendar.util.ts
@@ -1,9 +1,29 @@
 import _ from 'lodash';
-import { convertDate } from '../../constants';
+import {
+    convertDate,
+    TIME_FORMAT,
+    BREAKPOINT_TIME,
+    DURATION_FORMAT,
+    DURATION_SETTINGS,
+} from '../../constants';
+import moment from 'moment';
+
+export const formatDuration = dur =>
+    moment.duration(dur).format(DURATION_FORMAT, DURATION_SETTINGS);
 
 export const groupByField = mode => item =>
     mode === 'month' ? convertDate(item.beginDate).date() : convertDate(item.beginDate).month();
 
+export const groupByActualDay = item => {
+    const date = convertDate(item.beginDate);
+    const day = date.date();
+
+    if (date.format(TIME_FORMAT) < BREAKPOINT_TIME) {
+        return day === 1 ? day : day - 1;
+    }
+    return day;
+};
+
 export const summariseLog = (items, mode) => {
     const data = {};
 
@@ -27,3 +47,23 @@ export const summariseOnline = (items, mode) => {
         });
     return data;
 };
+
+export const summariseTimeOnline = (items, mode) => {
+    const data = _(items)
+        .filter(item => item.app === 'ONLINE')
+        .groupBy(groupByActualDay)
+        .map((value, key) => {
+            return {
+                beginDate: _.minBy(
+                    value.filter(
+                        item => convertDate(item.beginDate).format(TIME_FORMAT) > BREAKPOINT_TIME,
+                    ),
+                    c => convertDate(c.beginDate),
+                ).beginDate,
+                endDate: _.maxBy(value, c => convertDate(c.endDate)).endDate,
+                online: _.sumBy(value, c => convertDate(c.endDate).diff(convertDate(c.beginDate))),
+            };
+        })
+        .value();
+    return data;
+};
diff --git a/client/src/constants.ts b/client/src/constants.ts
index 2e61e424..b4318234 100644
--- a/client/src/constants.ts
+++ b/client/src/constants.ts
@@ -3,5 +3,11 @@ import moment from 'moment';
 export const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
 export const INPUT_DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss ZZ';
 export const TIME_FORMAT = 'HH:mm:ss';
+export const TIME_FORMAT_SHORT = 'HH:mm';
+export const DURATION_FORMAT = 'w[w] d[d] h[h] m[m] s[s]';
+export const DURATION_SETTINGS = { largest: 2 };
+export const BREAKPOINT_TIME = '04:00';
 
 export const convertDate = (d: Date) => moment(d, INPUT_DATE_FORMAT);
+
+export const COLORS = { green: '#8BC34A' };
diff --git a/client/src/router.tsx b/client/src/router.tsx
index 4ae4314c..e44554ee 100644
--- a/client/src/router.tsx
+++ b/client/src/router.tsx
@@ -7,19 +7,27 @@ import { MainAppPage } from './routes/MainAppPage';
 import { TrayAppPage } from './routes/TrayAppPage';
 import { TimelineProvider } from './TimelineContext';
 
+import { LocaleProvider } from 'antd';
+import en_GB from 'antd/lib/locale-provider/en_GB';
+import moment from 'moment';
+import 'moment/locale/en-gb';
+
+moment.locale('en-gb');
 export function MainRouter() {
     return (
         <Router>
-            <RootProvider>
-                <TimelineProvider>
-                    <Switch>
-                        <Route path="/" exact component={MainAppPage} />
-                        <Route path="/app" component={MainAppPage} />
-                        <Route path="/trayApp" component={TrayAppPage} />
-                        <Route path="*" component={NotFound} />
-                    </Switch>
-                </TimelineProvider>
-            </RootProvider>
+            <LocaleProvider locale={en_GB}>
+                <RootProvider>
+                    <TimelineProvider>
+                        <Switch>
+                            <Route path="/" exact component={MainAppPage} />
+                            <Route path="/app" component={MainAppPage} />
+                            <Route path="/trayApp" component={TrayAppPage} />
+                            <Route path="*" component={NotFound} />
+                        </Switch>
+                    </TimelineProvider>
+                </RootProvider>
+            </LocaleProvider>
         </Router>
     );
 }
diff --git a/client/src/routes/SummaryPage.tsx b/client/src/routes/SummaryPage.tsx
index 8cc3f9ac..468a0c46 100644
--- a/client/src/routes/SummaryPage.tsx
+++ b/client/src/routes/SummaryPage.tsx
@@ -1,11 +1,16 @@
 import * as React from 'react';
 import { MainLayout } from '../components/MainLayout/MainLayout';
 import { SummaryCalendar } from '../components/SummaryCalendar/SummaryCalendar';
+import { SummaryProvider } from '../SummaryContext';
+import { LineChart } from '../components/LineCharts/LineChart';
 
 export function SummaryPage({ location }: any) {
     return (
         <MainLayout location={location}>
-            <SummaryCalendar />
+            <SummaryProvider>
+                <SummaryCalendar />
+                <LineChart />
+            </SummaryProvider>
         </MainLayout>
     );
 }
diff --git a/electron/app/window-manager.ts b/electron/app/window-manager.ts
index 9d152662..a0452118 100644
--- a/electron/app/window-manager.ts
+++ b/electron/app/window-manager.ts
@@ -142,7 +142,7 @@ export default class WindowManager {
          */
         let icon = os.platform() === 'darwin' ? config.icon : config.iconBig;
         const url = config.isDev
-            ? 'http://localhost:3000/trayApp'
+            ? 'http://localhost:3000/#/trayApp'
             : `file://${__dirname}/index.html#/trayApp`;
 
         this.menubar = menubar({
@@ -167,8 +167,8 @@ export default class WindowManager {
             this.menubar.window.webContents.send('focus-tray', 'ping');
 
             if (config.isDev) {
-            logger.info('Open menubar dev tools');
-            this.menubar.window.openDevTools({ mode: 'bottom' });
+                logger.info('Open menubar dev tools');
+                this.menubar.window.openDevTools({ mode: 'bottom' });
             }
         });
     }

From 77f9c3bb2a02f65bca08fff202f703f4839b341f Mon Sep 17 00:00:00 2001
From: MayGo <maigo.erit@gmail.com>
Date: Sat, 15 Jun 2019 22:15:45 +0300
Subject: [PATCH 2/7] use functional component

---
 .../src/components/LineCharts/LineChart.tsx   |  15 +-
 .../SummaryCalendar/SummaryCalendar.tsx       |  17 +-
 client/src/components/Timeline/Search.tsx     | 157 ++++++++----------
 3 files changed, 80 insertions(+), 109 deletions(-)

diff --git a/client/src/components/LineCharts/LineChart.tsx b/client/src/components/LineCharts/LineChart.tsx
index 462c6fb4..a496fb41 100644
--- a/client/src/components/LineCharts/LineChart.tsx
+++ b/client/src/components/LineCharts/LineChart.tsx
@@ -1,20 +1,13 @@
 import * as React from 'react';
 import moment from 'moment';
-import {
-    VictoryBar,
-    VictoryChart,
-    VictoryAxis,
-    VictoryTooltip,
-    VictoryVoronoiContainer,
-} from 'victory';
-import { convertDate, DATE_TIME_FORMAT, TIME_FORMAT, COLORS } from '../../constants';
+import { VictoryBar, VictoryChart, VictoryAxis, VictoryTooltip } from 'victory';
+import { convertDate, TIME_FORMAT, COLORS } from '../../constants';
 import { chartTheme } from '../Timeline/ChartTheme';
 import useWindowSize from '@rehooks/window-size';
 import { SummaryContext } from '../../SummaryContext';
 import {
     addToTimeDuration,
     formatToTimeEveryOther,
-    dayTickValues,
     formatToDay,
     toTimeDuration,
 } from './LineChart.util';
@@ -35,9 +28,7 @@ const labelComponent = () => (
 );
 export const LineChart = () => {
     const { innerWidth: chartWidth } = useWindowSize();
-    const { selectedDate, onlineTimesSummary } = React.useContext(SummaryContext);
-
-    console.error('onlineTimesSummary', onlineTimesSummary);
+    const { onlineTimesSummary } = React.useContext(SummaryContext);
 
     return (
         <VictoryChart
diff --git a/client/src/components/SummaryCalendar/SummaryCalendar.tsx b/client/src/components/SummaryCalendar/SummaryCalendar.tsx
index fb7cb1bd..b7ae14a9 100644
--- a/client/src/components/SummaryCalendar/SummaryCalendar.tsx
+++ b/client/src/components/SummaryCalendar/SummaryCalendar.tsx
@@ -1,25 +1,18 @@
 import { Flex } from '@rebass/grid';
 import { Badge, Calendar, Spin, Icon } from 'antd';
-import moment, { Moment } from 'moment';
+import { Moment } from 'moment';
 import * as React from 'react';
 import useReactRouter from 'use-react-router';
 import { TimelineContext } from '../../TimelineContext';
 import { SummaryContext } from '../../SummaryContext';
 import { Spinner } from '../Timeline/Timeline.styles';
 import { Item, TaskList } from './SummaryCalendar.styles';
-
 import { Logger } from '../../logger';
-import {
-    TIME_FORMAT,
-    convertDate,
-    TIME_FORMAT_SHORT,
-    DURATION_FORMAT,
-    DURATION_SETTINGS,
-} from '../../constants';
+import { convertDate, TIME_FORMAT_SHORT } from '../../constants';
 import { formatDuration } from './SummaryCalendar.util';
 
 export const SummaryCalendar = () => {
-    const { setTimerange } = React.useContext(TimelineContext);
+    const { loadTimerange } = React.useContext(TimelineContext);
     const {
         selectedDate,
         setSelectedDate,
@@ -36,13 +29,11 @@ export const SummaryCalendar = () => {
     const onDateSelect = (date: Moment | undefined) => {
         const pathname = '/app/timeline';
         if (date) {
-            setTimerange([date.startOf('day'), date.endOf('day')]);
+            loadTimerange([date.startOf('day'), date.endOf('day')]);
             history.push(pathname);
         } else {
             Logger.error('No date');
         }
-
-        // setPath
     };
 
     const changeSelectedDate = (date?: Moment, mode?: 'month' | 'year') => {
diff --git a/client/src/components/Timeline/Search.tsx b/client/src/components/Timeline/Search.tsx
index c99370d1..483b4baf 100644
--- a/client/src/components/Timeline/Search.tsx
+++ b/client/src/components/Timeline/Search.tsx
@@ -18,136 +18,125 @@ type IFullProps = IProps;
 const getDayBefore = d => moment(d).subtract(1, 'days');
 const getDayAfter = d => moment(d).add(1, 'days');
 
-export class Search extends React.PureComponent<IFullProps, IProps> {
-    public onChange = (dates: any) => {
-        Logger.debug('TIMERANGE:', dates, this.props);
+export const Search = ({ loadTimerange, timerange, changeVisibleTimerange }) => {
+    const onChange = (dates: any) => {
+        Logger.debug('TIMERANGE:', dates);
         if (dates != null) {
             const beginDate = dates[0];
             const endDate = dates[1];
             const newTimerange = [beginDate, endDate];
-            this.props.loadTimerange(newTimerange);
+            loadTimerange(newTimerange);
         } else {
             Logger.error('No dates selected');
         }
     };
 
-    public selectToday = () => {
-        this.props.loadTimerange(getTodayTimerange());
+    const selectToday = () => {
+        loadTimerange(getTodayTimerange());
     };
 
-    public selectYesterday = () => {
+    const selectYesterday = () => {
         const beginDate = getDayBefore(moment().startOf('day'));
         const endDate = getDayBefore(moment().endOf('day'));
-        this.props.loadTimerange([beginDate, endDate]);
+        loadTimerange([beginDate, endDate]);
     };
 
-    public goBackOneDay = () => {
-        const { timerange } = this.props;
+    const goBackOneDay = () => {
         const beginDate = getDayBefore(moment(timerange[0]));
         const endDate = getDayBefore(moment(timerange[1]));
-        this.props.loadTimerange([beginDate, endDate]);
+        loadTimerange([beginDate, endDate]);
     };
 
-    public goForwardOneDay = () => {
-        const { timerange } = this.props;
+    const goForwardOneDay = () => {
         const beginDate = getDayAfter(moment(timerange[0]));
         const endDate = getDayAfter(moment(timerange[1]));
-        this.props.loadTimerange([beginDate, endDate]);
+        loadTimerange([beginDate, endDate]);
     };
 
-    public showDay = () => {
-        const { timerange } = this.props;
+    const showDay = () => {
         const beginDate = moment(timerange[0]).startOf('day');
         const endDate = moment(timerange[0]).endOf('day');
-        this.props.changeVisibleTimerange([beginDate, endDate]);
+        changeVisibleTimerange([beginDate, endDate]);
     };
 
-    public showHour = () => {
-        const { timerange } = this.props;
+    const showHour = () => {
         const beginDate = moment(timerange[0]).startOf('hour');
         const endDate = moment(timerange[0]).endOf('hour');
-        this.props.changeVisibleTimerange([beginDate, endDate]);
+        changeVisibleTimerange([beginDate, endDate]);
     };
 
-    public showAM = () => {
-        const { timerange } = this.props;
+    const showAM = () => {
         const beginDate = moment(timerange[0]).startOf('day');
         const endDate = moment(timerange[0])
             .startOf('day')
             .hour(12);
-        this.props.changeVisibleTimerange([beginDate, endDate]);
+        changeVisibleTimerange([beginDate, endDate]);
     };
 
-    public showPM = () => {
-        const { timerange } = this.props;
+    const showPM = () => {
         const beginDate = moment(timerange[0])
             .startOf('day')
             .hour(12);
         const endDate = moment(timerange[0]).endOf('day');
-        this.props.changeVisibleTimerange([beginDate, endDate]);
+        changeVisibleTimerange([beginDate, endDate]);
     };
 
-    public showEvening = () => {
-        const { timerange } = this.props;
+    const showEvening = () => {
         const beginDate = moment(timerange[0])
             .startOf('day')
             .hour(17);
         const endDate = moment(timerange[0]).endOf('day');
-        this.props.changeVisibleTimerange([beginDate, endDate]);
+        changeVisibleTimerange([beginDate, endDate]);
     };
 
-    public render() {
-        const { timerange } = this.props;
-
-        Logger.debug('Have timerange in Search:', timerange);
-        return (
-            <Flex>
-                <Box p={1}>
-                    <Button onClick={this.selectYesterday}>Yesterday</Button>
-                </Box>
-                <Box p={1}>
-                    <Button onClick={this.goBackOneDay}>
-                        <Icon type="left" />
-                    </Button>
-                </Box>
-                <Box p={1}>
-                    <RangePicker value={timerange} onChange={this.onChange} />
-                </Box>
-                <Box p={1}>
-                    <Button onClick={this.goForwardOneDay}>
-                        <Icon type="right" />
-                    </Button>
-                </Box>
-                <Box p={1}>
-                    <Button onClick={this.selectToday}>Today</Button>
-                </Box>
-                <Box flex={1} />
-                <Box p={1}>
-                    <Button onClick={this.showDay} type="dashed">
-                        All Day
-                    </Button>
-                </Box>
-                <Box p={1}>
-                    <Button onClick={this.showAM} type="dashed">
-                        AM
-                    </Button>
-                </Box>
-                <Box p={1}>
-                    <Button onClick={this.showPM} type="dashed">
-                        PM
-                    </Button>
-                </Box>
-                <Box p={1}>
-                    <Button onClick={this.showEvening} type="dashed">
-                        Evening
-                    </Button>
-                </Box>
-                <Box p={1}>
-                    <Button onClick={this.showHour} type="dashed">
-                        Hour
-                    </Button>
-                </Box>
-            </Flex>
-        );
-    }
-}
+    Logger.debug('Have timerange in Search:', timerange);
+    return (
+        <Flex>
+            <Box p={1}>
+                <Button onClick={selectYesterday}>Yesterday</Button>
+            </Box>
+            <Box p={1}>
+                <Button onClick={goBackOneDay}>
+                    <Icon type="left" />
+                </Button>
+            </Box>
+            <Box p={1}>
+                <RangePicker value={timerange} onChange={onChange} />
+            </Box>
+            <Box p={1}>
+                <Button onClick={goForwardOneDay}>
+                    <Icon type="right" />
+                </Button>
+            </Box>
+            <Box p={1}>
+                <Button onClick={selectToday}>Today</Button>
+            </Box>
+            <Box flex={1} />
+            <Box p={1}>
+                <Button onClick={showDay} type="dashed">
+                    All Day
+                </Button>
+            </Box>
+            <Box p={1}>
+                <Button onClick={showAM} type="dashed">
+                    AM
+                </Button>
+            </Box>
+            <Box p={1}>
+                <Button onClick={showPM} type="dashed">
+                    PM
+                </Button>
+            </Box>
+            <Box p={1}>
+                <Button onClick={showEvening} type="dashed">
+                    Evening
+                </Button>
+            </Box>
+            <Box p={1}>
+                <Button onClick={showHour} type="dashed">
+                    Hour
+                </Button>
+            </Box>
+        </Flex>
+    );
+};

From 09a0a83f5611302a04ba6ab73e6f68702fef841a Mon Sep 17 00:00:00 2001
From: MayGo <maigo.erit@gmail.com>
Date: Sat, 15 Jun 2019 22:24:12 +0300
Subject: [PATCH 3/7] fix summaryitem click

---
 client/src/components/SummaryCalendar/SummaryCalendar.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/client/src/components/SummaryCalendar/SummaryCalendar.tsx b/client/src/components/SummaryCalendar/SummaryCalendar.tsx
index b7ae14a9..93d32ee9 100644
--- a/client/src/components/SummaryCalendar/SummaryCalendar.tsx
+++ b/client/src/components/SummaryCalendar/SummaryCalendar.tsx
@@ -29,7 +29,7 @@ export const SummaryCalendar = () => {
     const onDateSelect = (date: Moment | undefined) => {
         const pathname = '/app/timeline';
         if (date) {
-            loadTimerange([date.startOf('day'), date.endOf('day')]);
+            loadTimerange([date.clone().startOf('day'), date.clone().endOf('day')]);
             history.push(pathname);
         } else {
             Logger.error('No date');

From e8f94f2661fedc9175fb3ca8912e35e27a8fac99 Mon Sep 17 00:00:00 2001
From: MayGo <maigo.erit@gmail.com>
Date: Sat, 15 Jun 2019 22:28:12 +0300
Subject: [PATCH 4/7] calendar fixes

---
 client/src/components/SummaryCalendar/SummaryCalendar.tsx   | 6 ++++--
 .../src/components/SummaryCalendar/SummaryCalendar.util.ts  | 3 +++
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/client/src/components/SummaryCalendar/SummaryCalendar.tsx b/client/src/components/SummaryCalendar/SummaryCalendar.tsx
index 93d32ee9..23785966 100644
--- a/client/src/components/SummaryCalendar/SummaryCalendar.tsx
+++ b/client/src/components/SummaryCalendar/SummaryCalendar.tsx
@@ -1,5 +1,5 @@
 import { Flex } from '@rebass/grid';
-import { Badge, Calendar, Spin, Icon } from 'antd';
+import { Calendar, Spin, Icon } from 'antd';
 import { Moment } from 'moment';
 import * as React from 'react';
 import useReactRouter from 'use-react-router';
@@ -92,7 +92,9 @@ export const SummaryCalendar = () => {
             <TaskList>
                 {listData.map(item => (
                     <Item key={item.content}>
-                        <Badge status={item.type} text={item.content} />
+                        <Icon type={item.type} theme="outlined" />
+                        {'  '}
+                        {item.content}
                     </Item>
                 ))}
             </TaskList>
diff --git a/client/src/components/SummaryCalendar/SummaryCalendar.util.ts b/client/src/components/SummaryCalendar/SummaryCalendar.util.ts
index 26f9c165..5674564e 100644
--- a/client/src/components/SummaryCalendar/SummaryCalendar.util.ts
+++ b/client/src/components/SummaryCalendar/SummaryCalendar.util.ts
@@ -49,6 +49,9 @@ export const summariseOnline = (items, mode) => {
 };
 
 export const summariseTimeOnline = (items, mode) => {
+    if (mode === 'year') {
+        return [];
+    }
     const data = _(items)
         .filter(item => item.app === 'ONLINE')
         .groupBy(groupByActualDay)

From 7f3063c55c28cd12cb12496cd2b02971cc8cbca5 Mon Sep 17 00:00:00 2001
From: MayGo <maigo.erit@gmail.com>
Date: Sat, 15 Jun 2019 23:01:07 +0300
Subject: [PATCH 5/7] tooltip texts seperated lines

---
 client/src/components/LineCharts/LineChart.tsx    | 8 +++++---
 client/src/components/Timeline/BarWithTooltip.tsx | 2 +-
 client/src/components/Timeline/Timeline.tsx       | 8 +++++---
 3 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/client/src/components/LineCharts/LineChart.tsx b/client/src/components/LineCharts/LineChart.tsx
index a496fb41..670bea89 100644
--- a/client/src/components/LineCharts/LineChart.tsx
+++ b/client/src/components/LineCharts/LineChart.tsx
@@ -55,9 +55,11 @@ export const LineChart = () => {
                 data={onlineTimesSummary}
                 labelComponent={labelComponent()}
                 labels={d =>
-                    `Start time: ${convertDate(d.beginDate).format(TIME_FORMAT)}
-                    End time: ${convertDate(d.endDate).format(TIME_FORMAT)}
-                    Duration: ${diffAndFormatShort(d.beginDate, d.endDate)}`
+                    `Start time: ${convertDate(d.beginDate).format(
+                        TIME_FORMAT,
+                    )}\r\nEnd time: ${convertDate(d.endDate).format(
+                        TIME_FORMAT,
+                    )}\r\nDuration: ${diffAndFormatShort(d.beginDate, d.endDate)}`
                 }
             />
             <VictoryBar
diff --git a/client/src/components/Timeline/BarWithTooltip.tsx b/client/src/components/Timeline/BarWithTooltip.tsx
index af5adfff..ef86ae5d 100644
--- a/client/src/components/Timeline/BarWithTooltip.tsx
+++ b/client/src/components/Timeline/BarWithTooltip.tsx
@@ -6,7 +6,7 @@ import { chartTheme } from './ChartTheme';
 export class BarWithTooltip extends React.Component<any, any> {
     constructor(props) {
         super(props);
-        this.state = { position: true, hover: false };
+        this.state = { hover: false };
     }
 
     public onMouseEnterHandler = () => {
diff --git a/client/src/components/Timeline/Timeline.tsx b/client/src/components/Timeline/Timeline.tsx
index dbaa181f..0c661f1f 100644
--- a/client/src/components/Timeline/Timeline.tsx
+++ b/client/src/components/Timeline/Timeline.tsx
@@ -20,6 +20,7 @@ import { blueGrey700, chartTheme, disabledGrey } from './ChartTheme';
 import { BrushChart, MainChart, Spinner } from './Timeline.styles';
 import { TimelineItemEditContainer } from './TimelineItemEditContainer';
 import { Logger } from '../../logger';
+import { formatDuration } from '../SummaryCalendar/SummaryCalendar.util';
 
 interface IProps {
     timerange: any;
@@ -128,13 +129,14 @@ export const Timeline = React.memo<IFullProps>(
 
         const getTooltipLabel = d => {
             const diff = convertDate(d.endDate).diff(convertDate(d.beginDate));
-            const dur = moment.duration(diff);
-            const formattedDuration = dur.format();
+
             const type = d.taskName === TrackItemType.StatusTrackItem ? 'STATUS' : d.app;
             const beginTime = convertDate(d.beginDate).format(TIME_FORMAT);
             const endTime = convertDate(d.endDate).format(TIME_FORMAT);
 
-            return `${type} - ${d.title} [${formattedDuration}] ${beginTime} - ${endTime}`;
+            return `${type}\r\n${d.title}\r\n${beginTime} - ${endTime}\r\n${formatDuration(
+                moment.duration(diff),
+            )}`;
         };
 
         const { appItems, logItems, statusItems } = timeItems;

From 6e6ca79cbc6379407a291338efce1b710d13b134 Mon Sep 17 00:00:00 2001
From: MayGo <maigo.erit@gmail.com>
Date: Sun, 16 Jun 2019 11:16:22 +0300
Subject: [PATCH 6/7] refactor

---
 .../src/components/LineCharts/LineChart.tsx   |  2 +-
 .../components/Timeline/BarWithTooltip.tsx    | 86 +++++++++----------
 2 files changed, 42 insertions(+), 46 deletions(-)

diff --git a/client/src/components/LineCharts/LineChart.tsx b/client/src/components/LineCharts/LineChart.tsx
index 670bea89..21f0f547 100644
--- a/client/src/components/LineCharts/LineChart.tsx
+++ b/client/src/components/LineCharts/LineChart.tsx
@@ -70,7 +70,7 @@ export const LineChart = () => {
                 style={{ data: { fill: COLORS.green } }}
                 data={onlineTimesSummary}
                 labelComponent={labelComponent()}
-                labels={d => `Online: ${moment.duration(d.online).format()}`}
+                labels={d => `Worked: ${moment.duration(d.online).format()}`}
             />
         </VictoryChart>
     );
diff --git a/client/src/components/Timeline/BarWithTooltip.tsx b/client/src/components/Timeline/BarWithTooltip.tsx
index ef86ae5d..dcc445b4 100644
--- a/client/src/components/Timeline/BarWithTooltip.tsx
+++ b/client/src/components/Timeline/BarWithTooltip.tsx
@@ -1,55 +1,51 @@
-import React from 'react';
+import React, { useState } from 'react';
 import { Portal } from 'react-portal';
 import { Bar, VictoryTooltip } from 'victory';
 import { chartTheme } from './ChartTheme';
 
-export class BarWithTooltip extends React.Component<any, any> {
-    constructor(props) {
-        super(props);
-        this.state = { hover: false };
-    }
+export const BarWithTooltip = ({
+    datum = {},
+    onClickBarItem,
+    getTooltipLabel,
+    x = 0,
+    y = 0,
+    ...rest
+}) => {
+    const [hover, setHover] = useState(false);
 
-    public onMouseEnterHandler = () => {
-        this.setState({
-            hover: true,
-        });
+    const onMouseEnter = () => {
+        setHover(true);
     };
 
-    public onMouseLeaveHandler = () => {
-        this.setState({
-            hover: false,
-        });
+    const onMouseLeave = () => {
+        setHover(false);
     };
 
-    public render() {
-        const { datum } = this.props;
-
-        const events = {
-            onMouseEnter: this.onMouseEnterHandler,
-            onMouseLeave: this.onMouseLeaveHandler,
-            onClick: () => this.props.onClickBarItem(datum),
-        };
+    const events = {
+        onMouseEnter,
+        onMouseLeave,
+        onClick: () => onClickBarItem(datum),
+    };
 
-        return (
-            <>
-                {<Bar {...this.props} events={events} />}
-                {this.state.hover && (
-                    <Portal closeOnEsc closeOnOutsideClick>
-                        <VictoryTooltip
-                            horizontal={false}
-                            x={this.props.x}
-                            y={this.props.y}
-                            style={chartTheme.tooltip.style}
-                            cornerRadius={chartTheme.tooltip.cornerRadius}
-                            pointerLength={chartTheme.tooltip.pointerLength}
-                            flyoutStyle={chartTheme.tooltip.flyoutStyle}
-                            active
-                            events={null}
-                            text={this.props.getTooltipLabel(this.props.datum)}
-                        />
-                    </Portal>
-                )}
-            </>
-        );
-    }
-}
+    return (
+        <>
+            {<Bar datum={datum} x={x} y={y} {...rest} events={events} />}
+            {hover && (
+                <Portal closeOnEsc closeOnOutsideClick>
+                    <VictoryTooltip
+                        horizontal={false}
+                        x={x}
+                        y={y}
+                        style={chartTheme.tooltip.style}
+                        cornerRadius={chartTheme.tooltip.cornerRadius}
+                        pointerLength={chartTheme.tooltip.pointerLength}
+                        flyoutStyle={chartTheme.tooltip.flyoutStyle}
+                        active
+                        events={null}
+                        text={getTooltipLabel(datum)}
+                    />
+                </Portal>
+            )}
+        </>
+    );
+};

From 8c547b71e5052b5520b4584bc4ff5c7cd9e6d3b7 Mon Sep 17 00:00:00 2001
From: MayGo <maigo.erit@gmail.com>
Date: Sun, 16 Jun 2019 11:40:02 +0300
Subject: [PATCH 7/7] 3.4.12

---
 electron/package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/electron/package.json b/electron/package.json
index 8a8aa6d8..d8f7983b 100644
--- a/electron/package.json
+++ b/electron/package.json
@@ -1,6 +1,6 @@
 {
     "name": "tockler",
-    "version": "3.4.11",
+    "version": "3.4.12",
     "description": "Automatically track applications usage and working time",
     "author": "Maigo Erit <maigo.erit@gmail.com>",
     "license": "GPL-2.0",