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",