Skip to content
This repository was archived by the owner on Dec 26, 2022. It is now read-only.

Commit 4797e3f

Browse files
author
Dmitry Yadrikhinsky
committed
HoursByTask
1 parent 5d3fb21 commit 4797e3f

File tree

7 files changed

+145
-2
lines changed

7 files changed

+145
-2
lines changed

src/helpers/DateTime.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ function timePad(time: number): string {
22
return String(time).padStart(2, '0');
33
}
44

5+
function onlySecs(secs: number) {
6+
return `${secs}s`;
7+
}
8+
59
export function msToTime(s: number, showSeconds: boolean = true) {
610
const ms = s % 1000;
711
s = (s - ms) / 1000;
@@ -11,7 +15,13 @@ export function msToTime(s: number, showSeconds: boolean = true) {
1115
const hrs = (s - mins) / 60;
1216

1317
if (showSeconds) {
18+
if (hrs === 0 && mins === 0) {
19+
return onlySecs(secs);
20+
}
1421
return `${timePad(hrs)}:${timePad(mins)}:${timePad(secs)}`;
1522
}
23+
if (hrs === 0 && mins === 0) {
24+
return onlySecs(secs);
25+
}
1626
return `${timePad(hrs)}:${timePad(mins)}`;
1727
}

src/helpers/IterateLastCurrent.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
type Callback<T, R> = (last: T | undefined, cur: T) => R;
2+
3+
export function mapLastCurrent<T, R = any>(
4+
items: T[],
5+
callback: Callback<T, R>
6+
): R[] {
7+
const result: R[] = [];
8+
for (let i = 0; i < items.length; i++) {
9+
if (i === 0) {
10+
result.push(callback(undefined, items[i]));
11+
} else {
12+
result.push(callback(items[i - 1], items[i]));
13+
}
14+
}
15+
return result;
16+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.drawer-task {
2+
width: 100%;
3+
}

src/screens/projects/components/DrawerTask/DrawerTask.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import React, { useMemo } from 'react';
22
import { Checkbox, Drawer, Input, Space } from 'antd';
33
import { observer } from 'mobx-react';
4+
import { FieldTimeOutlined } from '@ant-design/icons';
5+
6+
import './DrawerTask.less';
47

58
import TaskModel from '../../../../models/TaskModel';
69
import rootStore from '../../../../services/RootStore';
710
import { useTaskDuration } from '../../../../hooks/TaskHooks';
11+
import HoursByTask from '../HoursByTask/HoursByTask';
812

913
const { projectStore } = rootStore;
1014

@@ -31,7 +35,7 @@ export default observer(function DrawerTask({
3135
onClose={onClose}
3236
visible={visible}
3337
>
34-
<Space direction="vertical">
38+
<Space direction="vertical" className="drawer-task">
3539
<Checkbox
3640
onChange={(e) => {
3741
const { checked } = e.target;
@@ -63,7 +67,12 @@ export default observer(function DrawerTask({
6367
}
6468
}}
6569
/>
66-
<div>Duration: {duration}</div>
70+
<div>
71+
<FieldTimeOutlined />
72+
<span>: {duration}</span>
73+
</div>
74+
75+
<HoursByTask task={task} />
6776
</Space>
6877
</Drawer>
6978
);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
.hours-by-task {
2+
width: 100%;
3+
}
4+
5+
.hours-by-task .label {
6+
margin-left: 8px;
7+
}
8+
9+
.hours-item .ant-card-body {
10+
display: flex;
11+
flex-direction: row;
12+
justify-content: space-between;
13+
padding: 8px;
14+
}
15+
16+
.space {
17+
flex: 1;
18+
}
19+
20+
.hours-range {
21+
font-size: 10px;
22+
}
23+
24+
.duration {
25+
font-size: 11px;
26+
font-weight: bold;
27+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React from 'react';
2+
import { Space } from 'antd';
3+
import { FieldTimeOutlined } from '@ant-design/icons';
4+
import isSameDay from 'date-fns/isSameDay';
5+
import format from 'date-fns/format';
6+
7+
import './HoursByTask.less';
8+
9+
import TaskModel from '../../../../models/TaskModel';
10+
import { mapLastCurrent } from '../../../../helpers/IterateLastCurrent';
11+
import HoursItem from './HoursItem';
12+
13+
function dateFormat(date: Date) {
14+
return format(date, 'dd.MM.yyyy');
15+
}
16+
17+
interface HoursByTaskProps {
18+
task: TaskModel | undefined;
19+
}
20+
21+
export default function HoursByTask({ task }: HoursByTaskProps) {
22+
return (
23+
<Space direction="vertical" className="hours-by-task">
24+
<div>
25+
<FieldTimeOutlined />
26+
<span className="label">Hours:</span>
27+
</div>
28+
{task?.time.length === 0 && <div>No billed hours</div>}
29+
{mapLastCurrent(task?.time || [], (last, range) => {
30+
if (!last || !isSameDay(last[0], range[0])) {
31+
return (
32+
<div>
33+
<div>{dateFormat(range[0])}</div>
34+
<HoursItem range={range} />
35+
</div>
36+
);
37+
}
38+
return <HoursItem range={range} />;
39+
})}
40+
</Space>
41+
);
42+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from 'react';
2+
import { Card } from 'antd';
3+
import format from 'date-fns/format';
4+
import { msToTime } from '../../../../helpers/DateTime';
5+
6+
function hoursFormat(date: Date) {
7+
return format(date, 'HH:mm');
8+
}
9+
10+
function hoursRangeFormat(date: Date[]) {
11+
if (date.length === 2) {
12+
return `${hoursFormat(date[0])} - ${hoursFormat(date[1])}`;
13+
}
14+
return `${hoursFormat(date[0])} -`;
15+
}
16+
17+
function getDuration(date: Date[]): string {
18+
if (date.length === 2) {
19+
return msToTime(date[1].getTime() - date[0].getTime(), false);
20+
}
21+
return '';
22+
}
23+
24+
interface HoursItemProps {
25+
range: Date[];
26+
}
27+
28+
export default function HoursItem({ range }: HoursItemProps) {
29+
return (
30+
<Card className="hours-item">
31+
<div className="hours-range">{hoursRangeFormat(range)}</div>
32+
<div className="space" />
33+
<div className="duration">{getDuration(range)}</div>
34+
</Card>
35+
);
36+
}

0 commit comments

Comments
 (0)