Skip to content

Commit def6298

Browse files
committed
Progress bar
1 parent f3bb41b commit def6298

File tree

10 files changed

+179
-49
lines changed

10 files changed

+179
-49
lines changed

src/components/Header/Header.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { observer } from 'mobx-react';
66
import HeaderLink from '../HeaderLink/HeaderLink';
77
import Profile from '../Profile/Profile';
88
import TaskControl from '../TaskControl/TaskControl';
9+
import ProgressBar from '../ProgressBar/ProgressBar';
910

1011
const { Header: HeaderBase } = Layout;
1112

@@ -21,7 +22,9 @@ function Header() {
2122
<HeaderLink>
2223
<Link to="/dashboard">Dashboard</Link>
2324
</HeaderLink>
24-
<span className="flex-1" />
25+
<span className="flex-1">
26+
<ProgressBar />
27+
</span>
2528
<TaskControl />
2629
<Profile />
2730
</HeaderBase>
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import React, { useCallback, useMemo, useState } from 'react';
2+
import { observer } from 'mobx-react';
3+
import { Slider } from 'antd';
4+
import { isSameDay } from 'date-fns';
5+
6+
import rootStore from '../../modules/RootStore';
7+
import { useInterval } from '../../hooks/UseInterval';
8+
import { getStartWorkingTime, getTimeItems } from '../../helpers/TaskHelper';
9+
import TaskTimeService from '../../services/TaskTimeService';
10+
11+
const { tasksStore, settingsStore } = rootStore;
12+
13+
function ProgressBar() {
14+
const { settings } = settingsStore;
15+
const workingHoursMs = settings.numberOfWorkingHours;
16+
17+
const [dayUpdateEveryDay, setDayUpdateEveryDay] = useState(new Date());
18+
const [timer, setTimer] = useState(new Date());
19+
20+
const shouldDayUpdate = useCallback(() => {
21+
const now = new Date();
22+
setTimer(now);
23+
if (!isSameDay(dayUpdateEveryDay, now)) {
24+
setDayUpdateEveryDay(now);
25+
}
26+
}, [dayUpdateEveryDay]);
27+
28+
useInterval(shouldDayUpdate);
29+
30+
const tasks = useMemo(() => tasksStore.getTasksByDate(dayUpdateEveryDay), [
31+
tasksStore.tasks,
32+
dayUpdateEveryDay,
33+
]);
34+
35+
const timeItems = useMemo(() => getTimeItems(tasks, dayUpdateEveryDay), [
36+
tasks,
37+
dayUpdateEveryDay,
38+
]);
39+
40+
const workingTimeStart = useMemo(() => getStartWorkingTime(timeItems), [
41+
timeItems,
42+
]);
43+
44+
const timeRangeItems = useMemo(() => timeItems.map((t) => t.time), [
45+
timeItems,
46+
]);
47+
48+
const progress = useMemo(
49+
() =>
50+
TaskTimeService.getDayProgress(
51+
timeRangeItems,
52+
workingTimeStart,
53+
workingHoursMs
54+
),
55+
[timer, timeRangeItems, workingTimeStart, workingHoursMs]
56+
);
57+
58+
console.log(progress);
59+
60+
return <Slider value={progress} />;
61+
}
62+
63+
export default observer(ProgressBar);

src/helpers/DateTime.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,7 @@ export function estimateWorkingTimeEnd(
111111
? new Date(startDate.getTime() + restTimeMs + workingHoursMs)
112112
: undefined;
113113
}
114+
115+
export function getRestHoursMs(workingHoursMs: number, durationMs: number) {
116+
return workingHoursMs - durationMs;
117+
}

src/helpers/TaskHelper.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isSameDay, compareAsc } from 'date-fns';
1+
import { isSameDay, compareAsc, isBefore } from 'date-fns';
22

33
import TaskModel from '../modules/tasks/models/TaskModel';
44
import TaskTimeItemModel from '../modules/tasks/models/TaskTimeItemModel';
@@ -21,7 +21,7 @@ export function getTimeItems(
2121
tasks: TaskModel[],
2222
date: Date
2323
): TaskTimeItemModel[] {
24-
let taskTime: TaskTimeItemModel[] = [];
24+
const taskTime: TaskTimeItemModel[] = [];
2525
tasks.forEach((task) => {
2626
const taskTimeItems: TaskTimeItemModel[] = [];
2727
for (let i = 0; i < task.time.length; i++) {
@@ -33,9 +33,12 @@ export function getTimeItems(
3333
taskTimeItems.push(new TaskTimeItemModel(task, range, i));
3434
}
3535
}
36-
taskTime = taskTime.concat(taskTimeItems);
36+
taskTime.push(...taskTimeItems);
3737
});
38-
taskTime = taskTime.sort((a, b) => compareAsc(a.time.start, b.time.start));
38+
taskTime.sort((a, b) => compareAsc(a.time.start, b.time.start));
39+
40+
console.log('getTimeItems', taskTime.length);
41+
3942
return taskTime;
4043
}
4144

@@ -71,3 +74,17 @@ export function getTaskTitlesPath(task: TaskModel) {
7174
}
7275
return titlePathStr;
7376
}
77+
78+
export function getStartWorkingTime(
79+
timeItems: TaskTimeItemModel[]
80+
): Date | undefined {
81+
let minTime: Date | undefined;
82+
let tmpStartTime: Date | undefined;
83+
timeItems.forEach((item) => {
84+
tmpStartTime = item.time.start;
85+
if (!minTime || isBefore(tmpStartTime, minTime)) {
86+
minTime = tmpStartTime;
87+
}
88+
});
89+
return minTime;
90+
}

src/hooks/TaskHooks.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -105,17 +105,3 @@ export function useTimeRangeDuration(timeRange: ITimeRangeModel | undefined) {
105105

106106
return duration;
107107
}
108-
109-
export function useStartWorkingTime(
110-
timeItems: TaskTimeItemModel[]
111-
): Date | undefined {
112-
return useMemo(() => {
113-
let minTime: Date | undefined;
114-
timeItems.forEach((time) => {
115-
if (!minTime || isBefore(time.time.start, minTime)) {
116-
minTime = time.time.start;
117-
}
118-
});
119-
return minTime;
120-
}, [timeItems]);
121-
}

src/hooks/UseInterval.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { useEffect } from 'react';
2+
3+
export function useInterval(fn: () => void, intervalMs: number = 1000) {
4+
useEffect(() => {
5+
let intervalId = setTimeout(function run() {
6+
fn();
7+
intervalId = setTimeout(run, intervalMs);
8+
}, intervalMs);
9+
return () => clearTimeout(intervalId);
10+
}, [fn]);
11+
}

src/modules/tasks/TaskStore.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {
1818
import { DEFAULT_PROJECT_ID } from '../projects/models/ProjectModel';
1919
import { ITreeItemWithParent } from '../../types/ITreeItem';
2020

21+
// FIXME ts errors
22+
2123
export default class TaskStore {
2224
tasks: TasksByProject = {};
2325
activeTask: TaskModel | undefined;
@@ -83,6 +85,7 @@ export default class TaskStore {
8385
for (const tasks of Object.values(this.tasks)) {
8486
TreeModelHelper.getFlatItemsRecursiveBase(tasks, condition, result);
8587
}
88+
console.log('getTasksByDate', result.length);
8689
return result;
8790
}
8891

src/modules/tasks/models/TaskModel.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export default class TaskModel extends AbstractModel
5454
key: string = '';
5555
title: string = '';
5656
children: TaskModel[] = [];
57-
parent: TaskModel | null = null; // update parent on drug&drop
57+
parent: TaskModel | undefined = undefined;
5858
projectId: string = '';
5959
checked: boolean = false;
6060
active: boolean = false;

src/screens/hours/components/TotalHours/TotalHours.tsx

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useMemo } from 'react';
22
import { observer } from 'mobx-react';
33
import { Space } from 'antd';
44

@@ -8,9 +8,11 @@ import {
88
estimateWorkingTimeEnd,
99
toTimeFormat,
1010
msToTime,
11+
getRestHoursMs,
1112
} from '../../../../helpers/DateTime';
1213
import LabelWithTooltip, { ILabelWithTooltipProps } from './LabelWithTooltip';
1314
import rootStore from '../../../../modules/RootStore';
15+
import { getStartWorkingTime } from '../../../../helpers/TaskHelper';
1416

1517
interface TotalHoursProps {
1618
timeItems: TaskTimeItemModel[];
@@ -24,44 +26,49 @@ const TotalHours = observer((props: TotalHoursProps) => {
2426
const workingHoursMs = settings.numberOfWorkingHours;
2527

2628
const { durationMs, restMs } = TaskHooks.useTimeItemsDuration(timeItems);
27-
const startWorkingTime = TaskHooks.useStartWorkingTime(timeItems);
29+
const startWorkingTime = useMemo(() => getStartWorkingTime(timeItems), [
30+
timeItems,
31+
]);
2832
const estimatedWorkingTimeEnd = estimateWorkingTimeEnd(
2933
startWorkingTime,
3034
restMs,
3135
workingHoursMs
3236
);
33-
const restHoursMs = workingHoursMs - durationMs;
37+
const restHoursMs = getRestHoursMs(workingHoursMs, durationMs);
38+
39+
const items: ILabelWithTooltipProps[] = useMemo(
40+
() => [
41+
{
42+
label: toTimeFormat(startWorkingTime),
43+
tooltip: 'Start time',
44+
},
45+
{
46+
icon: 'mi-work-outline',
47+
label: msToTime(durationMs, false),
48+
tooltip: 'Working hours',
49+
},
50+
{
51+
icon: 'mi-local-cafe',
52+
label: msToTime(restMs, false),
53+
tooltip: 'Rest hours',
54+
},
55+
{
56+
icon: 'mi-notifications',
57+
label: toTimeFormat(estimatedWorkingTimeEnd),
58+
tooltip: 'Estimated end time',
59+
},
60+
{
61+
label: msToTime(restHoursMs, false),
62+
tooltip: 'Time left',
63+
},
64+
],
65+
[startWorkingTime, durationMs, restMs, estimatedWorkingTimeEnd, restHoursMs]
66+
);
3467

3568
if (!timeItems.length) {
3669
return null;
3770
}
3871

39-
const items: ILabelWithTooltipProps[] = [
40-
{
41-
label: toTimeFormat(startWorkingTime),
42-
tooltip: 'Start time',
43-
},
44-
{
45-
icon: 'mi-work-outline',
46-
label: msToTime(durationMs, false),
47-
tooltip: 'Working hours',
48-
},
49-
{
50-
icon: 'mi-local-cafe',
51-
label: msToTime(restMs, false),
52-
tooltip: 'Rest hours',
53-
},
54-
{
55-
icon: 'mi-notifications',
56-
label: toTimeFormat(estimatedWorkingTimeEnd),
57-
tooltip: 'Estimated end time',
58-
},
59-
{
60-
label: msToTime(restHoursMs, false),
61-
tooltip: 'Time left',
62-
},
63-
];
64-
6572
return (
6673
<Space size="middle">
6774
{items.map((props, index) => (

src/services/TaskTimeService.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import {
2+
calcDuration,
3+
calcDurationGaps,
4+
estimateWorkingTimeEnd,
5+
} from '../helpers/DateTime';
6+
import { ITimeRangeModel } from '../modules/tasks/models/TaskModel';
7+
8+
const TaskTimeService = {
9+
getDayProgress(
10+
timeItems: ITimeRangeModel[],
11+
workingTimeStart: Date | undefined,
12+
workingHoursMs: number
13+
) {
14+
if (!workingTimeStart) {
15+
return 0;
16+
}
17+
18+
const durationMs = calcDuration(timeItems);
19+
const restMs = calcDurationGaps(timeItems);
20+
const estimatedWorkingTimeEnd = estimateWorkingTimeEnd(
21+
workingTimeStart,
22+
restMs,
23+
workingHoursMs
24+
);
25+
26+
if (estimatedWorkingTimeEnd) {
27+
const durationWorkDayMs =
28+
estimatedWorkingTimeEnd.getTime() - workingTimeStart.getTime() || 1;
29+
return (durationMs * 100) / durationWorkDayMs;
30+
}
31+
32+
return 0;
33+
},
34+
};
35+
36+
export default TaskTimeService;

0 commit comments

Comments
 (0)